Hatena::ブログ(Diary)

inutchの日記

2017-02-05

D3.jsを使って地図を作成(Windows版)

久しぶりにコードを書きたくなり、普段仕事がインフラ系なのでなんかビジュアル系のものがやりたくてD3.jsで地図を描くことにしました。Webのチュートリアルが古かったり、mac前提だったりして環境設定でいろいろはまったのでメモ。2017年2月5日時点でのWindows 10でのメモです。

エディタ+実行環境

Visual Studio Community 2015 にしました。JavaScriptでもインテリセンスが強力だし、F5を押したらIIS Expressを起動してくれるし。

「新規作成」>「Webサイト」>「ASP.NET空のWebサイト」でプロジェクト作成して、「追加」>「新しい項目の追加」>「HTMLページ」

その他ツール類の準備

各ツールの利用目的などの詳細は後で。

QGIS QGISプロジェクトへようこそ!

GISデータとして一般的なSHAPEファイルを表示するツール。GeoJSONとして保存もできますがそれは次のツールを利用。

GDALのWindowsビルド GISInternals Support Site

"Stable Releases" からアーキテクチャにあったものを選んで、一番上の全部まとめたzipをダウンロード。

使うのは、ogr2ogrコマンドでSHAPEファイルをGeoJSONに変換する機能だけ。

Node.jsの環境整備 Windows の Node.js 開発環境構築 最小手順 - Qiita

このサイトの通りにやりました。次のtopojsonのために必要です。

topojson

GoeJSONをTopoJSONに変換するツール。最新版(2.x系)のtopojsonにはtopojsonというコマンドは含まれず、同じ機能はgeo2topoコマンドになっている。geo2topoで作成したTopoJSONファイルは、Webで情報が豊富な"//d3js.org/topojson.v0.min.js"で読み込むとエラーになるようなので注意。なので、あえて古い1.6.27をインストール。

> npm install -g topojson@1.6.27

gオプションを忘れるとカレントディレクトリのプロジェクトにインストールしようとするので注意。

データの入手

国土数値情報(国土交通省) 国土数値情報ダウンロードサービス

日本のいろいろなデータがあります。ただ、簡単な日本地図を描きたいのに適したデータは見つからず。「行政区域」データが日本地図を描くのに使えそうですが、データがかなり大きいです(日本全国で235MB)。

どういうデータが提供されているかは、国土情報ウェブマッピングシステムで確認できます。

Natural Earth Downloads | Natural Earth

世界地図と様々なデータがあります。Scaleは一番細かい"Large scale data, 1:10m"でも上記の日本だけの行政区域よりサイズが小さいです。"Admin 1 – States, Provinces" > "Download states and provinces"をダウンロード(13.9MB)。

データの加工

SHAPE -> GeoJSON

入手したSHAPEファイルを、D3.jsで扱えるGeoJSONに変換します。日本のみにフィルタも。

> ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" ne_pref_japan_geo.json ne_10m_admin_1_states_provinces.shp
GeoJSON -> TopoJSON

GeoJSONを、効率化してサイズを小さくしたり機能追加したTopoJSONに変換。

> topojson -o ne_pref_japan_topo.json ne_pref_japan_geo.json

コーディング

GeoJSONの表示
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <title></title>
    <style>
    </style>
</head>
<body>
    <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script>
        var width = 800;
        var height = 800;

        //地図を描く先のsvg要素を準備
        var svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height);

        //JSONファイルを読み込み、コールバックで描画処理
        d3.json("ne_pref_japan_geo.json", function (error, data) {
            console.log(data);

            //緯度経度→画面座標の投影方法を指定。
            var projection = d3.geo.mercator()
                .center(d3.geo.centroid(data))
                .scale(1200)
                .translate([width / 2, height / 2]);

            //上記投影方法をオプションとして投影を行うfunctionを生成。
            //ここで生成したfunctionであるpathは、GeoJSONのデータがinput、
            //svgのpath要素のd属性に指定するパスコマンドがoutput。
            var path = d3.geo.path()
                .projection(projection);

            //描画処理本体。
            //D3.jsの機能を使いGeoJSONのデータ個数に合わせてpath要素を生成、
            //path要素に対してGeoJSONのデータを紐付ける。(appendまで)
            //最後のattrが肝。第二引数のpathは、上で作成したfunction。
            //path functionは、path要素に紐付けられたGeoJSONデータを入力とし、
            //projectionを踏まえて画面座標に投影したsvgのパスコマンドを出力し、
            //それがd属性にセットされる。
            svg.selectAll("path")
                .data(data.features)
                .enter()
                .append("path")
                .attr("d", path);
        });

    </script>
</body>
</html>

f:id:inutch:20170208220659p:image

sample

TopoJSONの表示
<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <meta charset="utf-8" />
    <title></title>
    <style>
    </style>
</head>
<body>
    <script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="//d3js.org/topojson.v0.min.js" charset="utf-8"></script>
    <script>
        var width = 800;
        var height = 800;

        var svg = d3.select("body")
            .append("svg")
            .attr("width", width)
            .attr("height", height);

        d3.json("ne_pref_japan_topo.json", function (error, topo) {
            console.log(topo);

            //読み込んだTopoJSONをGeoJSONに変換
            //objectsの次の要素(ne_pref_japan_geo)は、変換元のGeoJSONのファイル名。
            var data = topojson.object(topo, topo.objects.ne_pref_japan_geo);

            var projection = d3.geo.mercator()
                .center(d3.geo.centroid(data))
                .scale(1200)
                .translate([width / 2, height / 2]);

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

            svg.selectAll("path")
                .data(data.geometries)  //GeoJSONの時と変わるので注意。
                .enter()
                .append("path")
                .attr("d", path);
        });

    </script>
</body>
</html>

sample

コードの理解のために

D3.jsの仕様とSVGの仕様は理解する必要があります。これらのサイトでまず概要理解しました。

D3.js

D3.js の Data-Driven な DOM 操作がおもしろい - てっく煮ブログ

d3.js チュートリアルまとめ(前半) (D3.js入門(Data-Driven Documents)) | OpenBook

SVG

svg要素の基本的な使い方まとめ

参考

D3.jsで画像を使わず日本地図をブラウザに表示

D3で世界地図を描画してみる - Qiita

D3.jsとOpen Data〜その1地図を描画する - Qiita

Let’s Make a Map

次やること

使っているライブラリのバージョンが低いので、まずは最新に追従しようと思います。

D3 API Reference

その後、地図にいろいろ書き込んだり、インタラクティブにしたり。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/inutch/20170205/1486312058