D3.js サンプル orgoShmorgo を読んでソーシャルグラフを考えるの巻き – その壱

Pocket

僕のテーマは人と人との紹介情報により、その人の価値を可視化するというものがあります。
以前からspyseeのような人物相関図を描画したいと思っていたのですが、描画処理の計算が難解そうに思えて、自分で開発することは諦めておりました。

例えば、工藤静香さんの相関図はこうなっております。

spysee工藤静香

出典:工藤静香 相関図 – あのひと検索スパイシー

っていうか、このページで利用されている工藤静香さんの写真がやばすぎですね。めちゃめちゃひどい(笑)
僕は、ベスト盤とかごく稀に聞くぐらいのファンなんですが。。
ま、spyseeは独自のアルゴリズムでインターネット上をクロールし、画像とテキストを分析して相関図を作成するのですが、こういうことも起きますね。

さて、上図の相関図はFLASHで作成されているのですが、いろいろありまして、D3.js (is a JavaScript library for manipulating documents based on data. )を利用してソーシャルグラフを描画できそうだということがわかりました。

そこで、僕のイメージに近いサンプルorgoShmorgoのソースコードを読み解きながら、ソーシャルグラフ作成について考えてみたいと思います。

orgoShmorgo

また、作業を始める前に、D3.jsの概要を把握する為にドットインストールさんのD3.js入門の動画を見て参考とさせていただきました。
いろいろと面白そうです。

それから、SVGについても理解が必要なので、@ITさんの10分でわかるSVG 基礎編と、SVGで図形やアニメを描画してみようで基礎的な知識をインプットしておきました。

まずは処理遷移を把握してみます

まずは処理の概要を把握します。

サンプルをダウンロードして、さっと眺めたところ、atomDB.jsにサンプル用のデータが格納され、main.jsにてメインの処理が実装されているようです。
まずはatomDB.jsです。

うにうに動くサンプルのオブジェクトのデータが入っているのがわかります。シンプルな構造のデータです。


var atomDB = {};

atomDB ['C'] = {
    name: 'Carbon',
    symbol: 'C',
    lonePairs: 4,
    size: 12
};

atomDB ['O'] = {
    name: 'Oxygen',
    symbol: 'O',
    lonePairs: 2,
    size: 12
};

このあたりはいまのところフーンとしてスルーします。

次に、main.jsを眺めます


