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

はじめに
================================
最近になってd3jsを触り始めて楽しさを再認識しました。
世界地図をブラウザでぐりぐりするのが目標です。
開発環境はMacOSです。

最終目標までの道は遠いです。
QGISもnpmだってインストールしてゴリゴリ進めます。

この辺りを大分参考にさせていただきました。

http://www.tam-tam.co.jp/tipsnote/javascript/post6174.html
http://y-uti.hatenablog.jp/entry/2014/02/09/071953
http://qiita.com/sawamur@github/items/ec32237bcbaaba94108d
http://ja.d3js.node.ws/blocks/mike/map/
http://blog.hitsuji.me/make-topojson-file-and-show-map/


やること
================================
1. シェープファイルの取得
2. Shape→GeoJSON
3. GeoJSON→topoJSON
4. d3jsで地図を表示

使うもの
================================
* d3.js
* QJIS
* GDAL
* node.js

地図データの取得
================================
シェープファイルと言われる地図データを取得します。
ここを参考にさせていただきました。
http://y-uti.hatenablog.jp/entry/2014/02/09/071953

シェープファイルとは
--------------------------------------------
シェープファイルというのは、おおまかに言うと

* ポリゴンと点のようなエリアや地点を表すベクター情報のファイル(.shp)
* 国名、県名、番号、あるいは、トイレ、井戸というような属性データ(.dbf)
* それらの対応関係を示すファイル(.shx)

のセットでできている地図データの形式です。多分。

http://www.pasco.co.jp/recommend/word/word028/
http://www.esrij.com/getting-started/learn-more/shapefile/

NatualEarthからダウンロード
--------------------------------------------
シェープファイルはNaturalEarthというところから落としてきます。
こんなリッチなデータをフリーで使わせていただけるなんてすごすぎる。
http://www.naturalearthdata.com/

今回はグリグリするのが目的で、そこまで詳細な地図は必要ないかなという判断で
国境の世界地図のシェープファイルをダウンロードしました。
800KBのzipなのでダウンロードは一瞬。

```
Medium scale data, 1:50m > Caltural > Admin 0 – Countries > Download countries
```


落としてきたzipは解凍。

QGISのインストール
--------------------------------------------

