d3.jsな日々

d3.jsでデータ可視化する際の覚書です

クラスを一時的に適用/非適用する .classed( )

セレクションにクラスを設定する場合、

セレクション.attr("class", "クラス名")

とする。ここでクラス名は"myclass"のように、先頭の"."は不要。
複数のクラスをつける場合は、"myclassA myclassB"とスペースで区切る。

ただし、attr("class", "..")は、"class"属性を上書きしてしまうから、すでにクラスを設定している状態であるクラスだけを外したい、という場合は使えない。

そういう場合は、

.classed("クラス名", true/false)

を使う。trueで有効、falseで無効化できるので、ある属性を一時的に外したいという時に便利。

zoomってどう使う?

可視化コンテンツではズームやパンが必要になることが多いが、実装はなかなか面倒なもの。しかし心配は不要。d3.jsには強力なズーム&パン機能が備わっている。このAPIが、d3.behavior.zoom() だ。

参考:Zoom Behavior · mbostock/d3 Wiki · GitHub
例:
SVG Geometric Zooming


1.zoomに設定できるもの
まず、zoom APIの設定パラメータ。

# zoom(selection)
指定したセレクションにzoomビヘイビアを設定する。

d3.behavior.zoom(selection)

としてもよいが、よりd3.js的な'Chain Syntax'で書くと、

selection.call(d3.behavior.zoom().以下略)

となる。
このselectionがズームイベントのイベントリスナとなる。


# zoom.translate([translate])
変位(translation)ベクタの初期値を設定。デフォルトは[0,0]。

# zoom.scale([scale])
スケールの初期値を設定。デフォルトは 1。

# zoom.scaleExtent([extent])
ズームスケールの範囲(extent)を設定。デフォルトは[0,∞]。

# zoom.center([center])
ズームの中心座標を2次元配列で設定。デフォルトはnullで、マウスカーソル座標がズーム中心になる(これは便利!)。

# zoom.size([size])
ビューポートのサイズを指定。デフォルトは[960, 500]。サイズを指定しないとスムーズなトランジションにならない。(たとえば、拡大中心がずれる、などの問題がおきるようだ。)

# zoom.x([scale-x])
# zoom.y([scale-y])

それぞれ、ズームにあわてXスケール、Yスケールのドメインが自動的に調節される。横軸、縦軸があるグラフの場合に必須。指定子ない場合はnullとなる。
スケールをプログラムで変更した場合は、zoom.x, zoom.yも再度呼び出す必要がある。
また、Xスケール・Yスケールを設定すると、translate, scaleはデフォルト値(tranalateは[0,0], scaleは1)に戻るので注意。

# zoom.on(type, listener)
イベントと、該当するイベントリスナーを設定。
イベントは、
zoomstart - ズーム開始時(たとえば、指を触れた時)
zoom -ズーム中
zoomend - ズーム終了時(たとえば、指を離した時)
の3種類。すでにリスナが設定された状態で設定すると、新しいリスナに上書きされる。複数のリスナを設定したい場合はnamespaceを使って、namespace.funcA, namescape.funcBというように書く。
リスナを破棄したい場合は、リスナに’null'を設定する。

マウスホイールの場合は物理的なzoomstart/zoomendがないので、ひとつのズーム操作の開始・終了から50ミリ秒以内にイベントが発行される。(つまりあくまでも近似。注意。)

イベントには次のプロパティをもつ。
scale - 現在のスケール:スカラ数
translate -現在のtranslationベクタ:2次元配列

# zoom.event(selection)
'selection'がセレクションの場合、登録されたリスナにただちにズーム・ジェスチャ(zoomstart, zoom, zoomend)を発行する。これはプログラム上でズームと同等の操作を行った時に有用。
'selection'がトランジションの場合、該当するtweenが登録される。これによって、トランジション時にもズームイベントが発行される。なおトランジション終了前にユーザがズーム操作を行なうと、トランジションは中断される。


2−1.zoomを使ったスクリプトの例 単純なズーム

SVG要素('svg_vis')をズーム&ドラッグする例。参考:SVG Geometric Zooming

