TopoJSON v2, v3 チュートリアルその1

コマンドラインでshpファイルやGeoJSONファイルをTopoJSONファイルに変換するチュートリアルです。各コマンドの解説は[こちら][TopoJSONコマンド解説]へ。

※2017年4月12日追記
2017年4月に[TopoJSON v3]がリリースされました。このチュートリアルで使うコマンドラインに関してはv2とv3で差異はないので記事タイトルを「TopoJSON v2 チュートリアルその1」から「TopoJSON v2, v3 チュートリアルその1」に変更しました。

## 前提となる環境

- Node.js（というかnpmが使える環境）

## TopoJSONのインストール

```shell-session
  $ npm install -g topojson shapefile
```

npmで`topojson`と`shapefile`をインストールします。`-g`オプションをつけてグローバルにインストールします。

## データのダウンロード

チュートリアル用の作業ディレクトリを作り、その中に元のデータを格納するフォルダと、生成したデータを格納するフォルダを適当な名前で作成してください。
ここではとりあえず「data」と「dist」とします。

今回は国土交通省国土政策局国土情報課が提供する[国土数値情報]のデータを利用します。

[国土数値情報]ダウンロードサービスのホームページに行って、GML(JPGIS2.1)シェープファイル形式を選択し、2.政策区域 > [行政区域]のデータを選択してください。
とりあえず一つ、好きな都道府県を選択してください。
このチュートリアルでは茨城県の平成28年のデータを利用します。

>N03-160101_08_GML.zip	1.96MB

ダウンロードしたzipファイルを解凍し、先ほど作成したdataフォルダに入れます。
shpファイルの入ったフォルダは5.5MBです（茨城県平成28年の場合）。

>N03-160101_08_GML	5.5MB

```shell-session
  .
  ├── data
  │   └── N03-160101_08_GML
  │       ├── KS-META-N03-16_08_160101.xml
  │       ├── N03-16_08_160101.dbf
  │       ├── N03-16_08_160101.prj
  │       ├── N03-16_08_160101.shp
  │       ├── N03-16_08_160101.shx
  │       └── N03-16_08_160101.xml
  └── dist

```

## shpファイルをGeoJSONファイルに変換

さて、準備は整いました。まずは`shp2json`コマンドでshpファイルをGeoJSONファイルに変換してみましょう。

```shell-session
  $ shp2json --encoding "Shift-JIS" ./data/N03-160101_08_GML/N03-20160101.shp > ./dist/ibaraki_h28.geojson
```

国土数値情報の文字コードは**Shift-JIS**なので、オプションで```--encoding "Shift-JIS"```を忘れずに設定してください。

さて、distフォルダに茨城県の行政区域データのGeoJSONファイルが生成されたはずです。この時点ではGeoJSONファイルのサイズは3.4MBあります。

> ibaraki_h28.geojson 3.4MB

## GeoJSONファイルをTopoJSONファイルに変換

このGeoJSONファイルを`geo2topo`コマンドでTopoJSONファイルに変換します。

```shell-session
  $ geo2topo -q 1e6 ibaraki=./dist/ibaraki_h28.geojson > ./dist/ibaraki_h28.topojson
```

ibarakiはTopoJSONファイルの中のオブジェクト名です。ここではibarakiとしましたが任意の名称を設定してください。

> ibaraki_h28.topojson 745KB

