カタカタブログ

SIerで働くITエンジニアがカタカタした記録を残す技術ブログ。Java, Oracle Database, Linuxが中心です。たまにRuby on Railsなども。

D3.js Force Layoutのノードを画像に変えてみる

前回、前々回とD3.jsのForce Layoutで簡単なネットワークを表現したが、ノードにただのcircleを使っていたので、今回はこれを画像に変えてみた。画像にすることで、ノードの意味をより明確に視覚化することができる。

過去記事:
D3.jsのForce Layout (力学モデルでグラフ描画するレイアウト)を動かしてみた - カタカタブログ
D3.js Force Layoutのパラメータについて調べてみた - カタカタブログ

ノードのグループ化

その前に、簡単なリファクタリングをしておく。今まではSVGで、ノードを表すcircleとtext要素をそれぞれ別々に定義していたが、g要素で囲むことでグループ化でき、そのグループに対して座標移動させるようなコードに変更する。

今までのコード

    var node = svg.selectAll("circle")
                  .data(nodes)
                  .enter()
                  .append("circle")
                  .attr({r: 20,
                         opacity: 0.5})
                  .style({"fill": "red"})
                  .call(force.drag);
    var label = svg.selectAll('text')
                    .data(nodes)
                    .enter()
                    .append('text')
                    .attr({"text-anchor":"middle",
                           "fill":"white"})
                    .style({"font-size":11})
                    .text(function(d) { return d.label; });
    force.on("tick", function() {
      link.attr({x1: function(d) { return d.source.x; },
                 y1: function(d) { return d.source.y; },
                 x2: function(d) { return d.target.x; },
                 y2: function(d) { return d.target.y; }});
      node.attr({cx: function(d) { return d.x; },
                 cy: function(d) { return d.y; }});
      label.attr({x: function(d) { return d.x; },
                  y: function(d) { return d.y; }});
    });

グループ化したコード

    var g = svg.selectAll("g").data(nodes).enter().append("g”)
                  .call(force.drag);
    var node = g.append("circle")
                  .attr({r: 20,
                         opacity: 0.5})
                  .style({"fill": "red"})
    var label = g.append("text")
                    .attr({"text-anchor":"middle",
                           "fill":"white"})
                    .style({"font-size":11})
                    .text(function(d) { return d.label; });
    force.on("tick", function() {
      link.attr({x1: function(d) { return d.source.x; },
                 y1: function(d) { return d.source.y; },
                 x2: function(d) { return d.target.x; },
                 y2: function(d) { return d.target.y; }});
      g.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
    });

これでノードとラベルの座標を指定するコードに重複がなくなった!この変更前後で表示されるネットワーク図はまったく変わらない。

ノードを画像に

続いて、ノード部分を画像に変えてみる。SVGで画像を表示するためには、imageタグを以下のように使用する。

<image xlink:href="<画像へのパス>" width="75" height="60"></image>

今回、サンプル画像として、以下のサイトからフリーアイコンをSVG形式でダウンロードしたので、それをHTMLファイルが置かれたディレクトリにimagesというディレクトリを作成し、その中に配置した。
http://www.flaticon.com/free-icon/dwelling-house_2144

circle要素のところをimageに置き換え、参照先に入手した画像を指定する。座標はさきほど定義したグループに設定しているので、画像を配置するときに座標は意識しなくてよい。

    var g = svg.selectAll("g")
                  .data(nodes)
                  .enter()
                  .append("g")
                  .call(force.drag);
    var node = g.append("image")
                  .attr({"xlink:href":"images//dwelling1.svg",
                         "width":75,
                         "height":60
                        });
    var label = g.append("text")
                    .attr({"text-anchor":"middle",
                           "fill":"black"})
                    .style({"font-size":14})
                    .text(function(d) { return d.label; });


ノードが単純な円だったのが、きれいな画像に変わった!もちろん、今回はローカルの画像を表示したが、パスをURL形式にすればインターネット上の画像表示もできるし、jpenやpng形式の画像も表示できる。
f:id:osn_th:20141127080823p:plain

以上!