前回の記事「D3.jsの使い方とグラフを作成するサンプル」に引き続き、今回もD3.jsを使用します。
今回はD3.jsを使用してブラウザ上に日本地図を作成し、
CSVファイルに記載された果物の都道府県別出荷量のデータを反映してみます。
⇒サンプルデモ(平成25年産 主要果樹都道府県別出荷量)
まずは必要なデータを準備します。
[準備1] 地図データの取得(日本地図の座標データ / JSONファイル)
日本地図は座標データを元にSVGの要素を使用してパスを作成します。
ひとつひとつ座標データを設定するのは大変なので、フリーの地理データを使用します。
フリーの地理データは下記のサイトで取得できます。
このサイトで取得できるデータはShape形式なので、
これをブラウザ上で使用するために、Shape形式→GeoJSON→TopoJSONの順番で変換します。
GeoJSONとTopoJSONの違い、それぞれの形式への変換や、最終的に使用する日本地図のjsonファイルの作成手順は、下記のサイトに詳しく書かれています。
●D3.js と TopoJSON で地図を作る
●D3.js で日本地図を描く - y_uti のブログ
[準備2] 反映対象データの取得(都道府県別果樹生産出荷統計 / CSVファイル)
日本地図に反映するデータを取得します。
農林水産省が公開しているデータを元にして、都道府県と出荷量を記載したCSVファイルを作成しました。
↓ CSVファイルの中身
state,value 北海道,7130 青森県,371600 岩手県,36500 宮城県,2980 秋田県,21400 山形県,40500 福島県,23500 茨城県,0 栃木県,0 ...
次に、サンプルのソースをざっと説明します。
1. 地図を表示するSVG要素を作成
最初に地図を表示するためにSVG要素を作成します。
作成したSVG要素には表示領域となる幅と高さを設定します。
// SVG要素作成 var svg = $body .append("svg") .attr({ 'width': width, 'height': height });
2. 投影法を指定してパスデータを作成
D3.jsは地図データを様々な地図の投影法に変換できます。
// 投影法の指定 var projectionOption = d3.geo.mercator() .center([137, 35]) // 中心の座標を指定 .scale(1800) // スケール(ズーム)の指定 .translate([width / 2, height / 2]); // 移動する var projection = d3.geo.path().projection(projectionOption);
3. 階級を区別するための色の範囲を指定
コロプレス地図では、データを範囲ごとの階級に区分します。
階級ごとに使用色や明暗を変更しますので、使用する色の範囲を指定しておきます。
d3.scale.quantize()を使用することで、どのようなデータを読み込んでも、指定した色の範囲に収まるようにスケールされます。
// 色の範囲を指定 var color = d3.scale.quantize() .range([ "rgb(191,223,255)", "rgb(153,204,255)", "rgb(115,185,253)", "rgb(77,166,255)", "rgb(38,147,255)", "rgb(0,128,255)", "rgb(0,109,217)", "rgb(0,89,178)", "rgb(0,70,140)", "rgb(0,51,102)" ]);
4. 地図の描画用関数
地図の描画は描画用の関数draw(str)を作成し、
読み込むCSVを変更するごとにこの関数を呼び出しています。
draw(str)の引数に、CSVファイルの拡張子を除いたファイル名を指定すると、
そのCSVファイルを読み込んで描画を開始します。
function draw(str) { //(省略) }
5. CSVデータの読み込み
csv()を使用してCSVデータを読み込みます。
読み込み後の処理は第二引数のコールバック関数に記載します。
d3.csv("js/" + str + ".csv", function (data){ //(省略) });
6. 色の定義域の指定
CSVのデータから最小値と最大値を取得して、
「3. 階級を区別するための色の範囲を指定」で指定した色の範囲にスケールさせます。
color.domain([ d3.min(data, function (d) { return Number(d.value); }), d3.max(data, function (d) { return Number(d.value); }) ]);
7. 地図データの読み込み
d3.json()を使用して地図データ(JSONファイル)を読み込みます。
読み込み後の処理は第二引数のコールバック関数に記載します。
d3.json("js/japan.json", function (jpn) { //(省略) });
8. JSONデータとCSVデータの連携
JSONデータとCSVデータが両方読み込まれましたので、
JSONデータ内で都道府県を表すstate要素に、CSVデータの対応する都道府県のデータを連携します。
// JSONの座標データとCSVデータを連携 for (var i = 0; i < data.length; i++) { var dataState = data[i].state; var dataValue = parseFloat(data[i].value); for (var j = 0; j < jpn.features.length; j++) { var jsonState = jpn.features[j].properties.name_local; if (dataState == jsonState) { jpn.features[j].properties.value = dataValue; break; } } }
9. JSONデータをHTMLの要素と連携させてブラウザに描画
CSVと連携したJSONデータをブラウザに描画するために、
HTMLのPATH要素と連携します。
D3.jsの selectAll() → data() → enter() → append() という一連の流れがわかりにくいのですが、下記のページで詳細に解説されています。
attr()やstyle()で見た目を調整します。
duration()を使用してCSV読み込み時にアニメーションで値(色)が切り替わるようにします。
on()を使用して地図上の各要素(都道府県)にマウスイベントが発生した時の処理を追加しています。
コロプレス地図だけでは具体的な数値がわかりませんので、マウスオーバー時に出荷量が表示されるようにしています。
// HTMLの要素とJSONデータを連携(初回はPATH要素が無いのでenterセレクションに保管される) var map = svg.selectAll("path") .data(jpn.features); if (init) { map.enter() // enterセレクションに保管 .append("path") // PATH要素の不足分を作成 .attr({ 'stroke': '#333', 'stroke-width': '0.5', 'd': projection }) .style("fill", '#FFF4D5') .on("mouseover", function (d) { return $tooltip .style("visibility", "visible") .text(d.properties.name_local + "の出荷量:約" + d.properties.value + "トン"); }) .on("mousemove", function (d) { return $tooltip .style("top", (event.pageY - 20) + "px") .style("left", (event.pageX + 10) + "px"); }) .on("mouseout", function (d) { return $tooltip .style("visibility", "hidden"); }); init = false; } map.transition() .duration(400) .style("fill", function (d) { $loading.style('display', 'none'); var value = d.properties.value; if (value) { return color(value); } else { return "#FFF4D5"; } })
一部の処理は地図の初回表示時のみに必要な処理なので、2回目以降は処理しないようにしています。
10. ボタンにクリックイベントを追加
画面左上のボタンをクリックすると、別のCSVが読み込まれます。
CSVの読み込みとその後の処理はdraw()にまとめています。
// クリックイベント追加 $item.each( function(d, i) { var $this = d3.select(this); $this.on("click", function () { $item.select("a").classed('on' ,false); $this.select("a").classed('on', true); draw($this.attr("class")); }); });
参考
・API Reference · mbostock/d3 Wiki · GitHub
・jQueryのあれ、D3.jsでどうやるの?(または、その逆) - Qiita
・農林水産省/リンクについて・著作権
・記事中で紹介しているサイト