(function () {
	var width = 1160,
	    height = 600;

	var color = d3.scale.category20();

	var moleculeExamples = {};

	var radius = d3.scale.sqrt()
	    .range([0, 6]);

うーん、しばらく変数宣言が続くので、ずーっとスルーします。

と、どうやら、これがメイン処理というのが見つかりました。


    var newMoleculeSimulation = function (newMolecule, example) {
        // Might be super dirty, but it works!
        $('#moleculeDisplay').empty();
        svg = d3.select("#moleculeDisplay").append("svg")
                    .attr("width", width)
                    .attr("height", height)
                    .call(selectionGlove);
        if (example)
            newMolecule = newMolecule[example];
        newMolecule = $.extend(true, {}, newMolecule);
        orgoShmorgo(newMolecule);

        Messenger().post({
          message: 'New Molecule Loaded',
          type: 'success',
          showCloseButton: true,
          hideAfter: 2
        });
    };

すげー、汚いけど動くぜ!!って書いてあります。
いいっすね。そういう感覚に惚れます。まずサンプルの要件を満たす事が重要ですものね。

まず、関数を代入しているnewMoleculeSimulation ですが、分子シミュレーション(Molecular Simulation)ということらしいです。
オリジナルのソースコードに、勝手に解説を紛れ込ませてみます。


    var newMoleculeSimulation = function (newMolecule, example) {
        // Might be super dirty, but it works!
        // 超汚ねーけど、うごくぜ、ひゃっほー!!
        // jQueryで、@id=moleculeDisplay 要素を空にする
        $('#moleculeDisplay').empty();
        // 空にした要素の中ににsvg領域を追加する。
        // .call(selectionGlove);が何者かは不明。要調査
        svg = d3.select("#moleculeDisplay").append("svg")
                    .attr("width", width)
                    .attr("height", height)
                    .call(selectionGlove);
        // 引数のexampleが入っていれば・・という。どうやら分子構造のパターンらしいですが、ま、ガン無視
        if (example)
            newMolecule = newMolecule[example];
        // 分子を生成 - jQuery.extend()でオブジェクトをマージ
        newMolecule = $.extend(true, {}, newMolecule);
        // おそらくここが分子構造図の描画処理 - ここを更に追えば良い
        orgoShmorgo(newMolecule);

        // 右下にメッセージを表示する - どうでもいいのでガン無視
        Messenger().post({
          message: 'New Molecule Loaded',
          type: 'success',
          showCloseButton: true,
          hideAfter: 2
        });
    };

さて、どうやら、orgoShmorgo という関数で描画処理を行っているようなので、更にこれを追いかけてみます。

と、言いたいところですが、「.call(selectionGlove);」も何者かわからないので、先にこちらをクリアにしてみます。

.call(selectionGlove); って何者?

さて、selectionGloveが何者なのかを追います。
どれどれ。

	var selectionGlove = glow("selectionGlove").rgb("#0000A0").stdDeviation(7);

うーん、、なんとなく、大したこと無さそうですね。これはオブジェクトを掴んだ時のglow(発光)効果っぽい感じがします。
libsの下にあるglow.jsを読むと、やはりそうした内容が書かれているようです。

glow javascript libraryで検索すると、BBCのライブラリがヒットしますが、こちらのglow.jsは記述の中に、「d3」の文字が見えますので、D3.js用のライブラリで間違いないようです。
以下、抜粋しておきます。


  my.rgb = function(value) {
    if (!arguments.length) return color;
    rgb = value;
    color = d3.rgb(value);
    var matrix = "0 0 0 red 0 0 0 0 0 green 0 0 0 0 blue 0 0 0 1 0";
    colorMatrix = matrix
      .replace("red", color.r/256)
      .replace("green", color.g/256)
      .replace("blue", color.b/256);
    return my;
  };

うーん、ここではglow効果に利用していますが、内容を変更する事で、オブジェクトをクリックした際の動作に利用できそうです。

あと、.call()についてはこちらの記事に

call(関数[,引数1,引数2,引数3,....])

こんな感じだっぺという記述があったので、ほーなるほどと思う事にしました。
今のところは、なんとなく把握しておくだけ異にします。
それでは本筋に戻ります。

orgoShmorgo(newMolecule);を追いかける

さて、それでは重要な処理部分をぶっこ抜き引用してみます。


	var orgoShmorgo = function(graph) {
	  var nodesList, linksList;
	  nodesList = graph.nodes;
	  linksList = graph.links;

	  var force = d3.layout.force()
	  		.nodes(nodesList)
	  		.links(linksList)
	  		.size([width, height])
	  		.charge(-400)
	  		.linkStrength(function (d) { return d.bondType * 1;})
	  		.linkDistance(function(d) { return radius(d.source.size) + radius(d.target.size) + 20; })
	  		.on("tick", tick);

	  var links = force.links(),
	  		nodes = force.nodes(),
	  		link = svg.selectAll(".link"),
	  		node = svg.selectAll(".node");

	  buildMolecule();

	  function buildMolecule () {
	  	// Update link data
	  	link = link.data(links, function (d) {return d.id; });

		  // Create new links
		  link.enter().insert("g", ".node")
		      .attr("class", "link")
		      .each(function(d) {
		      	// Add bond line
		      	d3.select(this)
		      		.append("line")
							.style("stroke-width", function(d) { return (d.bondType * 3 - 2) * 2 + "px"; });

						// If double add second line
						d3.select(this)
							.filter(function(d) { return d.bondType >= 2; }).append("line")
							.style("stroke-width", function(d) { return (d.bondType * 2 - 2) * 2 + "px"; })
							.attr("class", "double");

						d3.select(this)
							.filter(function(d) { return d.bondType === 3; }).append("line")
							.attr("class", "triple");

						// Give bond the power to be selected
						d3.select(this)
							.on("click", bondClicked);
		      }); 

		  // Delete removed links
		  link.exit().remove(); 

		  // Update node data
	  	node = node.data(nodes, function (d) {return d.id; });

	    // Create new nodes
		  node.enter().append("g")
		      .attr("class", "node")
		      .each(function(d) {
		      	// Add node circle
			      d3.select(this)
			      	.append("circle")
		      		.attr("r", function(d) { return radius(d.size); })
		      		.style("fill", function(d) { return color(d.symbol); });

		        // Add atom symbol
			      d3.select(this)
			      	.append("text")
							.attr("dy", ".35em")
							.attr("text-anchor", "middle")
							.text(function(d) { return d.symbol; });

						// Give atom the power to be selected
						d3.select(this)
							.on("click", atomClicked);

						// Grant atom the power of gravity
						d3.select(this)
							.call(force.drag);
			    });

		  // Delete removed nodes
	    node.exit().remove();

		  force.start();
	  }

おー、なんかすげーぞ、読み応えのあるソースが出て来た。

とりあえず、いつものごとく変数宣言は無視して、メイン処理を見るとどうやら、

buildMolecule();

という記述があるので、

function buildMolecule () {

の部分がメインの処理のようです。

ここからがっつり読み込まないといけなさそうなので、今日のところはここまでにして引き続き次回以降に読解を続けたいと思います。

 

D3.js サンプル orgoShmorgo を読んでソーシャルグラフを考えるの巻き – その弐 へ続く

Pocket