cytoscape.js を用いたインタラクティブなグラフ描画の実装



# はじめに

ちょっとしたネットワークグラフを書きたいとき、[graphviz](https://graphviz.org/)、[plantUML](https://plantuml.com/ja/)、[blockdiag](http://blockdiag.com/ja/blockdiag/)等は便利ですが、細かい調整をしようとすると泥沼にハマります。😅しかし、[drawi.io](https://ferret-plus.com/8408) などで１からネットワークグラフを書くのはしんどい。そこで「良きに計らえ」でいい感じでだいたいノードをレイアウトしてもらって、その後手動でノードを動かして最終形にするのが一番効率がよいと思われます。

このようなインタラクティブなグラフ作成をしたいとき、[cytoscape](https://qiita.com/keiono/items/0db6495c4d8bc215de67)のような本格的なデスクトップアプリもありますが、ウェブサービスで実現したいときは cytoscape.js を使うとよいと思われます。
本記事では cytoscape.js を用いて上記を実現する方法を紹介したいと思います。

# 描画するグラフデータについて

描画するネットワークの元データとしては、[DBpedia](https://ja.wikipedia.org/wiki/DB%E3%83%9A%E3%83%87%E3%82%A3%E3%82%A2)を利用しています。ここには概念（単語）とそれらの間の関係（名）が２項関係で大量にストックされて検索可能になっています。詳細は[DBpedia 関連](#dbpedia-関連)を参照ください。これを利用して、あるキーワードを投げて得られたそのキーワードに関係する概念とその関係を図示することにします。
DBpedia が提供する情報だけだと寂しいので、wikipedia の「関連語」情報も併用しています。

# Look & Feel

まずは大体どんな感じのものなのか、下記動画で紹介します。
これは、「デンプン」という単語に関連するオントロジー情報を wikidata から検索して表示しています。検索された情報（ネットワーク図）をきれいに配置するため、ここでは`fcose` というアルゴリズムで「デンプン」を中心としたサークル形状としてノードを配置しています。fcose　アルゴリズムの`spacing Factor`パラメータを調整することで、ノードの大きさや密集度を変更しています。
個々のノードの位置はマウスで調整可能です。オブジェクトを選択してコンテキストメニューで選択することにより削除することもできます。

<iframe width="400" height="628" src="https://www.youtube.com/embed/CDCQvTx2gRU" title="wikidata viewer 1" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# ソースコード

下記から取得できます。

https://github.com/ZudenSachenselbst/sample/blob/master/wikidata_viewer.html

html 単一ファイルです。ダウンロードしてブラウザで開けば使えます。

## codePen

上記ファイルを codePen にも上げています。[^1] こちらはサイト上で直接動作しますが、エディタ画面が邪魔ですね：）また少し動作が遅くなります。


https://codepen.io/ZudenSachenselbst/pen/RwyXPYv

[^1]:同一オリジン制約回避のため、CORS-PROXY を使っています。セキュアでないので github のコードの方を参照して下さい。

# 機能

- ノード追加、ノード検索
単語を入力して`ノード追加`のボタンを押します。wikiData にその概念があれば、ノードとして追加されます。
単語を入力して`ノード検索`のボタンを押すと、その単語のノードがselect された状態になります。ノード数が多くなってくると必要になる機能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350477/d68e4a27-11ab-f772-d09c-f9dd36cb9fc9.png)


- ZOOMとPan
  - グラフ全体をZOOM IN/OUT します。マウスホイールでもZOOM IN/OUT できますがスライダーを使ったほうが細かく調節できます。
  - `矢印をクリックするとグラフ全体がその方向に平行移動します。グラフエリアのノードとエッジ以外の部分をクリックしてドラッグしてもよいです。
  - `square`をクリックすると、グラフ全体がキャンバスにちょうど収まるようzoom in/out してくれます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/350477/60c28152-0ca9-e63e-40bd-1e6d4e705a64.png)

- ノードの移動（１つ、複数）
上記動画で示したように、ノードをクリック＆ドラッグでノードを移動できます。複数のノードを動かすときは、コントロールキーを押しながらノードを１つずつクリックすることで複数のノードを選択できるので、最後のノードの選択（クリック）後、クリックしたままドラッグします。
あるいはシフトキーを押しながらマウスをドラッグすると矩形領域選択モードになり、矩形領域内のすべてのノードが選択された状態になります。この後、選択されたノードのどれか１つをドラッグすることで複数のノード全体が平行移動します。
- ノードやエッジの削除 （１つ、複数）
ノードをクリックしてマウスの右ボタンをクリックします。コンテキストメニューから`remove`を選択することでそのノード（とそのノードにつながっているエッジ）が削除されます。複数ノードの削除は、上述した方法で複数ノードを選択後に同様の操作を行います。

- DBpedia による関連概念の追加
ノードを右クリックしてコンテキストメニューから `onto expand`を選ぶことにより、その概念に関連した概念（ノード）と関係（エッジ）をグラフに追加します。上記 youtube 動画にあるとおりです。 

- wikipedia による関連語の追加

ノードを右クリックしてコンテキストメニューから `wiki expand`を選びます。その概念（単語）がタイトルのwikipedia のページがあれば、そのページ内部の関連語をグラフに追加します。

- ２概念間の関係の発見
グラフ上の２つの概念をつなぐエッジ（関係名）やノード（概念）をDBpedia から検索してグラフに追加します。実装中です😅


- ノードレイアウトの保存と読み込み
`保存`ボタンを押すと、現在のグラフレイアウトをJSON形式で`詳細`エリアに出力します。これをコピペしてテキストファイルとして保存します。また保存したJSONデータを`詳細`エリアにペーストして`読み込み`ボタンを押すと、グラフが復元されます。

- 選択したノード（複数）のレイアウト変更

<iframe width="788" height="520" src="https://www.youtube.com/embed/jmXP-bAo6FY" title="wikidata viewer 2" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

選択したノードを指定されたレイアウトアルゴリズムで再配置します。ノードが選択されていない場合は全ノードが再配置されます。レイアウトとしては`breadthfirst`,`fcose`,`grid`,`random`,`circle`,`concentric`,`cose` といったアルゴリズムが選択できます。各アルゴリズムのパラメータの一部は`spacing factor` スライダーバーで指定することができます。
詳細は[ここ](https://blog.js.cytoscape.org/2020/05/11/layouts/)を参照下さい。

- SVG出力
実装中です😅

# 実装

- DBpedia や wikipedia の検索には axios ライブラリを利用しています。
- コンテキストメニューには`cytoscape-context-menus.js`を利用しています。
- パンとズームは`cytoscape-panzoom.js`を利用しています。
- `fcose`レイアウトアルゴリズムには`cytoscape-fcose.js`を利用しています。

各ライブラリが依存するモジュールについては、ソースコードを参照下さい。

# おわりに

cytoscape.js を利用したインタラクティブな描画実装例として 「[Cytoscape.jsを試してみた](https://qiita.com/madilloar/items/bb9e9dddd37639998637)」があります。大変参考になりました。本記事はこれにノードの動的追加／削減やコンテキストメニューの扱い等を追加したものになります。
実装してみて、豊富な拡張プラグインのお陰で大変書きやすいと感じました。イベントハンドラも書きやすかったです。
問題点としては cytoscape のキャンバスをウェブページ内に組み込むときに絶対位置となるため、ページ上部のボタンやスライダーエリアをresizable にできないという点です。いろいろトライしてみましたがいい解決方法が見つかりませんでした。諸賢の方に（コメント欄で）ご教示いただければ幸いです。

本記事がなにかの参考になれば幸いです。





# link

## cytoscape.js 関連（日本語の紹介記事）

Cytoscape.jsを試してみた - Qiita https://qiita.com/madilloar/items/bb9e9dddd37639998637

jQuery: Cytoscape.js お試せた17 (layout 混合は使い物になるだろうか + nodes.shift()) | site-hhs https://hhsprings.pinoko.jp/site-hhs/2018/01/jquery-cytoscape-js-%E3%81%8A%E8%A9%A6%E3%81%9B%E3%81%9F17-layout-%E6%B7%B7%E5%90%88%E3%81%AF%E4%BD%BF%E3%81%84%E7%89%A9%E3%81%AB%E3%81%AA%E3%82%8B%E3%81%A0%E3%82%8D%E3%81%86%E3%81%8B-cy-shift/

Cytoscape.jsを用いてデータを可視化する - Qiita https://qiita.com/Wangxinyin/items/6965abdd369736e4073f

ネットワークグラフ描画ライブラリ7個まとめ - Qiita https://qiita.com/SuyamaDaichi/items/c47dc74cfefd92516e28

(2017.6.9) Neo4jの可視化ライブラリまとめ https://www.slideshare.net/mkiuchi4/201769-neo4j

Cytoscape.js でインタラクティブなグラフ構造描画 - WordNet の可視化例 - NO_WAIT_FOREVER https://shinaisan.hatenablog.com/entry/2017/04/30/230908

## DBpedia 関連

DBpedia Japanese https://ja.dbpedia.org/

DBペディア - Wikipedia https://ja.wikipedia.org/wiki/DB%E3%83%9A%E3%83%87%E3%82%A3%E3%82%A2

DBpedia Japaneseとは？ https://www.slideshare.net/takeda/what-isdpbedia

DBpediaでWikipediaからデータを取得する方法 - Masamuneブログ https://masamune-blog.hatenablog.com/entry/2021/12/17/154837