vis.jsを使ってネットワークを書く

# はじめに

私はネットワーク（つながりネットワーク的なもの）を描く際に[vis.js](http://visjs.org/)を使っているのですがあまりメジャーじゃないように思うので宣伝します。

# 基本的なネットワークの描き方

論よりコードということでさっそくサンプルコードです。

```html:ssample1.html
<html>
  <head>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.css" rel="stylesheet">
    <style type="text/css">
      #network {
        width:  400px;
        height: 400px;
        border: 1px solid #000;
      }
    </style>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.js"></script>
  </head>
  <body>
    <div id="network"></div>
    <script type="text/javascript">
      var nodes = new vis.DataSet([
        {id: 1, label: 'A'},
        {id: 2, label: 'B'},
        {id: 3, label: 'C'},
        {id: 4, label: 'D'},
        {id: 5, label: 'E'},
        {id: 6, label: 'F'},
        {id: 7, label: 'G'},
        {id: 8, label: 'H'},
      ]);
      var edges = new vis.DataSet([
        {from: 1, to: 2, arrows: 'to'},
        {from: 1, to: 3, arrows: 'to'},
        {from: 3, to: 4, arrows: 'to'},
        {from: 6, to: 1, arrows: 'to'},
        {from: 7, to: 8, arrows: 'to'},
        {from: 8, to: 7, arrows: 'to'},
      ]);
      var container = document.getElementById('network');
      var data = {
        nodes: nodes,
        edges: edges
      };
      var options = {
      };
      var network = new vis.Network(container, data, options);
    </script>
  </body>
</html>
```

1. ノードのオブジェクトを定義
2. エッジのオブジェクトを定義（どのノードとどのノードをつなげるか）
3. その他のオプションを定義（ここでは空）
4. 描画する場所のDOMを指定してNetworkオブジェクト作成

これだけで以下のようなネットワークが描けます。

![sample1.png](https://qiita-image-store.s3.amazonaws.com/0/141091/f38e6476-a683-5ee7-8d2d-874ffef01a91.png)

気に入ってる点として、

- ノードをドラッグする
- ビューポートを移動する（ノード以外をドラッグ）
- 拡大縮小する（マウスホイール）

なんて感じにインタラクティブにネットワークをいじることができます。
またノードの配置はデフォルトで「物理的に安定な位置」に配置されるようになっています（リロードするたびに位置が変わります）。自動計算だけでなく自分で位置指定することも可能です。

以下、ノード、エッジのプロパティを設定していくことでネットワークの見た目を徐々に改善していこうと思います。注目点だけ差分表示します。全体は以下のgistを参照してください。

https://gist.github.com/junjis0203/768b45609b1d648e6016f66b38269a5b

# ノードを色分けする（groupプロパティ）

え？全部のノードが同じでわかりにくい？そういう場合はノードをグループ化しましょう。

```js:sample2.htmlの一部
      var nodes = new vis.DataSet([
        {id: 1, label: 'A', group: 1},
        {id: 2, label: 'B', group: 1},
        {id: 3, label: 'C', group: 1},
        {id: 4, label: 'D', group: 1},
        {id: 5, label: 'E', group: 2},
        {id: 6, label: 'F', group: 2},
        {id: 7, label: 'G', group: 2},
        {id: 8, label: 'H', group: 2},
      ]);
```

![sample2.png](https://qiita-image-store.s3.amazonaws.com/0/141091/fe809745-89b9-7201-2195-a75a8516d150.png)

はい、Fさんが裏切者なことがはっきりしました（？）

グループの色は順番に割り当てられますがオプションで指定することもできます。ただし、{通常,クリックしたとき}×{ノードの色,枠の色}を指定しないといけないので結構めんどくさいです。

# エッジを太くする（widthプロパティ）

AさんはBさんよりCさんの方が好き、ということがわかるようにしましょう。具体的には線を太くします。

```js:sample3.htmlの一部
      var edges = new vis.DataSet([
        {from: 1, to: 2, arrows: 'to', width: 1},
        {from: 1, to: 3, arrows: 'to', width: 5},
        {from: 3, to: 4, arrows: 'to'},
        {from: 6, to: 1, arrows: 'to'},
        {from: 7, to: 8, arrows: 'to'},
        {from: 8, to: 7, arrows: 'to'},
      ]);
```

![sample3.png](https://qiita-image-store.s3.amazonaws.com/0/141091/fefd098b-90ff-8765-12d3-dd30f4953e20.png)

線の太さは10を超えるとかなり太い印象になる（特に矢印を付けてるとジョークみたいにでかくなる）ので10を超える場合は適当にスケーリングすることをおすすめします。

# ノードの大きさを変える（valueプロパティ）

このネットワークを見る分にキーマンはAさんだ。ということがはっきりするようにAノードを大きくしましょう。
基本的にはvalueを設定するだけですがノードの形状[^1]によってはscaling.label.enabledをtrueにしないと大きくなりません。

[^1]: 今回は省いていますがshapeで指定できます。画像表示も可能です。

```js:sample4.htmlの一部
      var nodes = new vis.DataSet([
        {id: 1, label: 'A', group: 1, value: 20, scaling: { label: { enabled: true} }},
        {id: 2, label: 'B', group: 1},
        {id: 3, label: 'C', group: 1},
        {id: 4, label: 'D', group: 1},
        {id: 5, label: 'E', group: 2},
        {id: 6, label: 'F', group: 2},
        {id: 7, label: 'G', group: 2},
        {id: 8, label: 'H', group: 2},
      ]);
```

![sample4.png](https://qiita-image-store.s3.amazonaws.com/0/141091/fc74f34b-a7e8-8039-9e98-ef66809c7d6a.png)

# ノード間の位置を調整する（massプロパティ）

ところで今までのネットワークを見てて気になることがありませんでしたか？
そう、Eさんは誰ともつながっていないぼっちです。ぼっちはぼっちとわかるように輪の外にはじきだしましょう（ひどい）

ネットワークはデフォルトで「半重力モデル」というものが設定されており、mass（質量）を大きく設定することで遠くに配置することができるようになります。

```js:sample5.htmlの一部
      var nodes = new vis.DataSet([
        {id: 1, label: 'A', group: 1, value: 20, scaling: { label: { enabled: true} }},
        {id: 2, label: 'B', group: 1},
        {id: 3, label: 'C', group: 1},
        {id: 4, label: 'D', group: 1},
        {id: 5, label: 'E', group: 2, mass: 2},
        {id: 6, label: 'F', group: 2},
        {id: 7, label: 'G', group: 2},
        {id: 8, label: 'H', group: 2},
      ]);
```

![sample5.png](https://qiita-image-store.s3.amazonaws.com/0/141091/b80c2317-a0e4-3fed-0c0c-9502824198c8.png)

この質量プロパティは調整が難しくて2だと「そこそこ離れた位置」に配置されます。5だとかなり遠くに置かれますが、そのために初期表示で他のノードも含めた全体図がかなり縮小表示されることになります。「ぼっちが孤立してることが強調されるがそこまで離れていない位置」というのが難しいところです。

# ノードがクリックされたら反応する

さて最後。ノードをクリックしたら該当ノードの情報を表示するとかしてみましょう。イベントが用意されているので簡単です。

```js:sample6.htmlの一部
      network.on("click", function(params) {
        if (params.nodes.length == 1) {
          var nodeId = params.nodes[0];
          var node = nodes.get(nodeId);
          console.log(node.label + 'がクリックされました');
        }
      });
```

注意が必要な点として、イベントで渡ってくるのはノードのidなので改めてDataSetオブジェクトからノードのオブジェクトを取得する必要があるということです。なお、ノードのオブジェクトはvis.jsで使うプロパティだけでなく任意のプロパティを設定することができるのでクリック時に特定のURLに飛ばすとか自由に行うことが可能です。

以上、vis.js（ネットワーク図）の紹介でした。他にもいろいろな設定ができるのでぐりぐり動かせるネットワーク図を描きたいときにいかがでしょうか。

P.S.
@altさんせっかく編集リクエストを送っていただいたのに「いやこれHTMLの一部だからhtmlで正しい」とrejectしてしまい申し訳ありませんでした。&lt;script&gt;で囲んでないとJSは強調されないんですね。