[こちら](http://qiita.com/w_shon/items/979a97939ba4d2fd5c82)にYosemiteにQGISインストールするまでについての
記事を書きましたので参考になりましたら是非。

ちなみに、私もQGISで中身を見て気づきましたが
県境データだと思っていたこれはアメリカとオーストラリアとブラジルしか県境がありませんでした。

```
Medium scale data, 1:50m > Caltural > Admin 1 - States, provinces > Download states and province
```

QGISで見るとこんなかんじです。
![スクリーンショット 2015-08-11 14.46.13.png](https://qiita-image-store.s3.amazonaws.com/0/24210/28e3a8d5-8417-e957-dcf8-50120771d2ed.png)

紫芋っぽい色がAdmin 0 – Countries
ミントっぽい色がAdmin 1 - States, provinces
です。


Shape→GeoJSON
================================
GeoJSONとは？
--------------------------------------------
geojsonの例

```json
{ "type": "FeatureCollection",
  "features": [
    { "type": "Feature",
      "geometry": {"type": "Point", "coordinates": [102.0, 0.5]},
      "properties": {"prop0": "value0"}
      },
    { "type": "Feature",
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
          ]
        },
      "properties": {
        "prop0": "value0",
        "prop1": 0.0
        }
      },
    { "type": "Feature",
       "geometry": {
         "type": "Polygon",
         "coordinates": [
           [ [100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
             [100.0, 1.0], [100.0, 0.0] ]
           ]
       },
       "properties": {
         "prop0": "value0",
         "prop1": {"this": "that"}
         }
       }
     ]
   }
```

こんな形式で地理的データ構造をエンコードするためのフォーマットです。
多分色んな所で使えて便利なので位置情報クラスタの常識なんだと思います。

ちなみに、今回利用するd3.jsで地図を描画できるのはGeoJSON形式とTopoJSON形式です。

http://blog.qaramell.com/?p=8191

GDALのインストール
--------------------------------------------
Shape形式の地図データをGeoJSON形式に変換するためにogr2ogrというツールを利用します。 
ogr2ogrはGDALにバンドルされているのでGDALをインストールします。

このあたりに詳しく書いてあります。
http://qiita.com/sawamur@github/items/ec32237bcbaaba94108d
http://www.geopacific.org/opensourcegis/gdal-ogr/ogr-commands

### QGISインストール済みの方

GDALはQGISのインストールのために必要なので、すでに入っているかもしれません。
もし「すでに入れたはずなのにコマンドラインツールが見つからないよ！」という方がいらっしゃったら
パスが通ってないせいかもしれないので、以下を.bash_profileに追記してパスを通しましょう。

```bash
export PATH=/Library/Frameworks/GDAL.framework/Programs:$PATH
```

### QGIS未インストールの方

まだ入れていない場合は、今回はMacなのでbrewからGDALをインストールすると簡単かもしれません。

GDALをインストール

```bash
$ brew install gdal
```

これで完了。


ogr2ogrでGeoJSONに変換
--------------------------------------------

shp→geojsonに変換

```bash
$ cd ne_50m_admin_0_countries
$ ogr2ogr -f GeoJSON countries.geojson ne_50m_admin_0_countries.shp
```

GeoJSON形式にフォーマットを指定して変換するだけです。
今回利用したシェープファイルは小さかったので0.5秒くらいで終わります。

参考にさせていただいた[こちら](http://qiita.com/sawamur@github/items/ec32237bcbaaba94108d)には、
出力ファイルがsjisになるので気をつけて。
と書いてありましたが、私の手元ではutf8で出力されていました。ラッキー。


GeoJSON→topoJSON
================================

topoJSONとは？
--------------------------------------------

[こちら](http://ja.d3js.node.ws/blocks/mike/map/)で 「データの変換」 の章に以下の様な説明がありました。
GeoJSONをより小さく、効率的にするために、トポロジーをエンコードして拡張した。
という感じでしょうか。

> TopoJSON(※) は GeoJSON の拡張形式であり、トポロジーをエンコードしたものです。座標計算に固定精度エンコーディングを用いることにより TopoJSON ファイルのサイズは GeoJSON よりずっと小さくなります。今作っている地図は、GeoJSON では 536KB ですが、TopoJSON ではたったの 80KB、つまり 85% も削減されています（gzip圧縮後の比較でもずっと小さくなります）。単にディスク必要量を 削減するだけではなく、TopoJSON に含まれるトポロジー情報を使えば、境界線の自動計算や こうした面白いアプリケーションを作ることもできます。

topojson(npm)をインストール
--------------------------------------------
GeoJSON→topoJsonの変換にはnode.jsにバンドルされているtopoJSONのコンバータを利用します。
そのために、node.jsのパッケージ管理ツールであるnpmをインストールして、
npmからtopojsonをインストールします。

node.jsのパッケージ管理はnodebrewというのが流行っているらしいです。
ここを参考にさせていただきました。
http://qiita.com/ryosy383/items/2ea9c92a792302b159b0

nodebrewをインストール（すぐ終わった

```bash
$ brew install nodebrew
```

node.jsの最新版をインストール（結構かかった

```bash
$ nodebrew install latest
```

latest版を利用する設定

```bash
$ nodebrew use latest
```

nodeのパスを通す。
viで.bash_profileに以下を追記する。

```bash
export PATH=$PATH:$HOME/.nodebrew/current/bin
```

npmが使えるようになったか確認のためにヘルプを見る

```bash
$ npm help
```

これでnpmが使えるようになる!

最後にtopojsonのインストール

```bash
$ npm install -g topojson
```

topojsonがインストールできたか確認

```bash
$ which topojson
```

準備完了！


GeoJSON→topoJSON
--------------------------------------------

topojsonに変換（一瞬

```bash
$ cd ne_50m_admin_0_countries
$ topojson --id-property su_a3 -p NAME=name -p name -o worldmap.topojson countries.geojson 
```

これでokです。

topojsonはいくつかのgeoJSONを結合して1つのjsonにすることもできるので
最後にgeoJSONの引数を幾つか追加すると結合されたtopoJSONを取得できます。

今回は1つのgeoJSONから1つのtopoJSONを生成。


d3.jsで地図を表示
================================

やっときました！
ここからが本番

ここからは[このチュートリアル](http://ja.d3js.node.ws/blocks/mike/map/)が手腕をふるいます。
素晴らしいチュートリアル。
詳しいことはだいたい説明してくれているので端折ります。

index.htmlを配置
--------------------------------------------

いい感じのディレクトリを作成して、index.htmlを配置します。

```bash
$ mkdir worldmap
$ cd worldmap
```

index.html

```html

<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="utf-8">
		<title>world map</title>
		<style>

/* CSS goes here. */

		</style>
	</head>
	<body>
		hello world map
		<script src="http://d3js.org/d3.v3.min.js"></script>
		<script src="http://d3js.org/topojson.v0.min.js"></script>
		<script>

/* JavaScript goes here. */

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

```

ローカルでWebサーバを起動
--------------------------------------------
Webブラウザではローカルのjsonなどを利用するためにはwebサーバを起動する必要があります。
(詳しくはクロスドメインで検索)
http://blog.qaramell.com/?p=11971

ここではMacではお手軽で有名なpythonさんでワンライナーでローカルにWebサーバを起動します。

```bash
$ cd worldmap
$ phthon -m SimpleHTTPServer 8008 &
```

たったこれだけで、
ローカルホストにWebサーバが起動されて、さっき作った /wolrdmap/index.html を
ブラウザで確認できます！

```
http://localhost:8008
```

まだこれでは"hello world map"と表示されるだけですが、もうすこしのはず。

topoJSONを描画してみる
--------------------------------------------

index.htmlがあるディレクトリにdataディレクトリを作成して生成したtopojsonを配置しました。

```bash
$ cd worldmap
$ cd mkdir data
$ cp (さっき生成したところ)/ne_50m_admin_0_countries/worldmap.topojson ./data/
```

topojsonを配置したら
/* JavaScript goes here. */
のところにこんなかんじで書いてみます。

```js

var width = 1600;
	height = 900;

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


/* JavaScript goes here. */
d3.json("data/worldmap.topojson", function(error, worldmap) {
	svg.append("path")
      .datum(topojson.object(worldmap, worldmap.objects.countries))
      .attr("d", d3.geo.path().projection(d3.geo.mercator()));
});

```

これで更新してみるとこんな感じ！

![スクリーンショット 2015-08-11 15.37.09.png](https://qiita-image-store.s3.amazonaws.com/0/24210/92c14c36-ead7-7a77-2a71-0dc851d01c0d.png)


70度より北らへんがスパット切れちゃってるので、
場所を動かしたりスケールを変えたりしましょう。


国ごとのパスを分割する
--------------------------------------------

さっきの状態ではすべてのパスがひとつなぎになってしまっているので
国ごとに色を変えるみたいなことができません。

なので次のように変更します。
ついでにprojectionでスケールも変えてみました。

```js 
var width = 1600;
height = 900;

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


/* JavaScript goes here. */
d3.json("data/worldmap.topojson", function(error, worldmap) {

	// topojsonからgeojsonオブジェクトを生成
	var countries = topojson.object(worldmap, worldmap.objects.countries);

	// 投影方法
	var projection = d3.geo.mercator()
		.scale(150)
		.translate( [width / 2, height / 2] );

	// svgのパス要素
	var path = d3.geo.path()
		.projection( projection );

	// 各国のパスを作成
	svg.selectAll(".countries")
		.data( countries.geometries )
		.enter()
		.append("path")
		.attr("class", function(d) { return "country " + d.id; } )
		.attr("d", path)
		.style("fill", function(d){return rand_color();} );

});


function rand_color(){
	var r = Math.floor( Math.random() * 255 ).toString(16);
	var g = Math.floor( Math.random() * 255 ).toString(16);
	var b = Math.floor( Math.random() * 255 ).toString(16);

	return "#" + r + g + b;
}


```


こんなかんじにすると国のパスが1つづつに別れて便利になりました。

![スクリーンショット 2015-08-11 17.02.14.png](https://qiita-image-store.s3.amazonaws.com/0/24210/9e6b4204-b258-0637-b2c4-b73e33c40820.png)


--------------------------------------------