はじめに
こんにちは、TIGコアテクノロジーユニットの平岡です。
この記事は、Vis Networkで階層グラフを可視化するの続編となります。未読の方は、是非そちらの記事もご覧下さい。
前回の記事では、JavaScript製のグラフ可視化ライブラリについて概観しました。また、その1つであるVis Networkについて紹介し、階層グラフの可視化を行いました。
Vis Networkはcanvasでの高速な描画が可能で 1、階層グラフをライトに表示・加工する場合は非常に有用ですが、大きな階層グラフを表示する場合にエッジの交差が多くなってしまうという課題があることを見てきました。
この記事では上述の課題を解決するためにmxGraphを用いて階層グラフの可視化を行います。mxGraphは階層グラフのレイアウト計算にSugiyama Algorithm 2を利用しており、階層グラフを綺麗に表示することが可能です。下の画像は前回の記事で描画した階層グラフを両ライブラリ間で比較したものですが、mxGraphの方がエッジの交差が少なく見やすいレイアウトになっていることがわかります。
mxGraphとは
概要
mxGraphには以下のような特徴があります。
- JavaScript製のグラフ可視化ライブラリ
- 描画方式はSVG
- diagrams.net(旧 draw.io)で利用されています
- Apache License 2.0
- 自動レイアウト計算にSugiyama Algorithmを利用しており、綺麗な階層グラフを表示できます
注意
本家は2020年11月9日にメンテ終了し、 3現在は有志がメンテを行っているようです。
mxGraphの使い方
階層グラフの描画
mxGraphの自動レイアウト計算を利用して階層グラフを描画してみましょう。
- 紹介するソースコードの全体はEdit fiddle - JSFiddle - Code Playgroundで確認できます。こちらと比較しながら読み進めると良いです。
- より詳しく知りたい方は、本家の以下コンテンツをご覧になってみてください。
- mxGraph User Manual - JavaScript Client - mxGraphのマニュアルです。
- JavaScript Diagram Editor - mxGraphを利用したサンプルページが豊富に紹介されています。
- API Specification - mxGraphのAPIドキュメントです。
まずは、グラフを表示する領域を確保し、mxGraph
のインスタンスを生成します。
// グラフを表示する領域を確保 |
graph.getDefaultParent()
はノードやエッジを追加する際に必要(後述)なので取得しておきます。また、階層グラフを自動レイアウトで計算するために、mxHierarchicalLayout
のインスタンスを生成します。
// グラフにノードやエッジを追加する際に必要 |
最後に、ノードやエッジの追加とレイアウト計算を行いましょう。
// グラフの形状やデザインの変更を行う(グラフモデルを変更する)際は |
tryブロックの中身を詳しく見てみましょう。ノードの追加は
const v1 = graph.insertVertex(parent, null, '1', null, null, 30, 30, null) |
のように行います。各引数の説明は以下のとおりです。
- 第1引数:先程取得した
parent
- 第2引数:ノードを一意に識別するためのID(指定なしの場合は自動的に割り当て)
- 第3引数:ノードのラベル
- 第4,5引数:ノードのx座標・y座標(後でレイアウト自動計算する場合はダミーの値でOK)
- 第6,7引数:ノードの幅・高さ
- 第8引数:ノードのスタイル
また、エッジの追加は
graph.insertEdge(parent, null, null, v1, v3, null) |
のように行います。各引数の説明は以下のとおりです。
- 第1引数:先程取得した
parent
- 第2引数:エッジを一意に識別するためのID(指定なしの場合は自動的に割り当て)
- 第3引数:エッジのラベル
- 第4,5引数:エッジの始点・終点
- 第6引数:エッジのスタイル
最後に、レイアウト計算を呼び出します。
// 追加したノード・エッジに基づいてレイアウトの自動計算を行う |
以上で、画像のように階層グラフが描画できました。
ノードの形状や色の変更
続いて、ノードの形状や色をカスタマイズしてみましょう。
(紹介するソースコード全体はEdit fiddle - JSFiddle - Code Playgroundで確認できます。)
graph.insertVertex()
の第8引数でノードのstyleを指定できます。また、styleに名前を付けて適用させることも可能です。
// ノード追加 |
tooltip
次は、ノードやエッジにマウスを当てた際にtooltipを表示させてみましょう。
(紹介するソースコード全体はEdit fiddle - JSFiddle - Code Playgroundで確認できます。)
まずは、tooltipを有効にしましょう。
// tooltipを有効にします |
すると、ノードのラベルがtooltipで表示されました。
ラベル以外のテキストをtooltipで表示させたい場合は、graph.getTooltipForCell
メソッドをoverrideすれば良いです。
(mxGraphでは、ノードやエッジをmxCell
クラスで扱います。graph.getTooltipForCell
は、このmxCell
を引数として表示したいtooltipを返すメソッドです。デフォルトでは先程のようにラベルがtooltipで表示されます)
cell
がエッジか否かの判定は、graph.getModel().isEdge(cell)
で行うことができます。
// tooltipで表示させたい内容を設定します |
イベント
イベント処理の例として、クリックしたノードの色をオレンジに変更してみましょう。
(紹介するソースコード全体はEdit fiddle - JSFiddle - Code Playgroundで確認できます。)
graph.addListener
メソッドでイベント発火時の処理を設定できます。
イベント一覧はmxEventに記載されています。
// クリックしたノードをオレンジ色に変更します |
SVG形式でexport
描画したグラフをexportできます。
SVG形式でexportする例をEdit fiddle - JSFiddle - Code Playgroundで確認できます。
ブラウザ上で描画したグラフをファイルとして保存できるのは凄く便利ですね。
SVG形式なので、業務で扱うような大きいグラフでも潰れずに表示できるのも良いです。
(Vis Networkの場合はcanvasで描画しているため、ファイル出力してもブラウザで見えている範囲だけの画像となり、大きなグラフを鮮明に表示することが難しかったです)
// グラフをexportするためのボタンを配置します |
Vis Network vs mxGraph
業務でVis NetworkとmxGraphを両方使ってみて大きく異なっていた点を紹介します。
階層グラフのエッジ間の交差
冒頭で書いたとおりです。mxGraphの方がエッジ間の交差が少なく、見やすいレイアウトになっています。
エッジがノードを貫通する場合
下の画像はVis Networkの自動レイアウトで階層グラフを描画したものです。
(ソースコードはこちら)
このグラフのオレンジ色のエッジに注目してみましょう。
一見すると、ノード1からノード3へ伸びるエッジとノード3からノード10へ伸びるエッジがあるように見えます。
しかし、ソースコードを見ると分かる通り、実際には後者のエッジはノード1からノード10へ伸びたものです。道中でノード3を貫通しているために、あたかもノード3から伸びているように見えてしまいます。
このように、Vis Networkにおいてエッジがノードを貫通する場合にはエッジの始点がどこなのか判別しにくくなるという課題があります。
mxGraphの場合はどうでしょうか。下の画像は、上と同じ階層グラフをmxGraphの自動レイアウトで描画したものです。レイアウト計算が賢いため、そもそもエッジがノードに重ならずに描画されました。(ソースコードはこちら)
比較のために、mxGraphで自動レイアウトを使わずに描画し、エッジがノードと重なる例を見てみます。
以下のグラフはノード1からノード2、ノード1からノード3へのエッジが出ています。ノード1からノード3へのエッジは道中でノード2の上を通るので、エッジの始点の判別がしやすいことがわかります。
(ソースコードはこちら)
描画速度
描画速度はどうでしょうか?
Vis Networkはcanvas, mxGraphはSVGで描画しているため、Vis Networkの方が速いことが予想されます。以下では簡単に性能比較を行ってみます。
- 下記のような一本道のグラフを自動レイアウトで描画するために要する時間を色々なノード数(10個,100個,1000個,2000個,4000個の5種類)に対して計測する
- 計測値は、3回測定して平均を取ったもの(単位:ミリ秒)を採用する
計測結果は以下のようになりました。
ノード数 | Vis NetWork | mxGraph |
---|---|---|
10 | 33.40 | 43.12 |
100 | 103.52 | 181.98 |
1000 | 742.94 | 1491.80 |
2000 | 1429.83 | 2906.09 |
4000 | - | 6900.84 |
ノード数が2000以下の範囲では、Vis Networkの方が概ね2倍程度速く描画できることがわかりました。
なお、Vis Networkでノード数4000の場合はMaximum call stack size exceeded
エラーが出たため空欄になっています。 5
mxGraphの描画速度を改善することはできないのでしょうか?
実は、何行かコードに追加するだけで、ある程度の改善が可能です。 6
追加したコードとその周辺を以下に載せます。次の2つの改善を行っています。
- グラフモデルの更新が完了したときに初めて描画を行うようにする
ignoreStringSize
を有効にする
// 高速化その1:グラフモデル更新中は描画をOFFにする |
高速化を施したmxGraphも含めて、計測結果を再掲します。Vis Networkの1.5倍程度まで改善できました。
ノード数 | Vis NetWork | mxGraph(高速化なし) | mxGraph(高速化あり) |
---|---|---|---|
10 | 33.40 | 43.12 | 31.78 |
100 | 103.52 | 181.98 | 144.06 |
1000 | 742.94 | 1491.80 | 1128.03 |
2000 | 1429.83 | 2906.09 | 2122.71 |
4000 | - | 6900.84 | 3477.48 |
まとめ
mxGraphの自動レイアウトを用いて階層グラフの可視化を行い、複雑な階層グラフが綺麗に描画できることを紹介しました。
また、Vis NetworkとmxGraphの両方を業務で扱ってみて得た知見についても紹介しました。
コアテクノロジーユニットでは、現在チームメンバーを募集しています。
私たちと一緒にテクノロジーで設計、開発、テストの高品質・高生産性を実現する仕組みづくりをしませんか?
興味がある方はお気軽に技術ブログTwitterや会社採用HPへ、連絡をお待ちしております。
https://www.future.co.jp/recruit/
- 1.フューチャー発のOSSであるCheetah Gridも高速に描画するためにcanvasを使用しています。興味がある方はVue.jsで最速に始めるCheetah GridやCheetahGrid+Vue.jsをエンプラで使ってみたを御覧ください ↩
- 2.階層グラフの可視化や Layered graph drawing - Wikipediaなどに詳しい説明があります。 ↩
- 3.アーカイブされ、issueが閲覧できなくなってしまいました… ↩
- 5.実装の詳細は確認できていませんが、自動レイアウト計算の実装で再帰関数を使っており、再帰の深さが一定値を超えたためエラーが出たと推測されます。 ↩
- 6.この記事で紹介する改善策は以前に本家のissueで見かけて知ったのですが、現在は閲覧できなくなってしまいました... ↩