D3.jsでヒートマップっぽいものを作ってみた

### 概要
地域データを扱うことが多いので，可視化の方法として[D3.js](https://d3js.org/)を勉強しました．
（可視化は手段なんで，RでもTablueauでも状況に合わせて使い分ければいいと思います）

D3.js（Data Driven Documents）は
> ウェブブラウザで動的コンテンツを描画するJavaScriptライブラリ（<a href="https://ja.wikipedia.org/wiki/D3.js">Wikipedia</a>より）

です．

[dotinstall](http://dotinstall.com/lessons/basic_d3js)をざっと見とくと理解が早いと思います．

今回は地図に「都道府県別 100万人あたりの常設映画館数」をプロットしてみました．

完成図はこちら．
![スクリーンショット 2017-02-19 21.16.18.png](https://qiita-image-store.s3.amazonaws.com/0/32524/e2814546-2929-59d4-02d6-441d48df68ab.png)
情報源：[e-Stat](http://www.e-stat.go.jp/SG1/estat/eStatTopPortal.do)
（東京が一番かと思いきや福岡が多いんですね．スクリーンの数とか，映画館の来場回数とか，DVDやテレビなど配給方法の割合とか色々気になりますが，今回は関係ないので触れません）

### 準備
## ツール
```bash
# ShapeかGeoJSONへの変換に利用する
$ brew install gdal

# GeoJSONからTopoJSONへの変換に利用する
$ npm install -g topojson

# 最新版のV2系では，Qiitaで使用されているtopojsonというコマンドが4つのコマンドに分割されている．
# V2ではgeo2topoが該当するが，今回はV1を指定してインストールする
$ npm install -g topojson@1.6.27

# kokoじゃなくてもいいんだけど
# 今回，外部ファイルからデータを読み込むんで，直接htmlファイルを開くだけだと外部ファイルを読み込めなず
# サーバにファイルを置いてアクセスする必要がある．kokoはそのための手段．
$ brew install node
$ npm install -g koko
```

## データ
[Natural Earth](http://www.naturalearthdata.com/)から都道府県レベルの地図データをダウンロードします．Shapeファイルなんで，GeoJSON -> TopoJSONと変換して使います．

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というShapeファイルが入っているので，pref.jsonというGeoJSONに変換する
$ ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" pref.json ne_10m_admin_1_states_provinces.shp

# GeoJSONからjapan.jsonというTopoJSONに変換する
$ topojson -p name -p name_local -p latitude -p longitude -o japan.json pref.json
```

### 実装
こんな感じです．
ちなみに2017/02/19時点でD3.jsの最新版はv4なんですが，アニメーションを設定しようとするとエラーになったので[https://d3js.org/d3.v3.min.js](https://d3js.org/d3.v3.min.js)を指定しています．

```html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title></title>
		<style type="text/css">
		.pref {
			fill: #fff;
			stroke: #aaa;
		}
		</style>
    </head>
    <body>
        <script src="https://d3js.org/d3.v3.min.js"></script>
        <script src="http://d3js.org/topojson.v0.min.js"></script>
        <script>
            var w = 1300;
            var h = 960;

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

            // 地図の投影図法を設定する．
            var projection = d3.geo.mercator()
                .center([136, 35.5])
                .scale(2000)
                .translate([w / 2, h / 2]);

            // GeoJSONからpath要素を作る．
            var path = d3.geo.path()
                .projection(projection);

            var pref = {
                    "1":"北海道",
                    "2":"青森県",
                    "3":"岩手県",
                    "4":"宮城県",
                    "5":"秋田県",
                    "6":"山形県",
                    "7":"福島県",
                    "8":"茨城県",
                    "9":"栃木県",
                    "10":"群馬県",
                    "11":"埼玉県",
                    "12":"千葉県",
                    "13":"東京都",
                    "14":"神奈川県",
                    "15":"新潟県",
                    "16":"富山県",
                    "17":"石川県",
                    "18":"福井県",
                    "19":"山梨県",
                    "20":"長野県",
                    "21":"岐阜県",
                    "22":"静岡県",
                    "23":"愛知県",
                    "24":"三重県",
                    "25":"滋賀県",
                    "26":"京都府",
                    "27":"大阪府",
                    "28":"兵庫県",
                    "29":"奈良県",
                    "30":"和歌山県",
                    "31":"鳥取県",
                    "32":"島根県",
                    "33":"岡山県",
                    "34":"広島県",
                    "35":"山口県",
                    "36":"徳島県",
                    "37":"香川県",
                    "38":"愛媛県",
                    "39":"高知県",
                    "40":"福岡県",
                    "41":"佐賀県",
                    "42":"長崎県",
                    "43":"熊本県",
                    "44":"大分県",
                    "45":"宮崎県",
                    "46":"鹿児島県",
                    "47":"沖縄県"
                    };

            // TopoJSONを読み込む．
            d3.json("japan.json", function(error, japan) {
                console.log(japan);
                var topo = topojson.object(japan, japan.objects.pref).geometries;
                svg.selectAll(".pref")
                    .data(topo)
                    .enter()
                    .append("path")
                    // キーとバリューを逆にしてしまったので回避策．
                    .attr("class", function(d) {
                        var id =  Object.keys(pref).filter((key) => {
                            return pref[key] === d.properties.name_local
                            });
                        return "pref pref" + id;
                    })
                    .attr("d", path);
            });    
    
            var category = [{"name":"常設映画館数（人口100万人当たり）（館）",
                "data":{"1":10.4,
                    "2":13.6,
                    "3":14.0,
                    "4":6.0,
                    "5":13.5,
                    "6":9.7,
                    "7":7.2,
                    "8":10.3,
                    "9":8.1,
                    "10":10.6,
                    "11":4.1,
                    "12":5.6,
                    "13":21.4,
                    "14":6.5,
                    "15":5.6,
                    "16":4.7,
                    "17":6.9,
                    "18":16.5,
                    "19":5.9,
                    "20":13.3,
                    "21":6.4,
                    "22":10.8,
                    "23":7.5,
                    "24":13.7,
                    "25":9.2,
                    "26":7.7,
                    "27":6.6,
                    "28":13.7,
                    "29":3.6,
                    "30":8.2,
                    "31":24.4,
                    "32":5.7,
                    "33":5.2,
                    "34":20.1,
                    "35":13.5,
                    "36":6.5,
                    "37":18.3,
                    "38":15.1,
                    "39":13.6,
                    "40":36.7,
                    "41":6.0,
                    "42":20.2,
                    "43":31.2,
                    "44":12.0,
                    "45":12.6,
                    "46":7.8,
                    "47":9.1}}];
            
            var max = 40;
            var hoge = Math.floor(255 / 40);

            // 右上にあった常設映画館数というテキストをマウスオーバーすると
            // 映画館数によって都道府県の色が濃くなるよう施す．
            svg.selectAll("text")
                .data(category)
                .enter()
                .append("text")
                .attr("class", "category-label")
                .attr("x", w - 360)
                .attr("y", function(d, i) {
                    return i * 20 + 200;
                })
                .text(function(d) {
                    return d.name;
                })
                .on("mouseover", function(d) {
                    for (var key in d.data) {
                        var color = 255 - hoge * d.data[key];
                        if (color < 0) {
                            color = 0;
                        }
                        d3.select(".pref" + key)
                            .transition()
                            .style("fill", "rgb(" + color + ", " + color + " ,255)");
                    }
                    d3.select(this).style("fill", "#66f");
                })
                .on("mouseout", function(d) {
                    for (var i = 1; i < 48; i++) {
                        d3.select(".pref" + i)
                            .transition()
                            .style("fill", "#fff");
                    }
                d3.select(this).style("fill", "gray");
                });
        </script>
    </body>
</html>
```

### 動作確認
```
$ koko -o
```

### 参考URL
* [D3.jsで日本地図を描き、都道府県別に色を塗る](http://qiita.com/ran/items/d88c5126362576be3291)
* [D3.jsを使って地図を作成(Windows版)](http://d.hatena.ne.jp/inutch/20170205/1486312058)
* [TopoJSONって何だろう？](http://blog.qaramell.com/hunzy/8661)
* [チュートリアル 「D3.jsとTopoJSONで地図を作る」を元に日本地図を作ってみる。](http://huruyosi.hatenablog.com/entry/%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB_%E3%80%8CD3.js%E3%81%A8TopoJSON%E3%81%A7%E5%9C%B0%E5%9B%B3%E3%82%92%E4%BD%9C%E3%82%8B%E3%80%8D%E3%82%92%E5%85%83%E3%81%AB%E6%97%A5%E6%9C%AC%E5%9C%B0)
* [D3.jsで画像を使わず日本地図をブラウザに表示](http://tactosh.com/2014/04/d3-js-japan-geo-map/)
* [D3.js チュートリアル： マップ – 日本地図を表示する](http://dataisfun.org/2014/06/02/?p=444)