JavaScript
d3.js
地図
geojson
Projection

D3.jsで日本地図を描くときの基本(geojson)

More than 1 year has passed since last update.

D3.jsで日本地図を描くときの基本(geojson)
D3.jsで埼玉県地図を描くときの基本(topojson)
D3.jsで埼玉県の地図上に市町村ラベルを描く

 D3.jsは便利で地図も簡単に描けますし、さらに高度なこともできます。ここでは基本的なことをまとめたいと思います。D3 v4で動作するソースコードを示します。サイト上にはD3 v3のものが多く、参考になるものが少なかったため誤りがあるかもしれませんがご了承ください。

今回作成したD3.jsプログラム
https://s3-ap-northeast-1.amazonaws.com/kuki-app-bucket/japanmap/japan.html

1.データの入手

 地図のデータは通常shapefileのフォーマットとして提供されていることが多いようです。それをWebに描画する時は、GeoJSONフォーマットに変換して使います。さらに容量を小さくしたいときはTopoJSON に変換しますが、ここではGeoJSONを使います。今回のGeoJSONデータでは、それぞれの都道府県に対する経度緯度のペアの配列を持っています。それに対してD3.jsのpathを生成し描画します。D3.jsで扱うdataセットは都道府県のデータの配列であることを覚えておいてください。

 以下に京都府のGeoJSONデータの一部を示します。これが各都道府県ごとに存在して配列にまとめられていて、さらに全体のヘッダーを付けて、ひとつのGeoJSONデータになっています。それを今回のD3.jsプログラムで描くと日本地図になるわけです。

        {
            "type": "Feature",
            "properties": {
                "nam": "Kyoto Fu",
                "nam_ja": "京都府",
                "id": 26
            },
            "geometry": {
                "type": "MultiPolygon",
                "coordinates": [
                    [
                        [
                            [
                                135.036697387695,
                                35.537334442138686
                            ],
                            [
                                135.035202026367,
                                35.53973388671878
                            ],

                            ---


                            [
                                135.47819519043003,
                                35.593200683593835
                            ]
                        ]
                    ],
                    [
                        [
                            [
                                135.33889770507798,
                                35.49359893798833
                            ],
                            ---
                            [
                                135.34719848632798,
                                35.495933532714844
                            ],
                            [
                                135.33889770507798,
                                35.49359893798833
                            ]
                        ]
                    ]
                ]
            }
        },

 実際のデータは以下のサイトから入手しました。
https://github.com/niiyz/JapanCityGeoJson

2.メルカトル図法

 地図は地球という球体の一部を平面図したものですから、なんらかの投影法(プロジェクション)が必要となります。メルカトル図法はgoogleマップやほとんどのWebマップで使われているもので、ここでもそれを指定します。プロジェクションを必要とする点が、他の折れ線グラフ等のグラフのプログラムとは違ってきます。逆に言えば、それ以外は、地図を描くのと折れ線グラフを描くのとはほぼ同じです。

3.D3.jsのプログラム

 以下に全ソースコードを示し、続いて技術的なコメントを付けて行きます。

<!doctype html>
<html>
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.12.0/d3.min.js" type="text/JavaScript"></script>
  </head>
  <body>
    <svg></svg>
    <script>
      var width = 600,
          height = 600;
      var scale = 1600;
      d3.json("./japan.geojson", createMap);

      function createMap(japan) {
        var aProjection = d3.geoMercator()
            .center([ 136.0, 35.6 ])
            .translate([width/2, height/2])
            .scale(scale);
        var geoPath = d3.geoPath().projection(aProjection);
        var svg = d3.select("svg").attr("width",width).attr("height",height);

        //マップ描画
        var map = svg.selectAll("path").data(japan.features)
          .enter()
          .append("path")
            .attr("d", geoPath)
            .style("stroke", "#ffffff")
            .style("stroke-width", 0.1)
            .style("fill", "#5EAFC6");

        //ズームイベント設定    
        var zoom = d3.zoom().on('zoom', function(){
            aProjection.scale(scale * d3.event.transform.k);
            map.attr('d', geoPath);
        });
        svg.call(zoom);

        //ドラッグイベント設定
        var drag = d3.drag().on('drag', function(){
            var tl = aProjection.translate();
            aProjection.translate([tl[0] + d3.event.dx, tl[1] + d3.event.dy]);
            map.attr('d', geoPath);
        });
        map.call(drag);
      }
    </script>
  </body>
</html>

 まずd3.geoMercator()ですが、これでメルカトル図法を指定します。center([ 136.0, 35.6 ])で日本の中心を経度緯度で指定します。この指定を忘れると日本地図は画面上に描かれないので注意してください。translate([width/2, height/2])で画面の中央を指定します。scale(scale)でデフォルトのスケールを指定します。これはズームイベントで変更可能となります。

        var aProjection = d3.geoMercator()
            .center([ 136.0, 35.6 ])
            .translate([width/2, height/2])
            .scale(scale);

 次にd3.geoPath()は地図グラフのpath generatorで、他の折れ線グラフのline generatorなどと同じです。SVG path データ文字列を生成します。上で指定したプロジェクションの設定を反映したものとなります。

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

 以上のような前準備をしたうえでマップを描画します。以下のコードになりますが、これは折れ線グラフ等の一般のグラフ描画とほぼ同じものです。ここでjapan.featuresは前に述べた都道府県のデータの配列となります。

        var map = svg.selectAll("path").data(japan.features)
          .enter()
          .append("path")
            .attr("d", geoPath)
            .style("stroke", "#ffffff")
            .style("stroke-width", 0.1)
            .style("fill", "#5EAFC6");

 最後にオマケですが、ズームとドラッグのイベントを簡単に追加することができます。この仕様はD3 v4で変更されています。サンプルプログラムを探してググるとD3 v3以前のものが多くヒットしますが、そのままでは動作しないのでアレ?とたじろいでしまいます。どちらもこんなに少ないコーディング量で済むとは、D3は便利ですね。

        //ズームイベント設定    
        var zoom = d3.zoom().on('zoom', function(){
            aProjection.scale(scale * d3.event.transform.k);
            map.attr('d', geoPath);
        });
        svg.call(zoom);

        //ドラッグイベント設定
        var drag = d3.drag().on('drag', function(){
            var tl = aProjection.translate();
            aProjection.translate([tl[0] + d3.event.dx, tl[1] + d3.event.dy]);
            map.attr('d', geoPath);
        });
        map.call(drag);