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