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

最近話題のD3.jsを使ってみました。使ってすぐに、D3は単なるグラフ描画ツールではなくデータ駆動型の壮大なライブラリだったことに気づきました。緯度経度座標のさまざまな投影法(メルカトル図法など)に対応しているので、地図が簡単に書けるのが便利です。それで今回は日本地図を描いてみました。

今回描いた日本地図

d3_japan
D3.jsを用いて上のような日本地図を描いてみました。日本地図をプロットした後に、都道府県名を表示させました。各都道府県の色や文字サイズは適当に。

参考記事

以下の記事を参考にさせて頂きました。
D3.js で日本地図を描く
D3.jsとTopoJSONで地図を作る
2つ目はD3.jsの作者のMike Bostockさんによるチュートリアルの和訳です。

手順

  1. 各種変換ツールをインストール
  2. Shape形式の地図データを入手
  3. 地図データをGeoJSONに変換
  4. GeoJSONをTopoJSONに変換
  5. D3.jsで日本地図をプロット

D3.jsではJSONを用いて地図を描画します。GeoJSONには都道府県名、緯度経度、境界線のラインを数値化したデータなどで構成されています。TopoJSONは、GeoJSONの冗長性を排除しファイル容量を80%削減した、D3.jsのために作られたGeoJSONの拡張形式です。

①各種変換ツールをインストール

これらのツールはShape形式のデータをJSONに変換することが目的なので、ローカルにインストールするだけでOKです。

  1. gdal
    これに同梱されているogr2ogrコマンドにより、Shape ⇒ GeoJSONに変換します。

    brew install gdal
    

    インストールまでエラーが出て大変でしたがここでは割愛します。

  2. topojson
    topojsonコマンドによりGeoJSON ⇒ TopoJSON変換を行います。

    sudo npm install -g topojson
    

    node.jsのインストールはここでは割愛します。

各種コマンドがインストールされているか確認します。

$ which ogr2ogr                                                                                   
/usr/local/bin/ogr2ogr
$ which topojson
/usr/local/bin/topojson

②Shape形式の地図データを入手

Natural Earthに全世界の地図データが公開されています。地図の種類が何種類かありますがここでは上記参考記事と同じ物を利用します。

wget http://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/cultural/ne_10m_admin_1_states_provinces.zip
unzip ne_10m_admin_1_states_provinces.zip

③地図データをGeoJSONに変換

ogr2ogr -f GeoJSON -where 'geonunit = "Japan"' japan.geojson ne_10m_admin_1_states_provinces.shp

Shape形式の地図データをGeoJSONに変換します。fオプションには出力ファイルの種類を指定します。GeoJSONの他にもPDFなども指定できるようです。whereオプションでは絞り込みの設定をします。ここではgeounit=”Japan”と指定して、日本地図だけを抽出しています。これを指定しないと全世界のGeoJSONが出力されます。

④GeoJSONをTopoJSONに変換

topojson -p name -p name_local -p latitude -p longitude -o japan.topojson japan.geojson

GeoJSONをTopoJSONに変換します。ここでpオプションにname(都道府県名英語表記),name_local(都道府県名日本語表記),latitude(緯度),longitude(経度)を指定して、出力されるtopojsonファイルに取り込んでいます。

D3.jsで日本地図をプロット

ここからはD3.jsを使ったコーディングに入っていきます。
ファイル構成は以下のようになります。さきほど作成したjsonファイルはjsonフォルダに入れておきます。

$ tree
.
├── japan.html
├── js
│   └── japan.js
└── json
    └── japan.topojson

以下の手順で進めていきます。

  1. htmlファイル作成
  2. 簡易サーバ起動
  3. SVG生成
  4. TopoJSON読み込み
  5. 日本地図をプロット
  6. 地図のスタイルを変更する
  7. 都道府県名をプロット

①htmlファイル作成

htmlは以下のように、head内でd3.jsとtopojsonのモジュールを読み込みます。
そしてbody内で読み込んだjapan.jsにコードを書いていきます。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>D3: Japan TopoJSON</title>
    <script src="http://d3js.org/d3.v3.min.js"></script>
    <script src="http://d3js.org/topojson.v0.min.js"></script>
  </head>
  <body>
    <h1>D3.js Japan</h1>
    <script src="js/japan.js"></script>
  </body>
</html>

②簡易サーバ起動

