Site icon Tips Note by TAM

D3.jsで日本地図を作成しデータを反映するサンプル
(コロプレス地図)

前回の記事「D3.jsの使い方とグラフを作成するサンプル」に引き続き、今回もD3.jsを使用します。

今回はD3.jsを使用してブラウザ上に日本地図を作成し、
CSVファイルに記載された果物の都道府県別出荷量のデータを反映してみます。

サンプルデモ(平成25年産 主要果樹都道府県別出荷量)
 
 
まずは必要なデータを準備します。

[準備1] 地図データの取得(日本地図の座標データ / JSONファイル)

日本地図は座標データを元にSVGの要素を使用してパスを作成します。
ひとつひとつ座標データを設定するのは大変なので、フリーの地理データを使用します。

フリーの地理データは下記のサイトで取得できます。

Natural Earth

このサイトで取得できるデータは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() という一連の流れがわかりにくいのですが、下記のページで詳細に解説されています。

d3.js - 三つの小円

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
農林水産省/リンクについて・著作権
・記事中で紹介しているサイト