D3.jsとは、JavaScriptでグラフや図などを描画するためのライブラリです。
このライブラリの機能の1つとして、地図を描画することができるため、今回はそれを使って遊んでみようと思います。
データを用意する
データはベクトル形式のものを用意します。
ベクトル形式のデータは国土交通省のものを使います。
国土数値情報 行政区域データ
http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03.html
ここから京都府の行政区分データを取得します。
必要なものは以下の3つのファイルです。
- 拡張子がshp: 経度・緯度の情報が含まれる
- 拡張子がdbf: 市町村名などの情報が含まれる
- 拡張子がshx: 上の2つのデータの対応関係の情報が含まれる
これらのデータはD3.jsでは直接扱えないため、変換する必要があります。
GeoJSON形式に変換する
先程ダウンロードしたShape形式をGeoJSON形式に変換するため、QGISというソフトウェアを使うことができます。
今回はオンラインで変換できるmapshaperというサイトを利用しました。
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