等高線付きの地図を作成する その2(アンドロイド地図アプリの開発 捕捉)

GWがステイホームで時間があったので、mapsforge用の等高線付きに地図作成にチャレンジしてみましたが、色々ハマりまくりで全然終わらず、目的の地図ができずじまいで終わってしまいました。

alasixosaka.hatenablog.com

今回は、その続きです。
その前に、ざっと前回のおさらい。
アンドロイド地図アプリを開発中で、アプリにはMapsforgeというエンジンを使っていて、map形式のファイルが必要。等高線付きの日本地図は20m間隔なら公開されている方がおられるが、もう少し精度を上げて10m間隔にしてみたい。そのためには、国土地理院から等高線のデータをダウンロードして自分で作成する必要がある。
やり方はをざっと書くと、まず、国土地理院のサーバーからデータをダウンロードする。それをfdggemというPythonスクリプトでGeoTIFF形式に変換。これらは非常に細かいメッシュに分かれているので、これをGdalを使って一つにまとめる。それをPhyghtmapを使ってOpenStreetMap形式に変換する。OpenStreetMapの地図ならこれと等高線なしの地図をマージすれば完成。
ところが、Mapsforgeでは、海と陸の境界を独特の処理で表しているので、陸地の外形をシェープファイルで取ってきて、それをまた、shape2osmでOpenStreetMapの地図に変換し、海の部分を表すOSM形式の簡単な地図を作成する。これらをosmosisでマージして、map形式に変換すれば完成する。
詳しいやり方は、前回の記事を見て下さい。

前回は、四国の地図についてはうまくできましたが、同じやり方で本州をやろうとしたらメモリ不足に悩まされてできませんでした。おそらく、等高線の情報が非常に多いためにデータサイズが多いのが原因ではないかと思います。なんせ四国の地図でも等高線付きは360MBもありますから。等高線が無いとわずか42MBです。
そこで、本州全体を一つにすることはあきらめて近畿だけの地図を作ることを考えました。ところが、処理がうまくできずに海が白い地図ができてしまいました。
ここまでが前回のお話。今回は何とかして近畿地方の等高線マップを作り上げるという話です。同じやり方で他の地方の地図も作れます。

やり方を考える。

とりあえず、いろいろ考えて、4つの方法がありそうだと思いました。

  1. osmosisのbboxという機能を使って、読み込みは本州全体として、書きだすところを近畿だけにしてみる。
  2. Mapsforgeサイトの記述に従って、海岸線のデータを切り出すところから近畿エリアを使って処理する。
  3. Mapsforgeサイトにある、Pythonスクリプトを使う方法を試す。
  4. Mapsforgeサイトにある、シェルスクリプトを使う方法を試す。

方法1

この方法がいままでのデータを使えるので最も簡単に済む。しかし、読み込むファイルを本州全体でやってしまうとメモリ不足が起こる可能性が高い。そこで、等高線のデータは近畿のデータにして、それ以外は本州全体のデータを使ってやってみた。
しかし、途中でバッファオーバーフローが出て止まってしまいうまくいかなかった。
そこで、kinki.polyというファイルと作って、

1 1 union_of_selected_boundaries
   134.256364 35.721237
   134.256364 33.400212
   137.039213 33.400212
   137.039213 35.721237
   134.256364 35.721237
END
END

osmcovert で元の地図データと海岸線のデータを切り出し、海用のデータkinki_s.osmを作成し合体して見た。しかし地図は出来たが相変わらず海は白いままだった。

方法2

方法2~方法4はいずれも、Mapsforge本家サイトに載っている方法。方法2が一番古いやり方らしく、コマンドラインでちまちまと処理を行っていく方法になっている。参考にしたサイトでも基本的にこの方法を使っている。
yueno.net
等高線を入れるとデータが大きくなり、処理に時間がかかるので、とりあえず愚直にサイトのやり方に従ってやってみた。
まず、海岸線のデータをダウンロードせよと書いてあるが、これは以前にダウンロードしてあるのでそれを使った。
そこからogr2ogrで必要なエリアを切り出す。ファイル名はkinki_o1.shpとした。

ogr2ogr -overwrite -progress -skipfailures -clipsrc 134.256364 33.400212 137.039213 35.721237 kinki_o1.shp land-polygons-split-4326/land_polygons.shp

つぎにこれをshape2osmでOSM形式に変換する。

py -2 -m shape2osm -l kinki_o1_ns kinki_o1.shp

これで、kinki_o1_ns.1.osmというファイルができる。
次に、海の部分を表す、kinki_s.osmを作った。

