D3.jsで日本地図を描き、都道府県別に色を塗る

都道府県別のアクセス数みたいなものを、種類別に地図上で塗り分ける、というのをD3.jsでやってみようと思います。

ちなみにD3.jsはぜんぜん知らなかったので、とりあえず[基本のチュートリアル](http://ja.d3js.info/alignedleft/tutorials/d3/)と[地図のチュートリアル](http://ja.d3js.node.ws/blocks/mike/map/)を読みました。

# ツールのインストール

地図データの変換とかをするためのツールをインストールします。(Macです。)

```bash
$ brew install gdal
$ brew install node
$ npm install -g topojson
```

# 地図データのダウンロードと変換

[Natural Earth](http://www.naturalearthdata.com/)から都道府県レベルの地図データをダウンロードします。

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

解凍するとファイルが色々ありますが、ne_10m_admin_1_states_provinces.shp というファイルを指定して日本のデータのGeoJSONファイルを作ります。

```bash
$ ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" pref.json ne_10m_admin_1_states_provinces.shp
```

pref.jsonというファイルができました。
このファイルからTopoJSONファイルを作るのですが、なぜか静岡県の名前がnullになっているので、pref.jsonファイルを開いて、`"name":"Shizuoka"`になっている行の`"name_local":null`を`"name_local":"静岡県"`に修正しておきます。
(ちなみに1個目の`"name":null`になってる行もよくわからないけど消して良さそう。)

TopoJSONファイルを作ります。

```bash
$ topojson -p name -p name_local -p latitude -p longitude -o japan.json pref.json
```

japan.jsonというファイルができました。

# 表示データの準備

表示したい都道府県別のデータは、ID => 都道府県名の配列と、ID => アクセス数の配列の2つがPHPであるので、それぞれ使いやすい形にしてJSONにしておきます。

```php
<?php
$pref = array(1 => '北海道', 2 => '青森県', ... , 47 => '沖縄県');
$category = array(
        array(
                'name' => 'カテゴリー1',
                'data' => array(1 => 291, 2 => 19, ... , 47 => 3)
        ),
        ...
        array(
                'name' => 'カテゴリー2',
                'data' => array(1 => 111, 2 => 39, ... , 47 => 24)
        ),
);
?>
<script type="text/javascript">
var pref = <?php echo json_encode(array_flip($pref), JSON_UNESCAPED_UNICODE); ?>;
var category = <?php echo json_encode($category, JSON_UNESCAPED_UNICODE); ?>;
```

それぞれ、

```js
var pref = {"北海道":1,"青森県":2, ... ,"沖縄県":47};
var category = [{"name":"カテゴリー1","data":{"1":291,"2":19, ... , "47":3}},{"name":"カテゴリー2","data":{"1":111,"2":39, ... , "47":24}}];
```

のようになります。
地図データに漢字の都道府県名が入っているので、そこからIDを引けるようにしておきます。

# D3.js

まず地図を表示。

```js
var width = 900,
    height = 960;

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

var projection = d3.geo.mercator()
    .center([136, 35.5])
    .scale(2000)
    .translate([width / 2, height / 2]);

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

d3.json("japan.json", function(error, japan) {
    var topo = topojson.object(japan, japan.objects.pref).geometries;
    svg.selectAll(".pref")
        .data(topo)
        .enter()
        .append("path")
        .attr("class", function(d) {
            return "pref pref" + pref[d.properties.name_local];
        })
        .attr("d", path);
});
```

各都道府県のパスに`pref`というクラスと、都道府県別のIDが付いたクラス(北海道なら`pref1`沖縄なら`pref47`)を付けておきます。
cssで白地図にします。

```css
<style type="text/css">
.pref {
    fill: #fff;
    stroke: #aaa;
}
</style>
```

地図の横にカテゴリー名を表示して、そのテキストをマウスオーバーした時に、該当するカテゴリーの数値情報が地図上の色でわかるようにします。
数値が多い都道府県がより濃い赤になるようにしました。(255以上の値はみんな同じ色になりますが。)

```js
svg.selectAll("text")
    .data(category)
    .enter()
    .append("text")
    .attr("class", "category-label")
    .attr("x", width - 160)
    .attr("y", function(d, i) {
        return i * 20 + 220;
    })
    .text(function(d) {
        return d.name;
    })
    .on("mouseover", function(d) {
        for (var key in d.data) {
            var color = 255 - d.data[key];
            if (color < 0) {
                color = 0;
            }
            d3.select(".pref" + key)
                .transition()
                .style("fill", "rgb(255, " + color + ", " + color + ")");
        }
        d3.select(this).style("fill", "#f66");
    })
    .on("mouseout", function(d) {
        for (var i = 1; i < 48; i++) {
            d3.select(".pref" + i)
                .transition()
                .style("fill", "#fff");
        }
        d3.select(this).style("fill", "black");
    });
```

『カテゴリー2』のテキストの上にマウスを置くと、このようになります。
![スクリーンショット 2016-01-14 11.03.10.png](https://qiita-image-store.s3.amazonaws.com/0/75498/176ede23-4d46-cf53-fae6-2a66789b3a2d.png)

地図描くのもD3.jsを使えばそんなに難しくないな、と思いました。