ズームの設定

var svg_vis = svg_vis_base
		    .append("g").attr("id", "svg_vis")
                  .call(d3.behavior.zoom()
                            .size([w_vis, h_vis])
                            .scaleExtent([1, 8])
                            .on("zoom", zoomed));

イベントリスナ

//When zoomed
function zoomed() {
   svg_vis.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}

zoom自身はセレクションに操作は行わない。ただ、ユーザの操作にしたがってtranslateとscaleを教えてくれるのみ。リスナ側でユーザ操作にしたがってセレクションの視覚的操作(必要ならそれ以外の操作)を記述しなければならない。


2−2.zoomを使ったスクリプトの例 要素の大きさ不変

2−1の例では拡大・縮小時に描画要素の大きさも拡大・縮小される。要素の大きさは不変で位置のみ変える場合は、ズームにあわせてscaleを変更し、変更されたScaleで要素を再度配置する、という手順で行なう。

通常のX-Scale、Y-Scaleの場合は次の参考事例を見ればわかると思う。SVG Semantic Zooming


地図のような2次元のprojectionの場合は、この事例が参考になる。mousewheel-zoom + click-to-center

少し解説する。

var zoom = d3.behavior.zoom()
    .translate(projection.translate())
    .scale(projection.scale())
    .scaleExtent([height, 8 * height])
    .on("zoom", zoomed);
(途中略)
 var g = svg.append("g")
    .call(zoom);

ここがzoomの設定部分だが、肝は、scale(projection.scale())の部分。これによってズームのスケールとプロジェクションのスケールが対応づけされる。

イベントリスナー、

function zoomed() {
  projection.translate(d3.event.translate).scale(d3.event.scale);
  g.selectAll("path").attr("d", path);
}

の1行目は、translateとscaleをズームイベントから取得し、projectionのtranslateとscaleを変更している。2行目は、すべてのPath要素に対して、再描画する操作。ちなみに、pathは、

var path = d3.geo.path()
    .projection(projection);

という変換関数で、その中にprojectionをひもづけしているから、新しいprojection設定後に再度pathで変換すればpath要素は新しい位置に描画される。(要素のpathと変換関数のpathを混同しないように!)

他の描画要素がある場合でも、新しいprojectionで座標のみ描画しなおせばよい。

callってどう使う?

d3.jsのAPIにcallというのがある。関数を呼び出すAPIであることはわかるのだが、具体的な使い方がいまひとつピントこなかったので、Referenceを読んでみた。Selections · mbostock/d3 Wiki · GitHub

まず具体的な例から。

function foo(selection) {
  selection
      .attr("name1", "value1")
      .attr("name2", "value2");
}

という関数があるとする。普通にこの関数を使おうとすると、

foo(d3.selectAll("div"));

というように書くだろう。これはこれで間違いではない。しかし、d3.jsの表記の基本は'Chain Syntax'、つまり、'.'で樹々つなぎにするほうが「d3.js的」だ。そこで作られのが 'call'なのだ(たぶん)。

callでは普通の関数の使い方とは主客逆転する。普通は上の例のように

関数(関数に渡すもの)

と書くが、callを使う場合の書き方は

関数に渡すもの.call(関数)

となる。
ここで「関数に渡すもの」とはd3.jsではセレクションがメインとなる。
つまり、callは、セレクションを処理する関数を’Chain Syntax'に組み込むためのAPI、と考えれば良い。

なお、callはセレクション以外にも引数を渡せる。表記は、

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

となる。

Node.jsでローカルHTTPサーバーを作る

Node.jsは高速でスケーラブルなネットワークアプリケーションを 簡単に構築するためにChromeJavaScript 実行環境 上に構築されたプラットフォーム。 イベント駆動とノンブロッキング I/O モデルを使用することにより 軽量・効率的で、分散されたデバイスにまたがるデータ集約的なリアルタイム アプリケーションを作ることができる。


Node.jsのインストールやバージョンアップはnvm(Node Version Manager)をつかう。
第1回 Node.jsとは:基礎から学ぶNode.js|gihyo.jp … 技術評論社
node.jsのバージョンアップ、バージョン切り替え - Qiita


