前記事に引き続き国土地理院数値地図やSRTM等の標高データの利用方法の整理です。実は、いくつかサンプルアプリを作成したものの未だにベースマップをどうするのかが悩みの種です。以前に試したマップの不満点は主に以下。
- OpenStreetMap: サーバ・ネットワークの負荷の問題か遅いことが多い
- OpenCycleMap: あまり日本の自転車事情にマッチしていない気がする。自転車道の定義がしっかりしているドイツ・オランダあたりを見るといい感じなんですが…
- Mapbox: MapBox studio等で自分好みにカスタマイズでき、サーバも安定しているので本命視したいところだが、いま一つカスタマイズの範囲が狭い気がする(単に好みじゃないだけかも)
そこで、以前に独自のタイルサーバを立ち上げてレンダリングにトライしました。結果は以下のような感じ。
- 今借りているレンタルサーバでのCPU性能、ストレージ容量の不足
- 等高線や陰影図の作成に使った国土地理院地図の使用条件申請問題
で、例によって公開せずに、自宅サーバでの運用で満足してしまっていましたが、この機会に公開容易なSRTMで再作成しながら、手順を再整理しておくことにしました。
ただ、結論として「ユーザに合ったそれぞれの地図デザイン」を目指すには、このラスタータイルは筋が悪そうだと思っています。PCやスマートデバイスのCPU処理能力やストレージも格段に向上した現在では、ベクタータイル方式かオフラインマップ方式でクライアント側ににレンダリング処理を任せる方が得策かと思っています(ベクタータイルは現在実験中)。
ということで、ラスタータイルマップの集大成的な意味でのまとめです。
レンダリングサーバ構築
標高情報を付け加える前に、タイルサーバの構築方法をおさらい。一から構築する際の流れは以下。
- PostGIS上に地図データベースの構築
- レンダリング用のスタイル定義ファイルの用意
- タイルサーバ(mod_tile)の設定
データベースの構築
PostGISをインストール、作成したい地域のOSMデータ(*.osm.pbf)はダウンロードして、以下を実行。
$ createdb gis
$ echo "CREATE EXTENSION postgis" | psql -d gis
$ echo "CREATE EXTENSION hstore" | psql -d gis
$ /usr/bin/osm2pgsql --create --slim -C 8000 --hstore --multi-geometry ${PBF_PATH}
※ -C 8000 は実行するマシンの搭載メモリ量によって調整が必要
スタイル定義ファイルの用意
(多分一昔前の)OSM標準のスタイルシートがこちらから取得できます。ここに同梱されているosm.xmlというファイルがスタイル定義ファイルです。これを使えばとりあえずタイルサーバは構築できるはず。
なお、独自のスタイル定義ファイルを作成する方法として、CartoCSSがあり、以前の自分はよくわからずにこれで作っていたらしい(笑)。カスタマイズにおいて一番肝の部分なのですが、他のスタイル記述言語(MapCSS等)との比較もしてみたくなったので、今回は割愛します。
タイルサーバーの設定
続いて、用意したOSMデータとスタイル定義から地図画像をレンダリングするサーバの構築です。Ubuntu系であれば、基本的にこちらを参考にすれば動くと思います。重要となるのは、以下にあるようにapatcheのconfigと、/etc/renderd.conf。
Most config can be done in the apache config and in /etc/renderd.conf
ここに先ほど用意した、スタイルファイル(osm.xml)を指定してやればOK。
等高線・陰影の追加
作成した地図に等高線と陰影の追加をしてみます。OSMには地形データは含まれていないので、別に作成したデータをレンダリング時にマージする形になります。
等高線データの作成
GDALのツールでできます。100m間隔の等高線なら以下のような感じ。入力するGeoTIFFファイルは、前回の記事で作ったもの。
$ gdal_contour -i 100 -a height map.tif map-c100.shp
$ shapeindex map-c100
陰影図の作成
こちらも基本的にGDALツールで作成できます。ついでに段彩図(色で標高を表すもの)も作成して結合してみます。
$ gdaldem hillshade -alt 60 map.tif map_hillshade.tif
$ gdaldem color-relief -alpha -co ALPHA=YES map.tif ramp.txt map_colorrelief.tif
$ python ./hsv_merge.py map_colorrelief.tif map_hillshade.tif map_colorshade.tif
ここで、2つの画像をマージするhsv_merge.pyはこちらから取得したものを使用。ramp.txtは標高ごとの色を指定するためのファイルで内容は以下。標高毎にRGBを指定します。
-9999 0 0 0 0
-50 0 0 205
-10 255 255 255
0 255 255 255
200 200 255 226
500 190 255 210
1000 180 255 200
1500 170 255 190
2000 160 255 185
3000 91 255 144
3800 255 255 255
SRTM250を使ってできた画像が以下。
画像をクリックしていただけるとわかりますが、なんか荒いですね。あと、詳しいことはわかりませんが、この座標系のままだとOSMのタイルとのマージがうまく行かないようです。その2つを解決するために、元のGeoTIFFを予め変換しておきます。
$ gdalwarp -of GTiff -co "TILED=YES" -srcnodata -32768 -t_srs "+proj=merc +ellps=sphere +R=6378137 +a=6378137 +units=m" -rcs -order 3 -tr 250 250 -multi map_src.tif map_dst.tif
ここで、-t_srsに指定しているのが変換後の座標系なんですが、多分地球を半径6378137mの球体と近似しているんだと思います。で、その後の-tsが解像度です。解像度を細かくするとスムージングされるようなので、試しに150m, 50mで作ってみました。
かなりそれっぽくなってきました。参考までに、国土地理院10mメッシュ(-tsは30を指定)で作成すると以下のようになります。圧倒的です。
なお、画像のサイズは、日本本土でSRTM(等倍)で約394MB、SRTM(-ts 150 150)で約1.2GB、SRTM(-ts 50 50)で約11GB、地理院10m(-ts 30 30)で43GBになってます(SRTMと国土地理院で範囲は異なるので目安程度です)。SRTM250だと必要以上に細かくしてもぼやけるだけなので、ベースマップの下絵には-ts 150版を採用することにしました。
ベースマップへのマージ
作成した画像データをマージするには、以下のような定義をosm.xmlに追加してやります。*.includeファイルの作成方法は、こちらの記事に記載されています。
*** city.xml 2017-11-07 13:41:14.000000000 +0900
--- osm.xml 2017-11-07 13:41:58.000000000 +0900
***************
*** 1,5 ****
<?xml version="1.0" encoding="utf-8"?>
! <!DOCTYPE Map[]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" background-color="#f2efe9" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Parameters>
--- 1,11 ----
<?xml version="1.0" encoding="utf-8"?>
! <!DOCTYPE Map[
! <!ENTITY contour-style SYSTEM "data/contour/contour-style.include">
! <!ENTITY contour-c10 SYSTEM "data/contour/contour-layers-c10.include">
! <!ENTITY contour-c50 SYSTEM "data/contour/contour-layers-c50.include">
! <!ENTITY contour-c100 SYSTEM "data/contour/contour-layers-c100.include">
! <!ENTITY color-shade SYSTEM "data/colorshade/color-shade.include">
! ]>
<Map srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over" background-color="#f2efe9" maximum-extent="-20037508.34,-20037508.34,20037508.34,20037508.34">
<Parameters>
***************
*** 42,47 ****
--- 48,58 ----
<PolygonSymbolizer fill="#b5d0d0" />
</Rule>
</Style>
+ &color-shade;
+ &contour-style;
+ &contour-c10;
+ &contour-c50;
+ &contour-c100;
<Layer name="ocean-lz"
minzoom="750000"
srs="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over">
(あ、こんなとこにさっきgdalwarpで指定した座標系が登場してた。これかー!)
まとめ
記事にまとめてみたけれども、ベースマップに関しては一長一短でまだ満足したものがありません。strava heatmapや雨雲マップ等との親和性から一時はラスターマップにこだわっていましたが、今はベクタータイルやオフラインマップの方が可能性を感じているので、この記事を持ってラスターマップの改善は一旦凍結するつもり。ただ、GeoTIFFの扱いやレンダリングの基本を身につけるにはかなり意義があったと思う。
あと、満足していないといいつつ、オープンソースや公開データを用いて、自宅PCでここまでの地図&ルート探索機能が作れるようになっていたのは、結構驚きでした。しかも、まだまだ奥が深そうという…
ここまで作成したものを、公開版Plannerにマージしたので、興味がある方は是非お試しください。