ツリーマップレイアウトは階層(親子)関係を利用してチャートを表示します。以前の記事で出てきたパックレイアウトと同じ仕組みです。ということは、ツリーマップレイアウトも階層化レイアウト機能の一種として用意されています。基本的な作りは変わらないので、パックレイアウトが分かれば、分かりやすいかと思います。
ツリーマップレイアウト生成の基本
ツリーマップレイアウトの生成には、階層化されたJavaScriptオブジェクトが必要です。ツリーマップレイアウトで最低限必要なので親となる(ルートとなるデータ)です。パックレイアウトと同じです。
1 2 3 |
var arrData = { value : 40 } |
valueはツリーマップレイアウトでデフォルトで使われているプロパティ名です。子ノードがある時は、childrenプロパティを使い配列でデータを指定します。下記のような感じです。
1 2 3 4 5 6 7 |
var arrData = { children : [ {value : 40}, {value : 20}, {value : 10} ] } |
children配列内にchildrenを使い、入れ子にすることもできます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var arrData = { children : [ { value : 40}, { value : 20}, { value : 10}, { children: [ { value: 40}, { value: 80}, { value: 100} ] } ] } |
ツリーマップレイアウトを利用するには、d3.layout.treemap()メソッドを使います。ツリーマップレイアウトはパックレイアウトと同様に、表示する範囲をsize()メソッドを用いて指定します。size()メソッドのパラメーターには配列形式で幅、高さを指定します。
あとはsvg要素に対してrect要素を追加していき、データをセットするdata()メソッドには、下記のようにしてnodes()メソッドを使う。
1 |
data( treemap.nodes(arrData)) |
上記のように書くと、D3.jsでは内部で計算が行われ、以下のプロパティが計算されます。
プロパティ名 | 詳細 |
---|---|
parent | 親階層のノード。ルート(一番上の階層)の場合はnull |
children | 子ノード。ない場合はnull |
value | 値 |
depth | 階層の深さ、ルートの場合は0 |
x | 四角形のX座標 |
y | 四角形のY座標 |
dx | 横のオフセット |
dy | 縦のオフセット |
attr()メソッドを使い要素に値を設定する場合は、上記の表のプロパティから読み出します。
サンプルコードの実行結果はこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<!DOCTYPE html> <head> <meta charset="utf-8"> <title>ツリーマップの作成</title> <script src="../js/d3.js"></script> <style> svg{ width:320px; height:240px; border:1px solid black; } .block{ stroke:black; fill:#c0c0c0; } </style> </head> <body> <h1>パックレイアウトを表示</h1> <svg id="graph"></svg> <script src="test.js"></script> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
/* パックレイアウトを表示 */ (function(){ // HTMLファイルで設定した#graphの幅、高さを取得する var svgEle = document.getElementById("graph"); var svgWidth = getComputedStyle( svgEle, null) .getPropertyValue("width"); var svgHeight = getComputedStyle( svgEle, null) .getPropertyValue("height"); svgWidth = parseFloat( svgWidth ); // 整数に変換する svgHeight = parseFloat( svgHeight ); // 整数に変換する var arrData = { children : [ { value : 50 }, { value : 100 }, { value : 150 } ] }; // ツリーマップレイアウト var treemap = d3.layout.treemap() .size([ svgWidth, svgHeight]); // ツリーマップを描画する var tmap = d3.select("#graph") .selectAll("rect") .data( treemap.nodes(arrData) ); tmap.enter() .append("rect") .attr("class","block") // CSSクラスを追加 .attr("x", function(d,i){ // X座標を設定 return d.x; }) .attr("y", function(d,i){ // Y座標を設定 return d.y; }) .attr("width", function(d,i){ // 幅を設定 return d.dx; }) .attr("height", function(d,i){ // 高さを設定 return d.dy; }); })(); |
分割された空間にデータの値を表示する
ツリーマップレイアウトは値に応じて領域を自動的に分割します。分割された領域にデータの値を出力します。データの値はvalueプロパティに入ってますが、そのまま文字として出力すると入れ子の構造のときに、文字が重なる現象が起きます。なので以下のように書いて、ルート階層のときと、子ノードがあるときは出力処理を回避します。
1 2 3 4 5 6 7 |
.text(function(d,i){ // 文字表示 if( d.depth == 0 || d.children){ // ルートか子ノードがある場合 return null; } return d.value; // 領域内に表示する文字を返す }); |
データの階層構造の違いを見た目で表す
データが入れ子になっているとき、下記を書くと階層が深くなるほど背景色が濃くなります。
1 2 3 |
.style("opacity",function(d,i){ // 深さに応じて不透明度設定 return d.depth / 3; // 入れ子が深くなるほど濃くする }); |
上記2つ踏まえたものは下記の実行結果です。
サンプルコードの実行結果はこちら
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<!DOCTYPE html> <head> <meta charset="utf-8"> <title>ツリーマップの作成 階層により色を分ける</title> <script src="../js/d3.js"></script> <style> svg{ width:320px; height:240px; border:1px solid black; } .block{ stroke:black; fill:#c0c0c0; } .name{ font-size:18px; text-anchor:middle; } </style> </head> <body> <h1>パックレイアウトを表示</h1> <svg id="graph"></svg> <script src="test.js"></script> </body> </html> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
/* パックレイアウトを表示 */ (function(){ // HTMLファイルで設定した#graphの幅、高さを取得する var svgEle = document.getElementById("graph"); var svgWidth = getComputedStyle( svgEle, null) .getPropertyValue("width"); var svgHeight = getComputedStyle( svgEle, null) .getPropertyValue("height"); svgWidth = parseFloat( svgWidth ); // 整数に変換する svgHeight = parseFloat( svgHeight ); // 整数に変換する var arrData = { children : [ { value : 240 }, { value : 120 }, { children : [ { value : 60 }, { value : 30 }, { children : [ { value : 20 }, { value : 10 } ]} ]} ]}; // ツリーマップレイアウト var treemap = d3.layout.treemap() .size([ svgWidth, svgHeight]); // ツリーマップを描画する var tmap = d3.select("#graph") .selectAll("rect") .data( treemap.nodes(arrData) ); tmap.enter() .append("rect") .attr("class","block") // CSSクラスを追加 .attr("x", function(d,i){ // X座標を設定 return d.x; }) .attr("y", function(d,i){ // Y座標を設定 return d.y; }) .attr("width", function(d,i){ // 幅を設定 return d.dx; }) .attr("height", function(d,i){ // 高さを設定 return d.dy; }) .style("opacity",function(d,i){ // 深さに応じて不透明度設定 return d.depth / 3; // 入れ子が深くなるほど濃くする }); // マップ内に文字追加 tmap.enter() .append("text") // text要素追加 .attr("class","name") // CSSクラス追加 .attr("transform",function(d,i){ return "translate(" + (d.x + d.dx/2 ) + "," + (d.y + d.dy/2 ) + ")"; }) .attr("dy","0.35em") // 表示位置調整 .text(function(d,i){ // 文字表示 if( d.depth == 0 || d.children){ // ルートか子ノードがある場合 return null; } return d.value; // 領域内に表示する文字を返す }); })(); |