インストールは'nvm install バージョン'で行う。

$ nvm install 0.10.25
######################################################################## 100.0%
Now using node v0.10.25

マイナーバージョン(ビルド番号)を省略すると最新のマイナーバージョンがインストールされる。

Node.jsを起動するには

$ node -v

と入力し、バージョンが表示されればOK。

nvm(node version manager) が動くようにする。インストールは

$ git clone git://github.com/creationix/nvm.git ~/.nvm

nvmが使用できるようにするには、

$ source ~/.nvm/nvm.sh

とする。これでnvmの準備は完了。…だが、ターミナル(コンソール)を閉じると、Nodeのバージョンはデフォルトに戻るし、ターミナルを起動するたびにsource...を実行しなければならない。これは面倒。

対策1:Nodeのデフォルトバージョンを変えるには

$ nvm alias default v0.11.11

とする。

対策2:source ...をターミナル起動時に実行するように、次を~/.zshenv, ~/.bash_profile, ~/.zshrc などに記載する。

if [[ -s /Users/aaaaaa/.nvm/nvm.sh ]] ; 
then source /Users/aaaaaa/.nvm/nvm.sh ; fi

ここでaaaaaaにはホームディレクトリを記述する。


Node.jsは複数のバージョンを切り替えて使うことができる。

現在インストールされているバージョンの確認は、'nvm ls'で行う。同時に現在使用中のバージョンも表示される。

$ nvm ls
  v0.10.25
  v0.10.26
  v0.11.11
current: 	v0.10.25

使用バージョンの切り替えは'nvm use 0.11'というように行う。

※日本ユーザーグループのサイトによると、現時点の最新版はv0.11.11だが、安定版はv0.10.25ということになっている。


Node.jsを使ってローカルに稼働するHTTPサーバーを作には、http-serverモジュールをインストールする。参考:http-server

このためには'npm (Node Package Manager)を使う。nvmと混同しないように!

XOOMS14-2:~ ay3844$ npm install http-server -g
  • gコマンドはグローバルにアクセスできるようにするオプション(らしい)。-gの実行は管理者権限か、sudoで実行する事が必要。

また、パッケージはバージョンごとにインストールが必要なことに注意。


後はサーバーのルートにしたいディレクトリに移動し、http-serverを起動するだけ。

XOOMS14-2:~ ay3844$ cd ~/Documents/_development/d3
XOOMS14-2:~ ay3844$ http-server


ブラウザから、http://localhost:8080/ でHTTPサーバにアクセスできる。

http://localhost:8080/project/index.html

のように、開きたいhtmlファイルを指定する。デフォルトポートは8080だが、http-serverの-pオプションでポート番号を変更できる。

http-serverは、Ctrl-Cで終了する。

ノード、セレクション、データバインド

ノードまたはエレメントとは、タグのペアで指定する要素
セレクションは、指定されたノードを操作するメソッドを持つ、配列のサブクラスである。

selectAll()と、function()は対応し、ループ処理と同等の処理が可能になる
処理内でデータ要素を使いたい場合は、function(d)
処理内でインデックスを使いたい場合は、function(d,i)
を使う

例1. すべてのp要素のフォントサイズを12ptにする

d3.select("#TEXT3").selectAll("p")
     .style("font-size", function() { return 12 + "px"; });

さらに、各ノードに対し、データ要素をバインドできる。これは、selectAll()の直後に、.data()を挿入する
例2. 各p要素のフォントサイズを、.data(配列)で指定した大きさにする

d3.select("#TEXT3").selectAll("p")
     .data([ 11, 13, 16, 20, 26, 32])
     .style("font-size", function(d) { return d + "px"; });

macのChromeでローカルファイルへのアクセスを可能にする

セキュレイティの制約から、現在のブラウザはローカルファイルへのアクセスを原則禁止している。が、起動時の設定によってローカルアクセスを可能にすることができる。
Chromeの場合は次を参考に設定すればよい。
【小ネタ】Chromeのローカルセキュリティポリシーの回避 | Developers.IO