d3.js
地図
topojson

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

More than 1 year has passed since last update.

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

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

前提となる環境

  • Node.js(というかnpmが使える環境)

TopoJSONのインストール

  $ npm install -g topojson shapefile

npmでtopojsonshapefileをインストールします。-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

  .
  ├── 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ファイルに変換してみましょう。

  $ 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ファイルに変換します。

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

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

ibaraki_h28.topojson 745KB

デモページ

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

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

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

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

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

  $ 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に変換するときに量子化をおこなった例

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

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

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

デモページ

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

TopoJSONを簡素化する

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

  $ 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コマンド解説の記事へ)
こんなに削って大丈夫なの?と思われる方!是非次のデモページをご覧ください!

デモページ

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

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

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

GeoJSONから直接変換する場合

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

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

  $ 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コマンドでマージするとひとまとまりの地物として扱うことができます。

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

デモページ

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

デモページ

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

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

最後に今回生成したTopoJSONファイルの中身を直接表示した乱暴なページを貼って終わります。

TopoJSONファイルの中身を確認するページ