講習
D3.jsの特徴
- データに基づいて、DOM(Document Object Model)を操作する、という考え方
- 各種チャート、グラフに必要な計算やデータ整形を賄ってくれる
- 表示はユーザーが自分で頑張って描く→自由!
- ウェブ標準のリソースのみを利用しているのでライブラリがなくなっても困らない
- ブラウザ上で動く(これ大事!)
- SVGかCanvasでいったらSVG( http://visualizing.jp/openvisconf2013-miguel/ )
- 最近ヴァージョン4.0がでた
- 各機能が小さいライブラリに分割された
CartoDBやMapboxなどのサービスとの違い
-
地形データを自分で用意する
- Leatlet.js(OpenStreetMap)やMapboxとの連携もできる
-
ユーザーインターフェイスも自分で用意する
-
描画の自由さ
-
たくさんの地図投影法が用意されている
- プラグインによる拡張( https://github.com/d3/d3-geo-projection/ )
どんなことができるのか
- ベルセロナへ乗り入れている飛行機の路線
- http://codefor.tokyo/misc/kb/Airport/barcelona.html
どんなことができるのか
- Results - London 2012 Olympics by The New York Times
- http://london2012.nytimes.com/results
どんなことができるのか
- EU内の人と使用言語の移動 2011年(多分)
- http://www.nzz.ch/inland-sommerserie-schweizer-karten-interaktiv/binnenwanderung-in-der-schweiz-1.18128893
どんなことができるのか
- モンテレーの殺人の密度マップ(Leaflet.js + D3.js)
- http://bl.ocks.org/diegovalle/5166482
どんなことができるのか
- 移民が送金したお金。ODAよりも移民送金額が高いことがわかる。
- http://www.tageswoche.ch/de/2013_19/international/540047/
どんなことができるのか
-
モノクロ画像から、カラースケールにもとづいて画像を生成(着色)
-
応用例
どんなことができるのか
-
離島をいい感じに矩形の中に収める
-
日本の場合…
実習
今日使う環境
blockbuilder
-
対象ブラウザ:Chrome もしくは Firefox
-
使ってなにが嬉しいか:サーバを立てなくて済む、Gistとの連動
操作の仕方(新しく作る場合)
- START CODINGをクリック
- 次の画面で、右上にある「Log In」ボタンを押し、ログインする(GitHubのアカウントが必要です)。
操作の仕方(すでにあるものを利用する場合)
- 下のURLの画面を表示する
- http://blockbuilder.org/n1n9-jp/e515f3df49957efc8693b746fdaf7bc8
- folkする
ファイル構成
-
プログラムファイル
- main.js
-
データファイル
- japan.json...日本の地形ファイル(TopoJson形式)
- population.tsv...人口データファイル(tsv形式)
- capital.tsv...都道府県庁データファイル(tsv形式)
-
いじらなくていいファイル
- index.html
- main.css
- README.md...メモ書き用ファイル(MarkDown形式)
操作の仕方
- コーディング領域でファイルを修正すると、表示領域に即座に反映される
- コーディング領域と表示領域、縦分割と横分割のどちらか選べる
- 保存したいときはSaveをクリック
- ファイルを追加したいときは右上の十字をクリック
今からやること
- 手順を一つづつみていきましょう。
- 知ってるよという方は、自由にいじってみてください。
- いじりすぎて動かなくなってしまったら、ブラウザの開発ツールを使ってデバッグするか、ソースコードをforkし直して位置からやり直すかしてみてください。
- Chrome DevTools( https://developers.google.com/web/tools/chrome-devtools/ )
- Firefox開発ツール( https://developer.mozilla.org/ja/docs/Tools )
表示領域の用意
var svgContainer = d3.select("#main").append("svg")
.attr("width", width).attr("height", height);
var mapContainer = svgContainer.append("g").attr("class", "japan");
var dataContainer = svgContainer.append("g").attr("class", "japan");
var legendContainer = svgContainer.append("g").attr("id", "legendBlock")
.attr('transform', 'translate(' + 800 + "," + 60 + ')')
.attr("width", 300).attr("height", 60);
/*
#main...HTMLに書いてあるid="main"(のdiv)を指定
.append("svg")...新しくsvgを発生させる
*/
外部ファイルの読み込み
- 複数ファイルの読み込みに挑戦。
- ネットにある他のサンプルは1ファイル読み込み想定のものが多い。今回はそれとは違うやり方。
<script src="http://d3js.org/queue.v1.min.js"></script>
<script>
queue()
.defer(d3.json, "japan.json")
.defer(d3.tsv, "population.tsv")
.defer(d3.tsv, "capital.tsv")
.await(mainFunc);
function mainFunc(_error, _topojson, _population, _capital) {
/*
括弧内はひとつめはエラー発生時エラーメッセージを受け取るためのもの
ふたつめ以降は defer() としてリストした順に、データを受け取れる
データは読み込み時に、オブジェクトの配列に変換される
外部ファイルのデータを受け取ったあとのコードをここに書く
*/
}
</script>
地図の表示のさせ方(投影法)の指定
/* 投影法の指定 */
//日本全体の場合
var projection = d3.geo.mercator()
.scale(1200)
.center([140.467551, 37.750299]);
//北海道のみの場合
var projection = d3.geo.mercator()
.scale(4000)
.center([143.635254, 43.704119]);
/* 地形データをSVGに変換するときに呼び出す */
var path = d3.geo.path().projection(projection);
/*
d3.geo.mercator()...メルカトル法を指定
.center([140.467551, 37.750299])...中心の座標を経度緯度の順で指定する
*/
地図の表示のさせ方(地図の描画)
var japanmap = svgContainer.append("g");
japanmap.selectAll("path")
.data(topojson.feature(_topojson, _topojson.objects.japan).features)
.enter()
.append("path")
.attr("id", function(d) {
return d.properties.nam_ja;
})
.attr("d", path)
.style("stroke", "#333")
.style("stroke-width", "0.2px")
.style("fill", "#FFF");
データと表現の連動
var rScale = d3.scale.linear()
.domain([0, 14000]).range(["#FFF", "#00F"]);
-
わかりづらいけど、覚えるしかない!
- domain...データのinput
- range...表現としてのoutput
統計データとの地形データ連携
- 統計データ自体に緯度経度が含まれているなら、コーディングの中で、地形データとの連携は考えなくてよい。
- 統計データ自体に緯度経度が含まれていない場合、共通のデータ項目を用いて、統計データとの地形データを連携させる必要がある。
地形データに含まれるデータの利用
- TopoJSONファイルの場合、data.objects.hokkaido.geometries
[i].propertiesに属性データが収納されているので、IDとして付与する、地図を着色するなど、適宜活用する。
.attr("id", function(d) {
return d.properties.nam_ja;
})
凡例をつくる
- d3 SVG Legendというライブラリがある
- http://d3-legend.susielu.com/#color-linear-custom
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.10.0/d3-legend.js"></script>
var rScale = d3.scale.linear().domain([0, 14000]).range(["#FFF", "#00F"]);
var legendObj = d3.legend.color()
.shapeWidth(15).shapeHeight(15)
.labelFormat(d3.format(".0f"))
.cells([0, 2000, 4000, 6000, 8000, 10000, 12000, 14000])
.orient('vertical')
.scale(rScale);
legendContainer.call(legendObj);
/*
rScale...インプットとアウトプットの組み合わせ(スケール)を指定
shapeWidth/shapeHeight...四角の大きさ(横幅、縦幅)
cells...凡例として表示したいデータ値
orient...向き(horizontal or vertical)
*/
ユーザーインターフェイスをつくる(今回はプルダウン)
/* 変数を準備 */
var menu;
var indexYear = -1;
var yearArray = new Array();
/* プルダウン初期化 */
function initMenu() {
menu = d3.select("#menuBlock select").on("change", changeYear);
menu.selectAll("option")
.data(yearArray)
.enter().append("option")
.attr("value", function(d, i) { return i; })
.text(function(d) { return d+"年"; });
}
ユーザーインターフェイスと地図表現の連動(japan.jsonとpopulation.tsv)
/* プルダウン変更時の挙動 */
function changeYear() {
/* プルダウンで選んだのが何番目か */
if (indexYear != -1) {
indexYear = parseInt( menu.property("value") );
} else {
indexYear = 0;
};
/* プルダウンで番目の年のみのデータを抽出 */
selectedPopulation = _population.filter( function(d) { return d.date == yearArray[indexYear] });
/* 地図を更新 */
renderMap();
}
function renderMap() {
/* 一部略 */
//プルダウンの変更がある度に実行
mapContainer.selectAll("path")
.style("fill", function(d, i) {
//変化させる前の色
return rScale( parseInt(_prev[i]) )
})
.transition()
.duration(2000)
.style("fill", function(d, i) {
//変化させた後の色
var _value = selectedPopulation[0][ d.properties.nam_ja ];
_prev[i] = _value;
return rScale( parseInt(_value) )
});
};
データの描画(capital.tsv)
function renderCircle() {
dataContainer.selectAll("circle")
.data(_capital)
.enter()
.append('circle')
.attr('class', "pref")
.attr('id', function(d){
return d.nam_ja;
})
.attr('cx', function(d){
console.log(d);
return projection([d.lon, d.lat])[0];
})
.attr('cy', function(d){
return projection([d.lon, d.lat])[1];
})
.attr('r', 2)
.style('fill', function(d){
return "#000";
});
}
カスタマイズしてみよう!
- 地形や丸の色を変えてみよう
- 丸を画像に変えてみよう
- データと連動する色を変えてみよう
- 違うデータファイルを読み込んでみよう
- (違う地図投影法を試してみよう)
丸を画像に変える
-
blockbuilderに画像をアップロードすることはできないので、使用したい画像をどこかのサーバにあげてからURLを.attr("xlink:href")に指定します。
-
function renderCircle() の中身を下記のソースコードへ丸っと差し替える。
dataContainer.selectAll(".chara")
.data(_capital)
.enter()
.append('image')
.attr('class', "chara")
.attr('id', function(d){
return d.nam_ja;
})
.attr("xlink:href", "aprilfool_piert_face.png")
.attr('x', function(d){
console.log(d);
return projection([d.lon, d.lat])[0];
})
.attr('y', function(d){
return projection([d.lon, d.lat])[1];
})
.attr("width", 20)
.attr("height", 16);
補習
地図の用意の仕方
- どこかから地形ファイルを手に入れる(たいていShapefile形式)
- コマンドラインツールでブラウザ上でみえる形式へ変換する
- ShapefileからGeoJSON...GDAL http://www.gdal.org/
- GeoJSONからTopoJSON...topojson https://github.com/mbostock/topojson
- 地形ファイルに含まれる属性データを整形・編集したかったり、別な属性データを埋め込みたい場合はQGIS( http://www.qgis.org/ja/site/ )というアプリケーションを利用する
地形ファイルの在り処
-
国土数値情報(日本限定、ライセンスに注意)
-
Natural Earth(世界全体、ライセンスフリー。ただし日本国土の国境の扱いが日本政府の方針と異なるケースがある)
-http://www.naturalearthdata.com/
TopoJSONとは
- GeoJSON( http://geojson.org/ )は、JSONを基にした、地理空間データ交換フォーマットです。オープンな標準規格。
- TopoJSONはD3.js作者による独自規格。
-
40分の1のサイズに!
-
メリットはファイルサイズ軽量化。
- ジオメトリの位置を相対値に変換。
- 境界線を二重に持たない。
内部構造
- GeoJSONの内部構造
- TopoJSONの内部構造
topojson.feature(topology, object).features
/*
topology...読み込んだtopojsonファイルのデータ全体を指定(今回の例では、_topojson)
object...読み込んだ地形データからGeometryCollectionへのパスを指定(今回の例では、_topojson.objects.hokkaido。最後のhokkaidoにあたる部分は、読み込むtopojsonファイルごとに違うと思ったほうがいい)
d3.jsでは、配列になっているGeometryCollectionを、自動的に順番に読み込むので、それを描画する。
_topojson.objects.hokkaido.geometries
[i].propertiesに、属性データが収納されているので、IDとして付与する、地図を着色するなど、適宜活用する。
*/
TopoJSONファイルの作り方
-
ShapefileはいったんGeoJSONに変換し、GeoJSONからTopoJSONへ変換する
-
ShapefileからGeoJSONへ変換
ogr2ogr -f geoJSON (出力ファイル名) (入力ファイル名)
- GeoJSONからTopoJSONへ変換
topojson -o (出力ファイル名) (入力ファイル名) -p
便利ツール
- ブラウザ上でGeoJSONを編集 http://geojson.io/
- JavaScriptソースコードをきれいに整形...beauitfy
- JavaScriptソースコードをミニマムに整形... uglify
ブラウザ上で動くツールもあるが( http://jscompress.com/ http://jsbeautifier.org/ )ファイルサイズが大きすぎると動作しないので、エディタアプリでの作業がいいかも。