ベクトルタイルの覚え書き

Androidアプリに,国土地理院ベクトルタイル提供実験の表示機能を実装したときの覚え書き

拙作のAndroidアプリ 地図ロイド で,国土地理院の ベクトルタイル提供実験 のデータを使わせていただく機能を実装したときの覚え書きです.

■ 基盤地図情報(基本項目)編

ベクトル地形図という機能を搭載しました.

ベクトル地形図

これは,基盤地図情報(基本項目) の一部のデータを,指定したスタイルで表示するというものです.

1.ベクトルデータの描画方式

(A) ベクトルデータとして描画する方式

ベクトルデータはポリライン(他にもありますが)なので,
各頂点を緯度経度からスクリーン座標に変換して,画面にポリラインを描画するというのが素直な方法ですが,
縮尺を下げて画面に存在する図形数が膨大になると,性能に問題が発生しました.

図形数が増えれば増えるほど,毎フレームの描画処理が増えてしまうため,
そうなると動きがモッサリです.

(B) ラスタタイルを生成してそれを描画する方式

ポリラインを描画した256×256サイズのbitmapをバックグラウンドで内部的に生成し,
それを,Webマップタイルと同じ原理で表示する方法です.
(つまりこのbitmapは,ズームレベル(18,17,16,…)毎に生成します)

これなら,図形数が増えてもbitmap生成時の性能に影響するだけで,一度きりです.
また,bitmap生成はバックグラウンドで行っているため,時間がかかっても操作感に影響はありません.

地図ロイドでは,この方式を使っています.

ただしこのbitmapはメモリ上に保持するだけで,現状,ファイルには保存していません.
スタイル設定によってbitmapの内容は変わるため,有効か無効かを管理する必要があるのと,
ストレージの容量を余分に消費することの是非で,今のところそうしています.

しかしファイルに保存した方が快適になるのは確実なので,考え中です.

2.GeoJSONパースの所要時間

ベクトルタイルのデータはGeoJSON形式で提供されていますが,これをパースするのに時間がかかります.
機種によって1タイルあたり100~500msecぐらいだと思いますが,小縮尺時は1画面あたりのタイル数が多いため,侮れません.

そのため,ダウンロードしたベクトルタイルのパース結果のうち必要最小限のデータだけを端末にキャッシュとして保持し,
それを使うようにすることで,このパース結果に相当する図形情報をメモリ上に構築する時間を,1タイルあたり30msec程度に短縮しました.

現状,可変長文字列のGeoJSON属性を簡単に扱えるように,パース結果はテキストで保持していますが,
固定長バイナリにすればさらに性能が上がると思います.

ただし全体の処理時間からして,ここはそこまで凝らなくても良いと思っています

3.レベル18限定による小縮尺時の性能低下

データがレベル18のみで提供されているため,小縮尺で表示するときに,
ダウンロードおよび処理するファイル数が多くなります.

これは根本的なところなのですが,
利用者からすると,ラスタデータを使うときに比べて大幅に性能低下するわけで,
何とか改善できないものかと思っています.

4.塗りつぶし

「水涯線」ですが,川の流れが数本平行して流れている場所など,
どこが川でどこが陸なのか見た目では全然わからなくなります.

視認性をよくするために,水涯線を元に水域を何とか塗りつぶせないかと考えていましたが,
挫折しました.

結局水域については,標準地図 のレベル17から水域の色(具体的には,R=190±10, G=210±10, B=255±10)
を抜き出して重ねる
という方法をとっています

が,注記などが重なっている場合に色が抜けるため,不完全です.下の例では,標準地図に入っている「布引貯水池」という注記で色が抜けています.

今のところ考慮していませんが,建物についても同じような話があると思います.

■ 地名情報編

3D表示機能で,地名の表示に対応しました.
3D地形を表示する

また,別アプリ 地図ロイドReality でも,地名を表示させていただいています.

これらは,ベクトルタイル提供実験(地名情報)の自然地名のデータを,3D表示しています.

1.同じ位置に重なった地名

例えば地理院地図サイトで,
この座標http://maps.gsi.go.jp/#15/34.839731/135.557728/&base=std&ls=std%7Cexperimental_nnfpt&disp=11&lcd=std

を開くと,中心に
-勝尾寺川
-佐保川

の2つの地名が重なっています.

ベクトルタイルのデータ
https://cyberjapandata.gsi.go.jp/xyz/experimental_nnfpt/15/28722/12997.geojson

ではこうなっていて,

{ "type": "FeatureCollection","features": [
{ "type": "Feature","geometry": {"type": "Point",
"coordinates": [135.557711,34.839723]},"properties": {"class":"NNFPt","lfSpanFr":"2012-10-12","orgGILvl":25000,"type":"河川",
"name":"勝尾寺川","kana":"かつおじがわ","rj":"Katsuoji Gawa","Aname":"","Akana":"","Arj":"","gaijiFlg":"0"}},
{ "type": "Feature","geometry": {"type": "Point",
"coordinates": [135.557711,34.839723]},"properties": {"class":"NNFPt","lfSpanFr":"2012-10-12","orgGILvl":25000,"type":"河川",
"name":"佐保川","kana":"さほかわ","rj":"Saho Kawa","Aname":"","Akana":"","Arj":"","gaijiFlg":"0"}}
]}

確かに緯度経度が完全に一致しています.

複数の地名が同じ緯度経度を持つということが,地理的にどういう意味を持つのかは存じ上げないのですが,
他の地名の組み合わせでも存在するため,おそらく何らかの地理的な意味合いがあるのだと思います.

しかし表示する時の話として,位置が完全に重なって画面上で読めなくなるというのがあります.

そのため,複数の地名が完全に同じ緯度経度を持つ場合は,
地名を連結して1つに集約して表示するという対応を,地図ロイドでは入れています.

この場所の場合ですと,「勝尾寺川/佐保川」と表示するようにしています.

...こういう対応ができるのは緯度経度が完全に一致しているからですので,
地理的な意味合いではなく,集約しやすいようにという配慮で意図的に同じ緯度経度を設定されているのかも,と想像したりします.

2.空振りのリクエスト (2018/10/5 追加)

地名データが存在しないタイルが当然あるわけですが,
例:
https://cyberjapandata.gsi.go.jp/xyz/experimental_nnfpt/15/28722/12998.geojson

この場合,アプリ側では404エラーのレスポンスを受けとることになります.

この空振りのリクエストは通信の無駄ですので,無くすことができればという思いはあります.

例えば何らかの形で,データが存在するタイルの一覧等を取得できれば,それを使わせていただけると思います.