📉

【D3.js】 hierarchy を使ってタテ型ツリー作成

2021/08/28に公開

前回は、公式ドキュメントにもソースが展開されているヨコ型ツリーを作成して、ノードのクリックによるハイライトメソッドを追加しました。
興味ある方はこちらの記事をご覧ください。(デモあります)
https://zenn.dev/yuji/articles/7eb96460317222

※今回は、機械学習などで用いられる決定木分析のデータビジュアライゼーションとしてタテ型で表示させる必要があったため学習した内容を執筆しています。

概要

今回は、タテ型のツリーに挑戦します。
利用するライブラリは、前回と変わらず、D3.jsを利用します。APIもhierarchyと変更ありません。

横型ツリーを作成した前提で進めていきたいと思います。
今回の完成イメージは以下のデモからご覧ください。

コードの解説

前回作成したコードをベースにタテ型ツリーにアップデートしていきます。

ノードを垂直方向に配置

ノードの位置はtranslateプロパティにて設定しています。
xyの設定値を入れ替えます。上から下に子孫要素が描画されます。
x軸は兄弟要素の座標位置、y軸は親要素の座標位置を持っています。

const node = g
  .append("g")
  .attr("stroke-linejoin", "round")
  .attr("stroke-width", 3)
  .selectAll("g")
  .data(root.descendants())
  .join("g")
  .attr("transform", (d) => `translate(${d.x}, ${d.y})`) // ←変更を加えた箇所
  .on("click", (e, d) => clicked(d));

パスを垂直方向へ伸ばす

ノードと同様にtranslateの値、xyを入れ替えます。
パスのスタイルも、linkVertical()に変更して垂直方向のパスを生成します。

const link = g
  .append("g")
  .attr("fill", "none")
  .attr("stroke", "#555")
  .attr("stroke-opacity", 0.4)
  .attr("stroke-width", 1.5)
  .selectAll("path")
  .data(root.links())
  .join("path")
  .attr(
    "d",
    d3
      .linkVertical() // ←変更を加えた箇所
      .x((d) => d.x)  // ←変更を加えた箇所
      .y((d) => d.y)  // ←変更を加えた箇所
  );

ノード位置を調整

実現したいことはできました。しかし、枠に対して位置がずれてしまっているのでエリアの中にしっかり収まるように変更していきます。

最上位にノードの位置が0, 0で設定されてしまっているので、画面中央に配置できるように基準値を設定します。

const node = g
  .append("g")
  .attr("stroke-linejoin", "round")
  .attr("stroke-width", 3)
  .selectAll("g")
  .data(root.descendants())
  .join("g")
  .attr("transform", (d) => `translate(${d.x + (width + d.x) / 2}, ${d.y})`) // ←変更を加えた箇所
  .on("click", (e, d) => clicked(d));

x軸の位置を画面中央に来るように変更しました。
パスも同様に設定していきます。

const link = g
  .append("g")
  .attr("fill", "none")
  .attr("stroke", "#555")
  .attr("stroke-opacity", 0.4)
  .attr("stroke-width", 1.5)
  .selectAll("path")
  .data(root.links())
  .join("path")
  .attr(
    "d",
    d3
      .linkVertical()
      .x((d) => d.x + (width + d.x) / 2) // ←変更を加えた箇所
      .y((d) => d.y)
  );

ノードをタテ型レイアウトに最適化させる

ヨコ型レイアウトの名残りで、ノードのデータとノードのドットがヨコにレイアウトされているので、上下に配置させます。

ノードデータを表示させる処理を変更しています。

node
  .append("text")
  .attr("y", -10) // ←変更を加えた箇所
  .attr("text-anchor", "middle") // ←変更を加えた箇所
  .text((d) => d.data.name)
  .clone(true)
  .lower()
  .attr("stroke", "white");

さいごに見た目を整える

ノード間に余白を持たせて見た目を整えます。
今回のデモは以下から確認できますので、前ソースを見たい方はこちらをご覧ください。

参考

Discussion