D3.js レイアウト – d3.tree.layoutでツリーを作る

このページは"D3.jsチュートリアル"シリーズの13 / 20です。

今回はツリーチャートを作ります。ツリーチャートは、親から子に枝分かれしていく樹形図です。d3.layout.treeにデータを入れると、自動的にツリーのノードの座標と、枝の開始点と終了点の座標を計算して出してくれます。
実際にツリーを描くときは、circleをノードの座標に、diagonal曲線を枝として使います。diagonalの詳しい使い方はD3.js レイアウト – diagonalで曲線をつなぐにあります。

でもツリーを作るときに一番重要なのは、使うデータの構造です。下のように、入れ子構造になっているデータを使います。
この場合は、”name”がノードの名前、”種類”が子のノードを入れるリストの名前です。ツリー構造として描く時は、この”種類”が枝の部分に使われて、子ノードへ枝を伸ばしていく形になります。”好き度”という値は今回は使いませんが、ノードの大きさなどに使うこともできます。

{
 "name": "明治",
 "種類": [
  {
   "name": "チョコ菓子シリーズ",
   "種類": [
    {
     "name": "きのこの山",
     "種類": [
      {"name": "味わいミント", "好き度": 10},
 	 {"name": "黒糖きなこ", "好き度": 5},
      {"name": "チョコバナナ", "好き度": 15}
     ]
    },
    {
     "name": "たけのこの里",
     "種類": [
      {"name": "マカダミアクッキー", "好き度": 2},
      {"name": "焼き栗", "好き度": 10}
     ]
    },
	{
	"name": "すぎのこ村", "好き度": 20
	}
   ]
  }
 ]
}

ではツリーレイアウトの設定を作る方法です。
下のようにd3.layout.tree()が基本形です。
.size([x,y])でツリー全体のサイズを決めます。
.children()の中に、関数を入れると、入れ子のための名前を指定できます。デフォルトでは”children”という名前ですが、今回使うデータでは”種類”なので、それを指定します。

var tree = d3.layout.tree()
.size([400,400]) // .size()でツリー全体のサイズを決める。
.children(children); // children()で入れ子のための名前を指定する。

function children(d) {
  return d["種類"];
}

次にデータからノードの座標データを作ります。
下のように、tree.nodes()にデータを入れると、ノードのための座標がx,yとして出ます。
childrenやparentというデータも出てきます。これはノードごとの相対的な親や子のデータで、使いたいときは使ってもいいものです。

// tree.nodesで、childrenのname分だけ、name/children/parent/depth/x/yを作る。
var nodes = tree.nodes(data);

さらにノードのデータから、枝の座標データを作ります。
先ほど作ったnodesを、tree.links(nodes)として入れます。
そうすると、sourceとtargetのx,y座標が出ます。これは枝の開始点と終了点の座標です。diagonalにすぐ使える形式になっています。

// nodesから、diagonal用データ構造を作る = [{ source:{x:10,y:20}, target:{x:100,y:200} }]など。
var links = tree.links(nodes);

あとは、これらの座標を使って、circleとdiagonalを描くだけです。

では実際に作ってみます。

 

樹形図を作る:

結果:

d3レイアウト-treelayout1
クリックすると実際のページが開きます。

コード:

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8">
</head>
<body>

	<script src="http://d3js.org/d3.v3.min.js"></script>

	<script>
	var width = 760;
	var height = 450;

	var svg = d3.select("body").append("svg")
	.attr("width", width)
	.attr("height", height)
	.attr("transform","translate(50,50)");//ここがツリーの左上になる。

	var tree = d3.layout.tree()
	.size([400,400]) // .size()でツリー全体のサイズを決める。
	.children(children); // children()で入れ子のための名前を指定する。

	function children(d) {
	  return d["種類"];
	}

	d3.json("kinoko_takenoko.json", function(data) {

	// tree.nodesで、childrenのname分だけ、name/children/parent/depth/x/yを作る。
	var nodes = tree.nodes(data);

	// nodesから、diagonal用データ構造を作る = [{ source:{x:10,y:20}, target:{x:100,y:200} }]など
	var links = tree.links(nodes);

	// 円とテキストを入れるノードのコンテナg。
	var node = svg.selectAll(".node")
	.data(nodes) //nodesの数分gを作る。
	.enter()
	.append("g")
	.attr("class","node")
	.attr("transform", function(d){ return "translate("+ d.y + "," + d.x + ")";}); //ノードの場所まで移動。横向きにするためにxとyを逆に。

	node.append("circle")
	.attr("r", 4)
	.attr("fill","steelblue");

	node.append("text")
	.text(function(d) { return d.name})
	.attr("y",-5);

	var diagonal = d3.svg.diagonal()
	.projection(function(d){ return [d.y,d.x];}); // 横向きにするためにxとyを逆に。

 	//linksで作ったsource、targetでdiagonal曲線を作る。
	svg.selectAll(".link")
	.data(links)
	.enter()
	.append("path")
	.attr("class","link")
	.attr("fill", "none")
	.attr("stroke", "red")
	.attr("d",diagonal);

	});

	</script>

</body>
</html>

解説:
データはすでに紹介したものと同じで、kinoko_takenoko.jsonという名で使っています。
ツリーの設定は、上で紹介したコードとほぼ同じです。

実際にツリーを作るために、nodesをデータとして取り入れてコンテナとしてのg要素を作っています。nodesに含まれるノード用のxとyで、g要素を移動しています。そこにcircleとtextを加える事で、円とテキストをノードの位置に配置しました。こうするとノードの近くに名前が表示されるので、そのノードの名前が何なのかが分かるようになります。

// 円とテキストを入れるノードのコンテナg。
var node = svg.selectAll(".node")
.data(nodes) //nodesの数分gを作る。
.enter()
.append("g")
.attr("class","node")
.attr("transform", function(d){ return "translate("+ d.y + "," + d.x + ")";}); //ノードの場所まで移動。横向きにするためにxとyを逆に。

node.append("circle")
.attr("r", 4)
.attr("fill","steelblue");

node.append("text")
.text(function(d) { return d.name})
.attr("y",-5);

さらにツリーの枝を作るためにdiagonalを設定し、linksで作ったデータで曲線を描いています。

var diagonal = d3.svg.diagonal()
.projection(function(d){ return [d.y,d.x];}); // 横向きにするためにxとyを逆に。

//linksで作ったsource、targetでdiagonal曲線を作る。
svg.selectAll(".link")
.data(links)
.enter()
.append("path")
.attr("class","link")
.attr("fill", "none")
.attr("stroke", "red")
.attr("d",diagonal);

treeレイアウトは結局はノードの座標と、枝の開始点と終了点を計算してくれるだけです。だからそのデータを使えば、好みに合わせてどんな要素を使うこともできます。


コメントを残す