D3.js packの使い方

D3.jsで階層構造を可視化するpackについて説明する。

サンプルプログラム

コードを確認

サンプルコード

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>D3 hierarchy pack</title>
  <script src="https://d3js.org/d3.v7.min.js"></script>
</head>

<body>
  <svg width="800" height="600"></svg>

  <script>
    // 1. データを用意
    var width = document.querySelector("svg").clientWidth;
    var height = document.querySelector("svg").clientHeight;
    var data = {
      "name": "A",
      "children": [
        { "name": "B", "value": 25 },
        {
          "name": "C",
          "children": [
            { "name": "D", "value": 10 },
            { "name": "E", "value": 15 },
            { "name": "F", "value": 10 }
          ]
        },
        { "name": "G", "value": 15 },
        {
          "name": "H",
          "children": [
            { "name": "I", "value": 20 },
            { "name": "J", "value": 10 }
          ]
        },
        { "name": "K", "value": 10 }
      ]
    };

    // 2. 描画するデータへ変換
    root = d3.hierarchy(data);
    root.sum(function (d) { return d.value; });

    var pack = d3.pack()
      .size([width, height])
      .padding(0);

    pack(root);

    // 3. SVG要素を設定
    var node = d3.select("svg").selectAll(".node")
      .data(root.descendants())
      .enter()
      .append("g")
      .attr("transform", function (d) { return "translate(" + d.x + "," + (d.y) + ")"; });

    var color = ["Orange", "Khaki", "Ivory"];
    node.append("circle")
      .attr("r", function (d) { return d.r; })
      .attr("stroke", "black")
      .attr("fill", function (d) { return color[d.depth]; });

    node.append("text")
      .style("text-anchor", function (d) { return d.children ? "end" : "middle"; })
      .attr("font-size", "150%")
      .text(function (d) { return d.children ? "" : d.data.name; });
  </script>
</body>

</html>

サンプルコードの説明

1. データを用意

  var data = {
    "name": "A",
    "children": [
      { "name": "B", "value": 25 },
      {
        "name": "C",
        "children": [
          {"name": "D", "value": 10 },
          { "name": "E", "value": 15 },
          { "name": "F", "value": 10 }
        ]
      },
      { "name": "G", "value": 15 },
      {
        "name": "H",
        "children": [
          { "name": "I", "value": 20 },
          { "name": "J", "value": 10 }
        ]
      },
      { "name": "K", "value": 10 }
    ]
  };

描画するデータを用意する。データ構造の詳細はこちらを参照する。また、すべての末端ノードにバブルの大きさを表すvalue値を設定する。親ノードの大きさは、次のデータ変換時に子や孫ノードのvalue値の合計として設定する。

2. 描画するデータへ変換

root = d3.hierarchy(data);
root.sum(function(d) { return d.value; });

var pack = d3.pack()
  .size([width, height])
  .padding(0);

pack(root);

用意したデータを描画用のデータ構造へ変換する。「用意したデータ -> hierarchy用データ -> 描画種類別データ(ここではpack)」という2段階の変換が必要である。

root = d3.hierarchy(data);
root.sum(function(d) { return d.value; });

上の処理でdataをhierarchy用のデータ構造rootへ変換した後、value値を持たないノードに対して子孫ノードのvalue合計値を計算する。

次に、pack用データへ変換する関数を呼び出す。

  var pack = d3.pack()
    .size([width, height])
    .padding(0);

  pack(root);

d3.pack()で呼び出した関数にrootを引数として渡すと、次のようなデータがルートへ付与される。

変数 説明
x 円の中心x座標。
y 円の中心y座標。
r 円の半径。

また、d3.pack()には次の設定が可能である。

pack.radius() 円の半径を、function(d) { return d.value; }のような関数で指定する。
デフォルトはnullである。
pack.size() packレイアウト全体のサイズを[幅, 高さ]の2要素配列で設定する。
引数を指定しない場合は現在のサイズを返す。
デフォルトは[1, 1]である。
pack.padding() 円の接線間の距離を指定する。
デフォルトは0である。

3. SVG要素を設定

// 3. SVG要素を設定
var node = d3.select("svg").selectAll(".node")
  .data(root.descendants())
  .enter()
  .append("g")
  .attr("transform", function(d) { return "translate(" + d.x + "," + (d.y) + ")"; });

var color = ["orange", "Khaki", "Ivory"];
node.append("circle")
  .attr("r", function(d) { return d.r; })
  .attr("stroke", "black")
  .attr("fill", function(d) { return color[d.depth]; });

node.append("text")
  .style("text-anchor", function(d) { return d.children ? "end" : "middle"; })
  .attr("font-size", "150%")
  .text(function(d) { return d.children ? "" : d.data.name; });

円に対するSVG要素を設定する。最初に"g"要素を設定し、その中に"circle""text"を設定する。

データ割り当て部分では次の関数を使用している。

root.descendants()

この関数は、入れ子になったノードを配列として並べる関数である。

"circle"では最初に色配列を設定し、"fill"属性で深さ方向の位置に応じた色を設定している。また、"text"で設定した"text-anchor"は、textの位置を設定するスタイルである。