smellman's Broken Diary

クソみたいなもんです

OpenStreetMapのMapbox Vector Tileを作ってみた

smellman.hatenablog.com

この記事にあるosm2vectortilesは古いため、Dockerのインストール方法以外は新しい記事を参考にしてください。

OpenStreetMap Advent Calendar 2016 3日目の記事です。

今回はOpenStreetMapのデータを使ってMapbox Vector Tileを自分で作ってみることに挑戦してみました。

対象となる環境はUbuntu 16.10をインストールしているThinkpad X220です。スペックは以下の通りです。

CPU
Intel(R) Core(TM) i5-2540M CPU @ 2.60GHz (物理2コア/論理4コア)
Memory
16GB
Storage
SSD 240GB

なお、このマシンは本体と換装パーツ込で33000円ぐらいで揃えたものです*1

さて、余談はよいとして、Mapbox Vector Tileを作るに当たって、今回はosm2vectortilesGenerate your own vector tilesを参考に行いました。

まず先にosm2vectortilesについて軽く紹介したいと思います。

このプロジェクトはOpenStreetMapをベースとしたMapbox Vector Tileの地図タイルを自由にダウンロードできるようにするもので、世界全域の地図タイル以外にもいろんな地域の地図タイルがmbtiles形式でホスティングされています。また、地図タイルの作成にdockerを活用しているのが特長です。また、地図タイルを長くやってる人にはMapTilerというプロダクトで有名なKlokan Technologiesがスポンサーをしていることも話題になりました。俺も久々にこのカンガルーの会社ロゴ見てびっくりした(汗

さて、さっそく作業をしていきます。まずはDockerとDocker-composeをインストールします。

sudo apt-get install docker docker-compose
sudo service docker start
sudo docker run hello-world

次に一般ユーザで作業をするためにdockerグループにユーザを追加します。

sudo usermod -aG docker $USER

これで一旦リブートをして再度ログインして、一般ユーザで動作するかを確認します。

docker run hello-world

dockerが一般ユーザで動くようになったら次にosm2vectortilesをgithubから落としてきます。

mkdir -p ~/develop/osm
cd develop/osm
git clone https://github.com/osm2vectortiles/osm2vectortiles.git
cd osm2vectortiles

今回の作業パスは ~/develop/osm/osm2vectortiles とします。

まずは手順通りにdocker-composeでPostGISのコンテナを取得します。

docker-compose up -d postgis

ここで一旦別のディレクトリを作成して、そこに今回使うファイルをダウンロードします。

まずはテストということで、bbbikeから東京の範囲のpolyファイルとPBFファイルをダウンロードします。polyファイルは後でBBOXの範囲を計算するのに使います。

mkdir -p ~/tmp/osmdata
cd ~/tmp/osmdata
wget http://download.bbbike.org/osm/bbbike/Tokyo/Tokyo.poly
wget http://download.bbbike.org/osm/bbbike/Tokyo/Tokyo.osm.pbf

次にPBFファイルをosm2vectortiles/importに配置します。

cp ~/tmp/osmdata/Tokyo.osm.pbf ~/develop/osm/osm2vectortiles/import/

次にまた別の作業ディレクトリを作成して、OpenStreetMapDataからwater-polygonsとsimplified-water-polygonsを、Natural Earthからnatural_earth_vectorSQLiteのファイルをダウンロードします*2

mkdir -p ~/tmp/osmpoly
cd ~/tmp/osmpoly
wget http://data.openstreetmapdata.com/water-polygons-split-3857.zip
wget http://data.openstreetmapdata.com/simplified-water-polygons-complete-3857.zip
wget http://naciscdn.org/naturalearth/packages/natural_earth_vector.sqlite.zip

これらを全てunzipしてから必要なファイルをosm2vectortiles/importにコピーします。

unzip water-polygons-split-3857.zip
unzip simplified-water-polygons-complete-3857.zip
unzip natural_earth_vector.sqlite.zip
cp ~/tmp/osmpoly/water-polygons-split-3857/water_polygons.* ~/develop/osm/osm2vectortiles/import/
cp ~/tmp/osmpoly/simplified-water-polygons-complete-3857/simplified_water_polygons.* ~/develop/osm/osm2vectortiles/import/
cp ~/tmp/osmpoly/packages/natural_earth_vector.sqlite ~/develop/osm/osm2vectortiles/import/

最終的にosm2vectortiles/importディレクトリが以下のようになります。

% ls -l import
合計 1079640
-rw-r--r-- 1 btm btm  18088604 12月  3 15:11 Tokyo.osm.pbf
-rw-r--r-- 1 btm btm 414519296 12月  3 15:16 natural_earth_vector.sqlite
-rw-r--r-- 1 btm btm         6 12月  3 15:16 simplified_water_polygons.cpg
-rw-r--r-- 1 btm btm     74837 12月  3 15:16 simplified_water_polygons.dbf
-rw-r--r-- 1 btm btm       853 12月  3 15:16 simplified_water_polygons.prj
-rw-r--r-- 1 btm btm  29614272 12月  3 15:16 simplified_water_polygons.shp
-rw-r--r-- 1 btm btm     49948 12月  3 15:16 simplified_water_polygons.shx
-rw-r--r-- 1 btm btm         6 12月  3 15:15 water_polygons.cpg
-rw-r--r-- 1 btm btm    452789 12月  3 15:15 water_polygons.dbf
-rw-r--r-- 1 btm btm       847 12月  3 15:15 water_polygons.prj
-rw-r--r-- 1 btm btm 642308932 12月  3 15:15 water_polygons.shp
-rw-r--r-- 1 btm btm    301916 12月  3 15:15 water_polygons.shx

では、またosm2vectortilesの作業ディレクトリに戻って手順書通りに作業を進めていきます。

docker-compose up import-external
docker-compose up import-osm
docker-compose up import-sql

ここまできたらあとはexportをするのみとなりますが、BBOX(範囲)のパラメータを与えないと世界中が対象となってしまうので、BBOXの値を計算します。
BBOX自体はwest,south,east,northの順にパラメータを与えます。
なので、経度180度線を気にしなければ、min(longitudes),min(latitudes),max(longitudes),max(latitudes)となります。
今回の東京の範囲のPOLYファイルをみると今回は単純な四角形なので値は簡単にわかります。

Tokyo
1
   139.62  35.56
   139.95  35.56
   139.95  35.78
   139.62  35.78
END
END

これを読めば、BBOX=139.62,35.56,139.95,35.78という値がとれるので、それを使ってドキュメントに沿ってexportを実行します。今回はzoom 9からzoom 14までを作成してみます。

docker-compose run \
  -e BBOX="139.62,35.56,139.95,35.78" \
  -e MIN_ZOOM="9" \
  -e MAX_ZOOM="14" \
  export
docker-compose up export

これらを実行するとexport以下にtiles.mbtilesというファイルができあがります*3。ただし、dockerで生成したためか所有者がrootのファイルになっているので、所有者の変更とあとでファイルを変更しても良いように別名でコピーをとっておきます。

sudo chown $USER:$USER ~/develop/osm/osm2vectortiles/export/tiles.mbtiles
cp ~/develop/osm/osm2vectortiles/export/tiles.mbtiles ~/develop/osm/osm2vectortiles/export/tokyo.mbtiles

あとは実際に動作するかをチェックします。
これはGetting Startedではtileserver-gl-lightを使うようになっているのですが、tileserver-gl-light自体はすでに古く、tileserver-glが最新になっているのでそちらを使うようにします。
ただし、tileserver-glの方は現在npm installに失敗してしまいます*4
なので、今回はtileserver-glのdockerを使って検証してみます。

cp ~/develop/osm/osm2vectortiles/export/tokyo.mbtiles /tmp/
cd /tmp
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl

これでサーバが動いているIPアドレスの 8080 port へアクセスします。

f:id:smellman:20161203192934p:plain

ここでBasicスタイルのVectorタイルを選択します。

f:id:smellman:20161203193118p:plain

初期状態でzoom 2なのですが、何も出ててきません。しかし、ズームアウトすると...

f:id:smellman:20161203193151p:plain

全球が出てきます。そこで東京の方に見ていきます

f:id:smellman:20161203193249p:plain

途中まではすごく雑です。

f:id:smellman:20161203193440p:plain

作成したズーム9からはちゃんと出てきます。

f:id:smellman:20161203193527p:plain

f:id:smellman:20161203193547p:plain

とりあえずズーム14までちゃんと作られたのが確認できます。

では、せっかくなのでズーム0〜ズーム8をマージしてみましょう。

マージはドキュメントにあるようにMapboxのmbutilにあるpatchというシェルスクリプトを使います。なお、sqlite3が入っている必要があります。また、ドキュメントにあるmbtilesのダウンロードはズームレベル5までのもの*5なので、ダウンロードページからPlanet from zoom level 0 to 8を選んでダウンロードします。

cd ~/develop/osm/osm2vectortiles/export
wget https://osm2vectortiles-downloads.os.zhdk.cloud.switch.ch/v2.0/planet_2016-06-20_7088ce06a738dcb3104c769adc11ac2c_z0-z8.mbtiles
wget https://raw.githubusercontent.com/mapbox/mbutil/master/patch
chmod +x patch
cp tokyo.mbtiles tokyo-and-planet.mbtiles
./patch planet_2016-06-20_7088ce06a738dcb3104c769adc11ac2c_z0-z8.mbtiles tokyo-and-planet.mbtiles

これも同様に動かしてみましょう。

rm /tmp/tokyo.mbtiles
cp ~/develop/osm/osm2vectortiles/export/tokyo-and-planet.mbtiles /tmp
cd /tmp
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl

f:id:smellman:20161203193638p:plain

最初から全球がレンダリングされていていい感じです。

f:id:smellman:20161203193710p:plain

f:id:smellman:20161203193730p:plain

日本の形もちゃんと出てきます。

f:id:smellman:20161203193800p:plain

ただし、zoom 8あたりで残念なマージになりました。
まぁ、ファイルを上書きしてしまってるのでしょうがないとはいえます。

とりあえずこれである程度の地図タイルが作れるようになりました。

では、せっかくなので日本全国の地図タイルを作ってみましょう。

まずはGeofebrikのOSMデータのミラーサイトから日本全体のデータをダウンロードします。

cd ~/tmp/osmdata
wget http://download.geofabrik.de/asia/japan.poly
wget http://download.geofabrik.de/asia/japan-161202.osm.pbf

次に、以前の作業に使ったpbfファイルの削除と、以前使ったdocker-composeを全て削除します。
docker-composeを削除するのはどうもpgdataを削除したぐらいでは何かが残ってしまっていてimposm3のmergeの処理が走ってしまうためです。
ただ、すでにイメージ自体はダウンロードしてるので再構築自体は高速です。

cd ~/develop/osm/osm2vectortiles
rm import/Tokyo.osm.pbf
docker-compose stop postgis
docker-compose rm -f export
docker-compose rm -f import-sql
docker-compose rm -f import-osm
docker-compose rm -f import-external
docker-compose rm -f postgis
docker-compose rm -f pgdata
docker-compose rm -f cache

あとは以前の手順と同様に再構築を進めます。

cp ~/tmp/osmdata/japan-161202.osm.pbf import
docker-compose up -d postgis
docker-compose up import-external
docker-compose up import-osm
docker-compose up import-sql

次にBBOXを取り出したいのですが、日本の範囲として定義されているPOLYファイルはPOLYGONになっていてちょっと計算が面倒なので、とりあえずスクリプトをでっち上げたのでこれを使ってBBOXの値をゲットします。

cd ~/tmp/osmdata
wget https://gist.githubusercontent.com/smellman/a2770f3a6c4717315cc0a4538b985a1c/raw/1008b6d5f061fb7c0eeaddef86d8faf68f1a6d57/poly2bbox.py
% python poly2bbox.py japan.poly
BBOX=122.5607,21.20992,153.8901,45.80245

こんな感じで取れるのでこれをBBOXのパラメータとして使います。
なお、今回はMAX_ZOOMを13としています。というのはどうもズーム9から14で試したら17時間ぐらい処理にかかるらしく、今日のAdvent Calendarに間に合わないので...*6

docker-compose run \
  -e BBOX="122.5607,21.20992,153.8901,45.80245" \
  -e MIN_ZOOM="9" \
  -e MAX_ZOOM="13" \
  export
docker-compose up export

できあがったのはだいたい550MBぐらいのmbtilesで、 exportの処理時間はおよそ3時間ぐらい、importを含めてだいたい4時間ぐらいでいけました*7

あとはバックアップを取ってからtileserver-glでチェックをしてみます。

rm /tmp/tokyo-and-planet.mbtiles
sudo chown $USER:$USER ~/develop/osm/osm2vectortiles/export/tiles.mbtiles
cp ~/develop/osm/osm2vectortiles/export/tiles.mbtiles ~/develop/osm/osm2vectortiles/export/japan.mbtiles
cp ~/develop/osm/osm2vectortiles/export/japan.mbtiles /tmp
cd /tmp
docker run -it -v $(pwd):/data -p 8080:80 klokantech/tileserver-gl

f:id:smellman:20161203201425p:plain

東京周辺はこんな感じです。

f:id:smellman:20161203201443p:plain

千葉市周辺、ちょっとレンダリング対象が少ない感じです。

f:id:smellman:20161203201500p:plain

伊豆大島も建物がレンダリングされていないので寂しいです。

f:id:smellman:20161203201518p:plain

三宅島も同様です。

やっぱりzoom 14の壁が厚いなーという印象ですね。
とりあえずzoom 14だけ足せるかこんなコマンド打って放置しておきます。

sudo chown root:root ~/develop/osm/osm2vectortiles/export/tiles.mbtiles
docker-compose run \
  -e BBOX="122.5607,21.20992,153.8901,45.80245" \
  -e MIN_ZOOM="9" \
  -e MAX_ZOOM="14" \
  export

あと13時間後らしいので、お楽しみですね!!!!!1

*1:今日の小江戸LUGはこのネタをやる予定だったんだけど、この記事がニャオスだったので不参加にしました...

*2:なおドキュメントにはどのファイルを落とすのかが具体的に書いてないので注意してください、僕はソースコードを読んでダウンロードするファイルを判断しました

*3:最後のdocker-compose up exportが必要かどうかっていうとちょっと謎なんだけど、とりあえずドキュメント通りにやってる

*4:たしかMapboxの方のパッケージングでNode 4.xでバグがあるのが引っかかっているような気がします。なお、tileserver-gl-lightの方はUbuntu 16.10でnpm installができ、動作しています

*5:ちなみにこれURLが古くリンク切れしてる

*6:そもそも日本全域自体結構範囲がありますが、それ以外にもBBOXが四角形なので必要のない範囲のレンダリングも必要となってしまいます

*7:ズームレベルを1つ上げると単純に処理が4倍になりますが、データがほとんど無い範囲が多いとそれだけPostgreSQLから帰ってくるものが少なくなるはずなので時間は減るはずです