[デモページ](https://observablehq.com/d/2d23d403b3399c51)

TopoJSONファイルをd3.jsで表示した例です。
詳しい説明は省きますが、ポリゴンとメッシュを表示しています。
各市町村のポリゴンにマウスを乗せると、マウスが乗ったポリゴンは水色に、隣接するポリゴンはシルバーに塗られます。

## shpファイルからTopoJSONに直接変換

GeoJSONファイルを作成せずに、パイプを使って直接shpファイルからTopoJSONファイルに変換することも可能です。

```shell-session
  $ shp2json --encoding "Shift-JIS" ./data/N03-160101_08_GML/N03-20160101.shp \
  | geo2topo -q 1e6 > ./dist/ibaraki_h28.topojson
```

このとき作成されたTopoJSONファイルでは、オブジェクト名が`"-"`になってしまいます。オブジェクト名が`"-"`になるのが嫌な場合は、こういう書き方で回避することができます。

```shell-session
  $ geo2topo -q 1e6 ibaraki=<( shp2json --encoding "Shift-JIS" ./data/N03-160101_08_GML/N03-20160101.shp ) \
  > ./dist/ibaraki_h28.topojson
```

#### `geo2topo`の`-q`オプションについて

`geo2topo`コマンドの`-q` `--quantization`オプションは非常に重要です。これを設定しなかった場合どうなるか、確認してみましょう。

##### ibaraki_h28_A.topojson

GeoJSONをTopoJSONに変換するときに量子化をおこなった例

```shell-session
  $ geo2topo -q 1e6 ibaraki=./dist/ibaraki_h28.geojson > ./dist/ibaraki_h28_A.topojson
```

##### ibaraki_h28_B.topojson

GeoJSONをTopoJSONに変換してから量子化をおこなった例

```shell-session
  $ geo2topo ibaraki=./dist/ibaraki_h28.geojson \
  | topoquantize 1e6 > ./dist/ibaraki_h28_B.topojson
```

[デモページ](https://observablehq.com/d/76a49e671b34feca)

2つのTopoJSONファイルの地物とメッシュ（後述します）を表示した地図です。一見どちらも変わらないように見えますが、変換の際に量子化をおこなっていないibaraki_h28_B.topojsonでは、茨城県の一番北西に位置する大子町のポリゴンが他のどのポリゴンとも隣接していません。元のGeoJSONファイルが、隣接する二つのポリゴンの座標の間に微小な誤差を持つ場合、適切な隣接判定をおこなってくれないのです。
これを防ぐために、`geo2topo`を実行する際には必ず`-q --quantization`オプションをつけましょう。


## TopoJSONを簡素化する

先ほど作成したTopoJSONファイルのサイズは745KBです。GeoJSONファイルが3.4MBだったことを考えると、これでも十分サイズは小さくなりましたが、`toposimplify`コマンドを使うことでTopoJSONのサイズをより小さくすることができます。

```shell-session
  $ toposimplify -P 0.05 -f ./dist/ibaraki_h28.topojson \
    > ./dist/ibaraki_h28_simplified.topojson
```

> ibaraki_h28_simplified.topojson 61KB

何とTopoJSONファイルは61KBになりました。最初のshpファイルが5.5MBあったので、おおよそ1/100になりました。
`-P 0.05`とすることでarcsの数を元のデータの約5%まで削減しています。（詳しくは[TopoJSONコマンド解説]の記事へ）
こんなに削って大丈夫なの？と思われる方！是非次のデモページをご覧ください！

[デモページ](https://observablehq.com/d/81e09262f0283b39)

このズームレベルでは、今までのものと違いをほとんど感じることはないと思います。
またTopoJSONを簡略化するということはパスを簡略化するということなので、パンやズームなど地図上の動作も非常に軽くなります。

データにどの程度の正確性を求めるかはケースバイケースなので、その辺りは考慮しながら閾値を設定する必要があります。元の行政区域データでは沖にある小島やヘッドランドもしっかり再現されていますが、今回のように全県を表示する場合はディテールにこだわる必要はありません。がっつり削ってしまいましょう。

先ほどの例ではGeoJSONからTopoJSONに変換したものを改めて簡略化しましたが、パイプを使ってコマンドを繋げることでshpファイルやGeoJSONファイルから直接簡略化されたTopoJSONファイルを作成することができます。

### GeoJSONから直接変換する場合

```shell-session
  $ geo2topo -q 1e6 ibaraki=./dist/ibaraki_h28.geojson \
  | toposimplify -P 0.05 -f > ./dist/ibaraki_h28_simplified.topojson
```

### shpファイルから直接変換する場合

```shell-session
  $ geo2topo -q 1e6 ibaraki=<( shp2json --encoding "Shift-JIS" ./data/N03-160101_08_GML/N03-20160101.shp ) \
  | toposimplify -P 0.05 -f > ./dist/ibaraki_h28_simplified.topojson
```


## TopoJSONの地物を結合する

一つの地物として扱いたい複数のポリゴン、例えば行政区域のデータで言うと飛び地や島嶼部を`topomerge`コマンドでマージするとひとまとまりの地物として扱うことができます。

```shell-session
  $ topomerge pref=ibaraki -k 'd.properties.N03_004' < ./dist/ibaraki_h28_simplified.topojson > ./dist/ibaraki_h28_merge.topojson
```

[デモページ](https://observablehq.com/d/a065ca6e63e715ce)

このデモページでは違いがあまりわからなかったかと思います。
次に表示するデモページでは`topomerge`をしたものとそうでないもの二つの地図を表示します。地図は境町にある坂東市の飛び地と、坂東市にある境町の飛び地付近にズームしています。
各ポリゴンにマウスを乗せたときに、周辺のポリゴンの色がどのように変化するか確認してください。

[デモページ](https://observablehq.com/d/1a975ab2d412bc95)

右側の市区町村ごとにマージをおこなった例では、飛び地のポリゴンにマウスオーバーしたときに本土のポリゴンも同様に水色に変化するはずです。このように、`topomerge`コマンドを使えば複数のポリゴンをひとまとまりの地物として扱うことができます。


コマンドラインでTopoJSONファイルを生成する際の基本形を国土数値情報のデータを使って実践してみました。

[TopoJSONコマンド解説]: http://qiita.com/cieloazul310/items/6dfd73952304ab61cd20
[国土数値情報]: http://nlftp.mlit.go.jp/ksj/
[行政区域]: http://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03-v2_3.html
[TopoJSON v3]: https://github.com/topojson/topojson/releases/tag/v3.0.0