きなこもち.net

.NET Framework × UiPath,Orchestrator × Azure × AWS × SIer

Angular × vie.js/D3.js × ネットワークグラフを描いてみた

この記事の目的

この記事は、

Angularのアプリケーションで、ネットワークグラフを表示すること
を目的としています。

本題

ネットワークグラフ表示ライブラリ

対象とするライブラリ

参考記事*1によると、7つのメジャーなライブラリがあるということでした。全部はやれないので、今回は、

を使って実験してみました。

Angular アプリケーションへの導入方法

Angularでは、D3.jsのような公開ライブラリをりようするために、いくつかステップを踏まないといけないようです。
angular.jp また、sigma.jsに関して言えば、GitHubリポジトリWikiにAngular向けのインストール手順を記載してくれているので、これも参考にして、設定すれば設定することができます。

Tips1

上記手順で、以下のコマンドを利用してsigmaをダウンロードすることになっていますが、自分の環境では、最新版(2.X)がダウンロードされてしまい、うまくサンプルコードを実行することができませんでした。そのため、バージョン指定してインストールしたほうが良いと思いました!

×npm install sigma -save
〇npm install sigma@1.2.1 -save

参考) sigma.js does not work in Angular 8 · Issue #1045 · jacomyal/sigma.js · GitHub

Tips2

SigmaWikiにある手順でも、もちろんうまくいくのですが、自分は以下の手順で設定しました。

- npm install <library name@ version >-save.
- Start your angular app with [ npm start ].
- add [import <library> from '<Library download dir> '] into app.component.ts.
- add sample code into app.component.ts.

sigma.js

試してみた。けど、ぬるぬる動かなかったのと、ノードの位置をコードから設定しないといけないのがハードル高めだったので、利用を見送りました(/・ω・)/

サンプルでは、こんなイメージを作れました。 f:id:kinakomotitti:20200216132807p:plain

参考)sigmajs.org

D3.js

雑感

ヌルヌル動く、かっこいいネットワークグラフが作れるライブラリです。たくさんサンプルコードとライブデモが公開されているので、目的の表現を探して、すぐに取り込める点が素敵だなと思いました。 また、基本的なところから丁寧に解説されているサイトがあったので、そこを参考にしながらサンプル実装しました。

できたもの

f:id:kinakomotitti:20200216133229p:plain

実装

シンプルな出力結果ですが、思ったよりも多くのコードを実装しないといけないです。でも、その分細かいところまで作りこんだグラフが作成できそうです。今回は、”やじるし”までこだわって作ろうと思ったのですが、時間の関係で見送りました( ゚Д゚)

    // 1. 描画用のデータ準備
    var linksData = [
      { "source": 0, "target": 1, type: "suit" },
      { "source": 1, "target": 4, type: "resolved" },
    ]
    var width = document.querySelector("svg").clientWidth;
    var height = document.querySelector("svg").clientHeight;
    var nodesData = [];
    for (var i = 0; i < 6; i++) {
      nodesData.push({
        "id": i,
        "x": width * i * Math.random() * 0.9,
        "y": height * i * Math.random() * 0.9,
        "r": 10,
        "text": "Node" + i
      });
    }

    // 2. svg要素を配置
    var link = d3.select("svg")
      .selectAll("line")
      .data(linksData)
      .enter()
      .append("line")
      .attr("stroke-width", 10)
      .attr("stroke", "orange");

    var nodeGroup = d3.select("svg")
      .selectAll("g")
      .data(nodesData)
      .enter()
      .append("g")
      .call(d3.drag()
        .on("start", dragstarted)
        .on("drag", dragged)
        .on("end", dragended));

    nodeGroup.append("circle")
      .attr("cx", function (d) { return d.x; })
      .attr("cy", function (d) { return d.y; })
      .attr("r", function (d) { return d.r })
      .attr("fill", "blue")
      .attr("stroke", "black")
      .append("title")
      .text("This is title.");

    nodeGroup.append("text")
      .attr("text-anchor", "right")
      .attr("dominant-baseline", "middle")
      .style("font", "42px sans-serif")
      .text(function (data) { return data.text; })
      .append("title")
      .text("This is title.");

    // 3. forceSimulation設定
    var simulation = d3.forceSimulation()
      .force("collide",
        d3.forceCollide()
          .radius(function (d) { return d.r * 2 }))
      .force("link", d3.forceLink().distance(100))
      .force("charge", d3.forceManyBody().strength(-400))
      .force("x", d3.forceX().strength(0.05).x(width / 2))
      .force("y", d3.forceY().strength(0.05).y(height / 2));

    simulation
      .nodes(nodesData)
      .on("tick", ticked);

    simulation.force("link")
      .links(linksData);

    // 4. forceSimulation 描画更新用関数
    function ticked() {
      nodeGroup.select("circle")
        .attr("cx", function (d) { return d.x; })
        .attr("cy", function (d) { return d.y; });
      nodeGroup.select("text")
        .attr("x", function (d) { return d.x + 10; })
        .attr("y", function (d) { return d.y; });
      link
        .attr("x1", function (d) { return d.source.x; })
        .attr("y1", function (d) { return d.source.y; })
        .attr("x2", function (d) { return d.target.x; })
        .attr("y2", function (d) { return d.target.y; });
    }

    // 5. ドラッグ時のイベント関数
    function dragstarted(d) {
      if (!d3.event.active) simulation.alphaTarget(0.3).restart();
      d.fx = d.x;
      d.fy = d.y;
    }

    function dragged(d) {
      d.fx = d3.event.x;
      d.fy = d3.event.y;
    }

    function dragended(d) {
      if (!d3.event.active) simulation.alphaTarget(0);
      d.fx = null;
      d.fy = null;
    }

参考サイト) wizardace.com

vis.js

雑感

ノードをドラッグしてヌルヌル動かせる。だけではなく、ノード以外の部分をドラッグして、全体を移動させること、マウスのホイールをくるくるすることで、全体のズームイン・アウトができる点が高性能だと思いました。ノードの色とか、テキストの見せ方とかについては、まだ十分な検証ができていないですが、少ない手数で、結構きれいなネットワークグラフが作成できます!これはすごい。

vis.jsのヌルヌルを体験したい方は、↓へアクセスしてみてください。

visjs.org

できたもの

f:id:kinakomotitti:20200216155717p:plain

実装

ノードとなるデータを準備して、ノード間のリンクを定義して、ライブラリに渡すだけで、指定したElementにグラフが表示されます。何ともシンプルで、こんな使い方ができるライブラリを探していたのでちょうどよかったです! ’’’ // create an array with nodes var nodes = new vis.DataSet([ { id: 0, label: "Node 0" }, { id: 1, label: "Node 1" }, { id: 2, label: "Node 2" }, { id: 3, label: "Node 3" }, { id: 4, label: "Node 4" }, { id: 5, label: "Node 5" }, ]);

// create an array with edges
var edges = new vis.DataSet([
  { from: 1, to: 0, arrows: "to"},//, dashes: true },
  { from: 1, to: 4, arrows: "to" },
]);

// create a network
var container = document.getElementById("mynetwork");
var data = {
  nodes: nodes,
  edges: edges
};
var options = {};
var network = new vis.Network(container, data, options);

'''

まとめ

  • Angularに、公開ライブラリを設定する手順は、公式の手順に従う!
  • ネットワークグラフをサクッと作るなら、Vie.jsがよさそう!
  • vue 3d network用に作ったけど、vis.jsのサンプルとかを実装したAngularプロジェクトは、ここ*2