トップ地図アプリMap4 > OSMバイナリレコードファイルの更新手順

OSMバイナリレコードファイルの更新手順

OSMバイナリレコードファイルの作成

はじめに

OSMバイナリレコードファイルの更新手順をここにまとめておく。

Geofabrik Download Serverから japan-latest.osm.pbf ファイルをダウンロードする

Geofabrik Download Server[1] から japan-latest.osm.pbf をダウンロードする。 ダウンロードから最終的な OSMバイナリレコードファイルを作成して、 スマホやタブレットに転送するにはそれなりの時間がかかるため、 新しいデータをいち早く確認したい場合には kanto-latest.osm.pbf や shikoku-latest.osm.pbf をダウンロードすることもある。

これらのデータは昨晩のデータである。狭い範囲で、最新のデータを得る方法として OverpassAPI[2] や JOSM による方法がある。

JOSM は編集ソフトであるが、狭い範囲であるが、ダウンロードしたファイルを外部ファイルに出力できる。

osm.pbfファイルを解凍する

osm.pbfファイルは圧縮形式のファイルである。これを xml形式のテキストファイルに解凍する。 解凍すると japan-latest.osm ファイルは約40GBの大きさになる。

パイプライン処理で必要なディスク容量を減らす方法もあるが、 ここではテキストファイルを作成する方法を採る。

解凍には、osm.pbfファイルの本家である osmosis か、別のフリーソフト osmconvert を使う。

解凍は osmconvert が速い(約10分)。ただし、osmosisに比べて使用実績が少ない。

osmconvert d:/downloads/japan-latest.osm.pbf > d:/osm/japan.osm
osmconvert d:/downloads/kanto-latest.osm.pbf > d:/osm/kanto.osm
osmconvert d:/downloads/shikoku-latest.osm.pbf > d:/osm/shikoku.osm

osmconvert でエラーが起きる場合、osmosis を使う。解凍に時間がかかる(約1時間)。

osmosis --rbf d:/downloads/japan-latest.osm.pbf --wx d:/osm/japan.osm

japan-latest.osm.pbf(1.87GB) を japan.osm(42GB) に解凍した。

xml形式をバイナリ形式に変換する

xml形式は分かりやすく、柔軟性が高いが、解釈に時間がかかる。 そこで単純なバイナリ形式に変換する。

OSMデータはNode、Way、Relationの三つのオブジェクトで構成される。

Nodeセクション、Wayセクションは巨大なため、パソコンでは日本地図領域の全データを同時にメモリに載せることはできない。 そのため、1パスで、xmlファイルからOSMバイナリレコードファイルを作り出すことはできない。

このため、第一フェーズとしては、xmlファイルをバイナリ化して、 Nodeセクション、Wayセクション、Relationセクションに分けて三つのファイルに出力する。

今回は次のようなフォーマットとしている。xml ファイルでは、Wayセクションのノード列は ノードID(osm_id)列であるが、 それを座標値に置き換えたものを出力する。 文字コードは UTF-8 とする。

length, osm_id, lon, lat, {key, val}*                         ----- Node   
length, osm_id, num_nodes, {lon, lat}*, {key, val}*           ----- Way  
length, osm_id, num_members, {type, ref, role}*, {key, val}*  ----- Relation       
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -encode japan

japan.osm(42GB) の変換結果は node0.obf(106MB)、way0.obf(1GB)、way1.obf(1GB)、way2.obf(995MB)、relation0.obf(38.7MB)、全体で3.11GB となった。

OSMバイナリレコードファイルを生成する

大半のNodeは Wayを構成する頂点の座標のみを表す。 ごく一部が単独で例えばバス停や交差点の信号機などの Pointレコードとなる。

一部の Way は Relation のメンバーとなるが、単独で、Line(道路など)や Polygon(建物、公園など)が多い。

Relationは都道府県境界(Polygon/Multipolygon)、広域森林(Polygon/Multipolygon)、バス路線などを表す。 バス路線はバス停(Point)や道路(Line)をメンバーとしてもつ。 メンバーは、通常は、それぞれ、単独でバス停レコード、道路レコードにもなっている。 Map3では、バス路線道路には中央に細い青線を引いている。バス路線レコードを新たに追加するのではなく、 バス停レコード、道路レコードにバス路線id列を含めている。

Relationの扱いが一番難しい。

陸地ポリゴンも本州、北海道など巨大であるが、高ズームでは、これを多数のポリゴンに分割したもので陸地の描画を行う。

Node、Way、Relation の場合はファイルサイズはさほど気にする必要はないが、 OSMバイナリレコードは直接、レンダリングに使うものであるからなるべくコンパクトにする必要がある。 また、無数のレコードから所望のレコードを素早く抽出する必要がある。

Map4 では Map3 とは大幅に異なるシンプルなフォーマットとする。

head(4), [num_nodes(4)],  {lon,lat}*, {key,val}*  ... point/line/polygon 
head(4), {num_nodes(4)}*, {lon,lat}*, {key,val}*  ... multipolygon
head:
  第0,1bit(0x03) 0: point、   1: line、    2: polygon、 3: multipolygon
  下位3バイト   レコード長
