トップ地図アプリMap5 > OSMバイナリレコードファイル形式

OSMバイナリレコードファイル形式

前ページ

はじめに

OSM地図レンダリング用のOSMバイナリレコードファイルは Map4 に、uid、timestamp、user を追加する。タイル分割では rec_id も追加する。

Encoder

Encoderの出力フォーマット:   文字コードは UTF-8。
length, osm_id, uid_user, time, lon, lat, {key, val}*                         ----- Node   
length, osm_id, uid_user, time, num_nds, {lon, lat}*, {key, val}*             ----- Way  
length, osm_id, uid_user, time, num_members, {type, ref, role}*, {key, val}*  ----- Relation

lengthは自分を含むレコード長(単位:バイト)。

uid、userはペアでコード化する。現在(2024年10月)、japan.osm で、4万弱のため、2バイトでもよいが、 4バイトを割り当てる。

データ更新時刻 time は分単位として、4バイトを割り当てる。精度はもっと下げてもよいので、2バイト化も可能である。 しかし、レコードサイズの縮小に拘らない方がよい。

role は enum で定義し、レコード上は2バイトで表す。inner、outer などいくつかについては、 レンダリングに関係するが、レンダリングでは無視するものが殆どである。 【ソースデータ例】

  <node id='31253515' timestamp='2022-01-09T09:59:40Z' uid='7707735' user='zyxzyx'
 visible='true' version='5' changeset='115935292' lat='35.6536052' lon='139.7601994' />

japan のファイルサイズは node 136MB、way 3.42GB、relation 37.0MB、計3.57GBとなった。

OSMバイナリレコードファイル形式

まずは Map4 を踏襲して、次のようにしている。

head,                              {lon,lat},  tags_length, {key,val}*  ... point
head,  bbox,             num_nds,  {lon,lat}*, tags_length, {key,val}*  ... line/polygon
head,  bbox, num_polys, {num_nds, {lon,lat}*}*, tags_length, {key,val}*  ... multipolygon
head:
  第0,1bit(0x03) 0: point、 1: line、 2: polygon、 3: multipolygon
  下位3バイト レコード長(headを含む)

しかし、OSMバイナリレコードファイル形式は Map4の一つ前に戻すことも考えている。マルチポリゴンの場合、 ポリゴン毎のノード数を前に置き、その後に、座標値列データを連続して置く。

この方がプログラムが分かりやすくなる。

現在は osm_id は特殊な (key,val) の一つとしているが、osm_id を独立項目にすることも検討している。 シンプルな建物やマイナーな道路などでは osm_id は省けるようにしたいため、head か bbox の次がよいだろう。

head,                              {lon,lat},  tags_length, {key,val}*  ... point
head,  bbox,             num_nds,  {lon,lat}*, tags_length, {key,val}*  ... line/polygon
head,  bbox, num_polys, {num_nds}*, {lon,lat}*, tags_length, {key,val}*  ... multipolygon
head:
  第0,1bit(0x03) 0: point、 1: line、 2: polygon、 3: multipolygon
  下位3バイト レコード長(headを含む)

OSMバイナリレコードファイルの管理

Map4では予めサイズの異なるバッファプールを確保して、最適なサイズを割り当てるようにした。 ガーベージコレクションは減るが、適切なサイズとバッファ数を求めるのが難しい。 常時、使用メモリが大きくなるのも、パフォーマンス上好ましくないと思われる。 よって、以前のように、動的確保、解放方式に戻す。

レンダリング

Map4と同じであり、当面、大きな変更はしない。

項目の追加

統計では uid、(user、) を知りたいことがある。 Android機に読込むファイルには要らないかもしれないが、あってもさほど邪魔にはならない。 osm_id、timestampは含めたい。2038年問題を考えると、timestampは 8バイトが望ましいが、 地図システムでは高精度はいらない。時:分または日付だけでもよい。よって、4バイトで十分である。

osm_id は、node に対しては4バイトでは足りない。way、relationは4バイトでもよいが、 4バイトの節約は大した利益を生まないので、一律8バイトとする方がいいだろう。

uid(4)、timestamp(4)、osm_id(8)を追加すれば、ファイルサイズは 10数%増える。 ファイル読み込み時間自体はその分増えるが、パフォーマンス全体の増え方はそれより小さいであろう。

日本地図のマッパーは変換表を使えば 2 バイトで表せるだろう。 数件のレコードにしか現れない uid は省略してもいい。timestampは日付だけであれば2バイトで表せる。 osm_idも8バイトはpointレコードのみとすれば、レコード当たり8バイト強の増加ですむ。 しかし、約8バイト節約の代償として、プログラムが少し複雑になる。 経験上、プログラムを複雑にするのは得策ではない。素直に16バイト追加した方がよい。

開発記録

Map4はガーベージコレクションを気にして、複雑にしすぎた。一からプログラムを作り直そう。

2024.9.18 座標値列データエリア

Map4では座標値列データエリアを再利用している。これが、少々複雑で、プログラムがパッと見ただけでは分からない。 float[] Renderer#points、int Renderer#ixPoints、int OSM#offPnts などが関連している。 OSMバイナリレコードごとではなく、複数レコードで一本化しているのかも知れない。 効率を考え、空間検索で抽出された全レコード分を points 配列に置いているのであろう。

必要サイズが分からないため、倍々で増やしている。最終的には余分なメモリが確保される無駄がある。 倍々ではなく、例えば、1.5倍とすれば、この無駄は減るが、再割り当て回数が増える。

よって、座標値データエリアは float[] OSM#points に置くことにする。

2024.9.17 全描画を確認した

2024.9.17 バス路線が描画された

色々シンプル化しているが、まず、バス路線(細い青線)だけ描画された。 使用メモリは 150~250MB である。

2024.9.17 ログディレクトリの生成

ログディレクトリがなかった場合、生成するようにした。

2024.9.17 陸地ポリゴン描画確認

陸地ポリゴンが正常に描画されることを確認した。 ただし、このまま先に進むのではなく、OSMのデータを見直したい。

OSMの配列メンバーの再利用はやめる。動的に確保して、レンダリングが終わったら解放する。

2024.9.17 readAll実行確認

readAll /storage/36C5-1401/Map5/dat/lands-high6/56/25.dat 1219218

リファレンス

[1] OpenStreetMap Data Extracts