D3.jsで今回のように外部ファイルからデータを読み込む場合は、直接htmlファイルを開くだけだと外部ファイルを読み込めないため、サーバにファイルを置いてアクセスする必要があります。ここでは簡単にpythonの簡易サーバを使います。japan.htmlと同じ階層に移動し、以下のコマンドでサーバを起動します。

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...

これでhttp://localhost:8000/japan.htmlにアクセスすればさきほどのhtmlが表示されます。

open http://localhost:8000/japan.html


まだjapan.jsには無いも書いていないのでh1が表示されるだけです。

③SVG生成

次にjapan.jsのコーディングに移ります。

var w = 1000;
var h = 1000;

// SVG要素生成
var svg = d3.select("body")
          .append("svg")
          .attr("width", w)
          .attr("height", h);

bodyにsvg要素を追加します。これが日本地図をプロットする土台となります。

④TopoJSON読み込み

次に先ほど作成したjapan.topojsonをD3.jsで読み込みます。以下のコードを追加します。

// 日本地図データ読み込み
d3.json("../json/japan.topojson", function(json) {
    var japan = topojson.object(json, json.objects.japan).geometries;
    console.log(japan);
});

d3.json()は非同期に実行されるので、ファイル読み込みに依存するコードは、これのコールバックの中に全部書きます。コンソールログを見ると都道府県ごとのオブジェクトの配列になっているのがわかります。さきほど指定した都道府県名、緯度経度などのプロパティもしっかり入っています。

⑤日本地図をプロット

d3.json()コールバック内にさらに追加します。

// 日本地図データ読み込み
d3.json("../json/japan.topojson", function(json) {
    var japan = topojson.object(json, json.objects.japan).geometries;

    // 投影法設定
    var projection = d3.geo.mercator()
        .center([137, 34])
        .translate([w/2, h/2])
        .scale(1500);

    // 緯度経度⇒パスデータ変換設定
    var path = d3.geo.path()
        .projection(projection);

    // パスデータとして日本地図描画
    svg.selectAll("path")
    .data(japan)
    .enter()
    .append("path")
    .attr("d", path);

});

projectionで緯度経度座標の投影法を設定します。ここではメルカトル図法にして、中心や縮尺の設定もしています。
次にpathで先ほどのprojectionを読み込みます。これのおかげで緯度経度のデータをピクセル座標にいい具合に変換してくれます。
最後にsvgにpath要素を追加して47都道府県をプロットします。これで都道府県の数だけpathが追加されます。

⑥地図のスタイルを変更する

以下のようにcolor変数を上の方で定義します。

var color = d3.scale.category10();

これにインデックスを渡せば、適当に色を振り分けてくれます。これで都道府県ごとの色を塗り分けます。
次にpath部分に以下のように追加します。

// パスデータとして日本地図描画
svg.selectAll("path")
.data(japan)
.enter()
.append("path")
.attr("d", path)
.attr("stroke", "black")
.attr("stroke-width", 0.5)
.style("fill", function(d, i) {
  return color(i);
});

これで色と枠線がつきました。

⑦都道府県名をプロット

都道府県名をtext要素としてd3.json()コールバック内に追加します。

// 都道府県名
svg.selectAll(".place-label")
.data(japan)
.enter()
.append("text")
.attr("font-size", "8px")
.attr("class", "place-label")
.attr("transform", function(d) {
  var lat = d.properties.latitude;
  var lng = d.properties.longitude;
  return "translate(" + projection([lng, lat]) + ")";
})
.attr("dx", "-1.5em")
.text(function(d) { return d.properties.name_local; });

上のようにコールバック関数の内部から各データのプロパティにアクセスして、都道府県名や緯度経度を取得できます。

projection([lng, lat])

の部分で緯度経度からピクセル座標を簡単に取得することができます。

完成!!

  • Shinichi Aoki

    大変参考になりました。
    ところでtext「静岡県」が表示されないのはなんででしょうか?

  • よしおかあきら

    お世話になります。このサイトでの日本地図作成はとても助かりました。

    静岡県が消えている原因は、japan.topojsonファイルに「静岡県」の表記がなかったことが原因で、もっと遡ると、wgetで持ってくる情報に入っていなかったのではないでしょうか。さきほどやっても再現性がありました。

    あとは、私は

    で動かしたましたが、エラーが発生して、以下のように書き換えました。

    var japan = topojson.feature(json, json.objects.japan).features;