D3.jsで地図を描画

D3.jsとは、JavaScriptでグラフや図などを描画するためのライブラリです。

このライブラリの機能の1つとして、地図を描画することができるため、今回はそれを使って遊んでみようと思います。

 

https://d3js.org/

 

データを用意する

データはベクトル形式のものを用意します。

ベクトル形式のデータは国土交通省のものを使います。

 

国土数値情報 行政区域データ

http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03.html

 

ここから京都府の行政区分データを取得します。

 

必要なものは以下の3つのファイルです。

  • 拡張子がshp: 経度・緯度の情報が含まれる
  • 拡張子がdbf: 市町村名などの情報が含まれる
  • 拡張子がshx: 上の2つのデータの対応関係の情報が含まれる

これらのデータはD3.jsでは直接扱えないため、変換する必要があります。

GeoJSON形式に変換する

先程ダウンロードしたShape形式をGeoJSON形式に変換するため、QGISというソフトウェアを使うことができます。

https://www.qgis.org/ja/site/

 

今回はオンラインで変換できるmapshaperというサイトを利用しました。

http://mapshaper.org/

Shape file(拡張子がshp)をインポートすると、上のように内容が表示されます。

今回は市区町村名もつけたいので、dbxとshxもインポートします。

Exportメニューから「GeoJSON」を選択してExportします。

GeoJSONについて

(JSONの説明はこちらを参照)

 

GeoJSONとは、JSONを用いたファイルフォーマットで、簡単に言うと地図データのフォーマットです。

 

GeoJSONフォーマット仕様

http://s.kitazaki.name/docs/geojson-spec-ja.html

 

ファイルの内容はこんな感じになっています。

{ "type": "FeatureCollection",
  "features": [
    { "type": "Feature",
       "geometry": {
         "type": "Polygon",
         "coordinates": :[[[135.23313376,35.76943856], .....
       },
       "properties": {
         "N03_001":"京都府","N03_002":"","N03_003":"竹野郡","N03_004":"下宇川村"
       }
     ]
   }
   ...

"geometory"が、オブジェクトの種類と座標値です。

オブジェクトの種類は、点、線、多角形、またはそれらの集合です。

"properties"には任意の値が持てるようですが、今回は地名を保持するのに使っています。

描画処理の実装

D3.jsは、svgを使って描画されるため、svgの知識が必要になります。

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>

.area {
    fill: silver;
    stroke: white;
}
.popup {
    fill: black;
    font-size: 10pt;
}

</style>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="main.js"></script>
<title>test</title>
</head>
<body>
<svg id="map"></svg>
</body>
</html>

最低限のJavaScriptは以下のようになります。

(function(fn) {
    if (document.readyState !== 'loading'){
        fn();
    } else {
        document.addEventListener('DOMContentLoaded', fn);
    }
})(function() {
    var WIDTH = 1000, HEIGHT = 800;

    var svg = d3.selectAll("#map")
        .attr("width", WIDTH)
        .attr("height", HEIGHT);

    var g = svg.append("g");

    d3.json('kyoto.json').then(function(json) {
        var projection = d3.geoMercator()
            .scale(34000)
            .center(d3.geoCentroid(json))
            .translate([WIDTH / 2, HEIGHT / 2]);

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

        g.selectAll('path')
            .data(json.features)
            .enter()
            .append('path')
            .attr('d', path)
            .attr('class', 'area'));
    });
});

参考:APIリファレンス

https://github.com/d3/d3/blob/master/API.md

 

GeoJSON(kyoto.json)を非同期で読み込んで、GeoJSONのPolygonをsvgのpathに変換しています。

projectionというのが、緯度経度を画面上の座標に変換する関数です。

実行結果が以下になります。

インタラクティブな感じにしてみる

SVGのオブジェクトはJavaScriptから操作できるため、ユーザーの操作に応じて動かすことが比較的簡単に実装できます。

....
        g.selectAll('path')
            .data(json.features)
            .enter()
            .append('path')
            .attr('d', path)
            .attr('class', 'area')
            .on('mouseover', function() {
                this.style.fill = 'red';
            })
            .on('mouseout', function() {
                this.style.fill = 'silver';
            });
....

マウスカーソルが当たった市町村の色が赤になります。

地名を表示する

マウスカーソルが当たった場所の地名を表示してみます。

....
        var popup_text;

        g.selectAll('path')
            .data(json.features)
            .enter()
            .append('path')
            .attr('d', path)
            .attr('class', 'area')
            .attr('data-address', function(d) {
                var p = d.properties;
                return p.N03_003 + p.N03_004;
            })
            .attr('data-position', function(d) {
                var p = d3.geoCentroid(d);
                return p[0] + "," + p[1];
            })
            .on('mouseover', function() {
                this.style.fill = 'red';
                var t = popup_text;
                var p = projection(this.getAttribute('data-position').split(','));
                t.text(this.getAttribute('data-address'));
                t.attr('x', p[0]);
                t.attr('y', p[1]);
            })
            .on('mouseout', function() {
                this.style.fill = 'silver';
                popup_text.text('');
                t.attr('x', 0);
                t.attr('y', 0);
            });

        popup_text = g.append('text');
        popup_text.attr('class', 'popup');

data-address属性に地名を持たせて、data-position属性に中心座標を持たせています。

緯度経度から画面上の座標への変換は、projection関数で簡単に実現できます。

外部リンク

国土地理院 地球地図日本

http://www.gsi.go.jp/kankyochiri/gm_jpn.html

 

国土数値情報 ダウンロードサービス

http://nlftp.mlit.go.jp/ksj/index.html

 

D3 gallery

https://github.com/d3/d3/wiki/Gallery

 

Natural Earth

http://www.naturalearthdata.com/