num_nodes:
   multipolygonの場合、最終要素(最終inner polygonノード数)の最上位ビットは 1 とする。

multipolygon の {num_nodes(4)}* は少しわかりにくい。 可変長バイトコード化では、先頭に polygon 数を置き、
num_polys, {num_nodes}*
としている。また、head は type と length を分離している。

次に、大きく、プログラムを修正するときがくれば、フォーマットを可変長バイトコードと合わせたい。

バイナリレコードファイルは高、中、低ズームの三つとなる。

java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -parse japan low
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -parse japan mid
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -parse japan high

結果は japan-low.dat(23.7MB)、japan-mid.dat(301MB)、japan-high.dat(2.85GB) となった。

OSMバイナリレコードファイルを分割する

メッシュ(zoom相当)に分割するのみで、バイナリレコードフォーマットは上記と同じ。

java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -devide  3 3 japan-low
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -devide  7 7 japan-mid
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -devide 12 7 japan-high

結果は japan-low3(12.9MB)、japan-mid7(304MB)、japan-high7(232MB)、japan-high12(3.40GB) となった。

可変長バイトコード化する

ブロック分割されたファイル毎に実行する。

レコードフォーマットは以下の通りとする。

可変長バイトコードによりデータ圧縮するととも、管理データを追加する。

座標値を復号化せず、タグの中身を調べるときのために、tags_length を持たせている。

head, length, tags_length,            [num_nodes],  {lon,lat}*, {key,val}*  ... point/line/polygon 
head, length, tags_length, num_polys, {num_nodes}*, {lon,lat}*, {key,val}*  ... multipolygon
head:
  第0,1bit(0x03) 0: point、   1: line、    2: polygon、 3: multipolygon

head(レコード type) は1バイト固定、 length、 tags_length、num_polys、num_nodes は 符号なし可変長バイトコード である。

length には、head と length 自体の長さは含まれない。

tags_length は末尾の {key,val}* 部の長さである。

先頭の lon、lat は4バイト固定整数ペアで、次のノードから 可変長バイトコード(符号あり)とする。

ただし、multipolygonの場合、各polygon毎に先頭は4バイト固定整数ペアとする。

タグ部 {key,val}* は入力ファイルのものと同じ。

管理データ

レコード毎に空間検索に使う境界ボックスなどの管理データを持つ。

メモリ上では int配列とする。先頭は flag;type;length、flag は width、height が各2バイト、合わせて4バイトの時 0、それぞれ4バイトの時 1 とする。type 0(Pointレコード)のときは width;height はなし(共に値は1なので省略)。

head;length(4), minlon(4), minlat(4), width;height(0/4/8)
head:
  第0,1bit(0x03) 0: point、   1: line、    2: polygon、 3: multipolygon
 第4bit(0x10) 0: width, height は2バイト、 1: width, height は4バイト

type、length は実際上はバイナリレコードと同じものであるから、 境界ボックス(minlon、minlat、width、height) をバイナリレコードに含めれば、平均的には約12バイト増で済むが、 byte配列から int、short値を取り出すのは固定長であってもシフトや加算演算が必要となる。

厳密にはバイナリレコードでの length には、バイナリコードにおける type と length 自体の長さは含んでいないが、 管理部の length はこれらを含んだレコード全体の長さを表す。

java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -bytecode japan low 3
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -bytecode japan mid 7
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -bytecode japan high 7
java -Dfile.encoding=UTF-8 -Xmx5g -classpath ./class OSMUtil -bytecode japan high 12

結果は japan-low3(13.1MB)、japan-mid7(226MB)、japan-high7(131MB)、japan-high12(2.74GB) となった。

{key,val}* のデータ圧縮

可変長バイトコード化案もあるが、あまり圧縮効果は期待できない。key は1バイト、val は key によって、 1、2、4、5 とした方が効果的であろう。

通常の osmタグの場合、1か2となる。 数値型は必要に応じて、複数のタイプに分ける。

layer、admin_level は1バイトで済む。

width、height は小数点以下1桁として、10をかけて整数化する。 それを1バイトか2バイトの整数で表す。3バイト以上のときは元の4バイトの float型とする。

way_area は精度はいらないので、そのまま整数化して 1、2バイトで表す。3バイト以上のときは元の4バイトの float型とする。

populationも精度はいらないので、4バイトを超えるのときは float型、4バイト以下は2か4バイト整数とする。

文字列型は長さが255までは長さを1バイト、それ以上の場合は2バイトで表す。

難しくはないが、コードが嵩む。そこまでしても効果は限られているので、上記案の一部だけを実行してみよう。

Androidスマホ/タブレットにファイル転送する

c:/map/data4/bus_route.tsv(バスrelationデータ) を Map4/Common/guides/ にコピーする。

フリーワード検索用 c:/map/data4/osmdata_japan.dat を Map4/Common/ にコピーする。

最終的なOSMバイナリレコードファイルは Map4/ にコピーする。

リファレンス

[1] OpenStreetMap Data Extracts
[2] JA:Overpass API
[3] JOSM