<?xml version='1.0' encoding='UTF-8'?>
<osm version="0.6" generator="osmconvert 0.8.2">
	<bounds minlat="33.400212" minlon="134.256364" maxlat="35.721237" maxlon="137.039213"/>
	<node timestamp="1969-12-31T23:59:59Z" changeset="20000" id="32951459320" version="1" lon="134.256364" lat="33.400212" />
	<node timestamp="1969-12-31T23:59:59Z" changeset="20000" id="32951459321" version="1" lon="134.256364" lat="35.721237" />
	<node timestamp="1969-12-31T23:59:59Z" changeset="20000" id="32951459322" version="1" lon="137.039213" lat="35.721237" />
	<node timestamp="1969-12-31T23:59:59Z" changeset="20000" id="32951459323" version="1" lon="137.039213" lat="33.400212" />
	<way timestamp="1969-12-31T23:59:59Z" changeset="20000" id="32951623372" version="1">
		<nd ref="32951459320" />
		<nd ref="32951459321" />
		<nd ref="32951459322" />
		<nd ref="32951459323" />
		<nd ref="32951459320" />
		<tag k="area" v="yes" />
		<tag k="layer" v="0" />
		<tag k="natural" v="sea" />
	</way>
</osm>

つぎにこれらを一つにマージする。

osmosis --rb file=kinki.osm.pbf --rx file=kinki_s.osm --s --m --rx file=kinki_o1_ns.1.osm --s --m --wb file=kinki_o1.pbf omitmetadata=true

最後にmap形式に変換する。

osmosis --rb file=kinki_o1.pbf --mw file=kinki_o1.map bbox=33.400212 134.256364 35.721237 137.039213  map-start-zoom=10

すると、とりあえず海は青くなったが、なんだか地図全体が青い。陸地も青いところだらけの変な地図になった。海と陸の境界がうまくいってないみたいだ。
なかなかうまくいかない。
うまくいかなかった理由は、海=seaと陸=landのレイヤーがあってなかったみたいだ。上のkinki_s.osmでは、tag k="layer" v="0" /となっているが、mapsforteのサイトではtag k="layer" v="-5" /となっている。
そして、kinki_o1_ns.osmを作った時に使ったshape2osmが2種類あり、Mapsforgeのサイトのものは、

fixed_tags = {
  'natural': 'nosea',
  'layer': '-5'

とlayer を-5にするようになっているが、参考サイトから持ってきたshape2osmでは

fixed_tags = {
  'natural': 'nosea',

となっていて、layerの記述がない。それで、tag k="layer" v="0" /としているのではないかと思う。レイヤーがあってないので海と陸の区別がうまくいかずに、全面海のような感じになったみたいだ。

f:id:alasixOsaka:20200517122957j:plain
京都市が水没した感じの地図になってしまった。
そこで、kinki_s.osmを作り直して、tag k="layer" v="0" /の部分をtag k="layer" v="-5" /とし、shap2osmもlayerの記述の入った方を使うことにして、始めからやり直した。
openstreetmapのファイル名はkinki_o1_o.pbfとした。

osmosis --rb file=kinki.osm.pbf --rx file=kinki_s.osm --s --m --rx file=kinki_o1_ns.1.osm --s --m --wb file=kinki_o1_o.pbf omitmetadata=true

これをmap形式に変換した。

osmosis --rb file=kinki_o1.pbf --mw file=kinki_o1_o.map bbox=33.400212 134.256364 35.721237 137.039213  map-start-zoom=10

すると、海がちゃんと表示されるようになった。
そこで、等高線の地図データとマージしてみた。

osmosis --rb file=kinki_o1_o.pbf --rb file=kinki_dem10b.osm.pbf --s -m --mw file=kinki_o1_oc.map bbox=33.400212 134.256364 35.721237 137.039213  map-start-zoom=10 type=hd

これでようやく等高線付きの地図ができた。

f:id:alasixOsaka:20200517125355j:plain
海が青く、等高線も表示できた。
ファイルのサイズは等高線なしで216MB、等高線ありだと748MBになった。10m間隔では本州全体を一つのファイルにするのは無理がありそう。
近所のポンポン山も10m間隔の方が見やすい。
f:id:alasixOsaka:20200517130641j:plain
ポンポン山付近の比較左が20m間隔。
今回は、方法2で何とかうまくいったので、方法3と方法4は試していない。ただ、方法4は地図データもサーバーからダウンロードするようになっているので、等高線をつけようと思うとシェルスクリプトを解読して変更する必要があるので、ちょっとハードルが上がりそう。pythonを使うのに問題が無ければ方法3の方が無難かもしれない。方法3なら自分で用意したosm の地図をベースに使えるのでやり易いと思う。また、時間のある時に試してみようかと思うがいつになるかは不明。