初めて、自作OSM地図を作って11年近くたつ、スマホ版も4年になる。 パソコン版よりスマホ版の方が難しく、まだ、安定していない。 急ぐ必要はないので、じっくり時間をかけて作り直したい。 スマホ版の当初はパフォーマンスを気にしすぎた。 無視はできないが、あまり気にせず、分かりやすさに重点を置きたい。
AI検索時代となり、知りたいことが端的に得られるようになり、プログラムに磨きをかけやすくなった。 空間検索については、当初から時々ネット記事を読んでいたが、なかなか理解できなかった。 それが、AI検索で大きく理解が進んだ。
OSMを知った当初は、osm2pgsql により、OSMデータを PostgreSQL/PostGIS にインポートしていた。 このレコードを Mapnik によりレンダリングして、タイル画像ファイル群を作っていた。
しかし、バージョンアップにより、osm2pgsql あるいは Mapnik が Windows OS ではサポートされないことがあった。 このため、VirtualBox を導入し、Ubuntu(Linux)上で osm2pgsql や Mapnik を動かした。
VirtualBox、Ubuntu、osm2pgsql、PostgreSQL/PostGIS, Mapnik がそれぞれ非同期にバージョンアップがあり、 これが大変な作業となる。OSM地図の個人利用のために、とてもやっておれない。
4、5年後、C#で全てを自作することにした。道路など曲線に沿って描く文字など、細かく見ると、 Mapnikによるレンダリングよりは多少見栄えは劣るが、見た目は殆ど差異がない。
4年余り前から、スマホに移行したことから、C# を Java に変更した。 地図の表示に、Windows タブレットを使っていたときは、パソコンで一括作成したタイル画像ファイルをタブレットにコピーしていた。
日本全土について高ズームでタイル画像を作ることは無理であるが、それでも、1週間以上かかった。
スマホは、今使っているノートPCほどではないが、以前のノートPCよりも高性能であり、レンダリングはスマホ自体で行っている。 事前に一括して作成するのではなく、表示するとき作成している。以前作成保存したタイル画像があれば、まず、それを表示している。 古いものであれば、裏で更新して、出来上がったときに、置き換えて表示する。
地図に描画するものは点、線、面であるが、点といっても、描画するのは、交差点の信号、バス停のアイコンおよび交差点名、バス停名などの 文字列である。それぞれ大きさを持っている。
レンダリングは、地図平面を細かく区切ったタイル単位である。交差点やバス停がタイル境界に近い場合、位置としては隣のタイルにあっても アイコンや文字列の一部は描画しなければならない。 したがって、レンダリング上は点は存在せず、面となる。
車道には幅があるため、例え、直線であっても、描画上は線ではなく、細長い矩形である。
これまでの自作地図アプリでは、レコード上は点、線として、空間検索のときに、タイル境界にマージンを加えていた。
その方が分かりやすいとも言える。このマージンは全ての点、線で同じ値となり、レコード毎に変更できない。 しかし、point, line, polygon/multipolygon で変えることはできる。
これまでのメッシュ分割をやめ、全体を一つのファイルにすると、検索時のマージンでは point, line, polygon/multipolygon で変えることはできない。osm2pgsql のように、 point、line、polygon/multipoly の三つに分ければ可能であるが、 一体化している方が後の処理も楽である。
そこで、レコード毎の境界ボックスにアイコンや名称の描画分を含めることにする。 これを踏まえ、point/line/polygon のレコード形式は同じとする。
点(point)の場合、ノード数(num_nds)は 0 であるから、 これを省くことは可能であるが、レコード数全体に占める pointレコード数は小さいため、わずかな削減は狙わず、 形式を合わせる。
マージンまたは境界ボックスの拡大は、点ではアイコンと文字列の描画を考慮する。線では、太さを考慮する。 車道名は道路幅内に描くが、歩道や鉄道名は線の中心から少し離れたところに描いていているので、道路幅だけでなくこの分を考慮する。 面ではなにも考慮しない。
面(polygon)でも、アイコンや文字列の描画はあるが、一般に、それらは polygon 内におさまるため、境界ボックスを広げる必要はない。
低ズームでアイコンも入りきらないほど小さいときは、アイコンを描画しない。 同様に、文字列が、polygonをはみ出すようなときは、文字列を描画しない。
現在、zoom 9 以下を低ズーム(zoom 5以上で、アイコン描画はなく、文字描画は地名のみ)、zoom 10~13 を中ズーム(アイコン、地名、鉄道名など)、 zoom 14以上を高ズーム(アイコン、文字全般)としている。
境界ボックスの広げ幅は zoom 5、10、14 で考える。
パソコンの自作OSM地図で、zoom 14 で、横4文字分が経度で 0.0046 であった。縦4行分が緯度で 0.0042 であった。
アイコンの大きさはばらつくが、経度で 0.0016、緯度で 0.0020 を割り当てれば十分であろう。
pointレコードの場合、アイコンは中心、文字はその下に描くため、点の境界ボックス(minlon, minlat, maxlon, maxlat)は、 中心点極座標を(xc, yc)とすると、ハイズームファイルでの point レコードの境界ボックスは (xc - 0.0023, yc - 0.0010, xc + 0.0023, yc + 0.0052) とすればよい。
文字やアイコンはこの先、もう少し大きくするかも知れない。それにより、上記の数値はもっと大きくする。 しかし、アイコンや文字は zoom 15, 16, 17, ... となっても、縦横2倍、4倍、8倍と大きくするわけではない。 zoom 15, 16 で初めて描画するアイコンもある。その場合、上の数値は 1/2倍、1/4倍 でよい。
次に line について境界ボックスの拡大を考える。川幅が20メートルほどの橋は短い独立したlineレコードである。 この場合、縦横上下に 道路幅分境界ボックスを広げればよい。 鉄道であった場合、鉄橋名は線に沿って描画するため、鉄路幅+1文字分の高さを縦横上下に加える。
通常の道路は一般に長い折れ線である。車道・道路名や鉄道自体の描画は上記のマージンで対応できる。歩道名の場合、文字高分のマージンがいる。
文字の高さの方が大きいので、境界ボックスを上下左右に 0.001度分広げる。
レンダリング結果を吟味して、問題があれば数値を見直す。
Parserプログラムを修正したが、ファイルサイズを3,4割削減するための差分化や mid、low で極座標の精度を下げている分、 プログラムが分かりにくくなっている。ファイルサイズの縮小を考えず、シンプルにすべきかも知れない。
pointのレコード形式を line/polygonと合わせた事による増加は kanto_high.dat では 859MB - 839MB = 20MB(2.4%) であった。
length:4, head:4, osm_id:8, uid:4, time:4, bbox:16, num_nds:4, {lon4/2,lat4/2}*, {key:2,val:2*}* -- point/line/polygon
length:4, ~同上~ time:4, bbox:16, num_polys:2, {num_nds:4}*, {lon:4/2,lat:4/2}*, {key:2,val:2*}* -- multipolygon
bbox: minlon, minlat, maxlon, maxlat
東京都など島しょ部を持つ都道府県や飛び地をもつ市区町村の行政区域(boundary=administrative)は 二つ以上の outer polygon を持つマルチポリゴンとなる。
現在のosm2pgsql はマルチポリゴンを一つのレコード(エンティティ)として扱う。オプションにより、 outer polygon毎に分けることもできる。
一つのレコードとするとその境界ボックスが大きくなるため、レンダリング時間が大きくなる。 outer polygon 毎に分割しておく方がレンダリング時間は短縮できる。
このため、自作OSM地図では、これまで通り、outer polygon毎に分割したレコードとする。
レンダリング以外の目的でもこの行政区域レコードを使用したい。そこでは、分割されたマルチポリゴン を束ねる管理データを持つ。
レンダリング以外の用途では、行政区域(boundary=administrative)だけが分離している方が扱いやすい。
当面は、レンダリングはこれまで通りとするが、将来的には、一般のOSMバイナリレコードには 行政区域レコードは含めず、レンダリングにも分離した行政区域レコードを使用したい。
行政区域データは一般のOSMバイナリレコードのようなタイル分割を行わない。
全体を一つのファイルとして、大きくは admin_level順にならべる。一つの admin-level においては、 区域の中心座標(107をかけて整数化した経度、緯度)をインタリーブしたモートンコードの 昇順とする。
これにより、距離的に近い区域同士はファイル上でも近い位置に置かれることになるため、 レンダリングに使う場合にも、ファイルからの読み込み時間が短縮される。
中心座標での検索はバイナリサーチで迅速に行えるが、 レンダリングでは境界ボックスによる空間検索となる。市区町村や都道府県レベルでは 全管理データをスキャンしてもさほど時間がかからないと思われるが、 大字・小字レベルでは全スキャンは時間がかる恐れがある。これは空間検索時間を実測してからのこととする。 全スキャンで時間がかからなければそれでよい。
市区町村以上は全スキャンとして、それ以下は高ズームでしか描画しないので、例えば、zoom 13 どの検索を 行い、これを4つ以上キャッシュしておく。レンダリングでは、一般レコードと同様に、このキャッシュから 該当レコードを取り出す。キャッシュの入れ替えは稀にしか起きないので、平均的な空間検索時間は非常に短くなる。
ただし、行政境界線のレンダリングそのものの時間は現在と同じため、分離する価値があるだろうか?
パソコンでOSMバイナリレコードを作成するとき、行政区域データを独立ファイルとして出力することを考えてみた。
現在のプログラムにこの機能を追加するのはそう簡単ではないことが分かった。
近年はそうでもないかも知れないが、以前のOSMデータでは boundary=administrative のデータがよく壊れていた。
精度は高くないが、国土交通省の提供する行政区域データは全都道府県のデータが完備している。 このデータを使って行政境界線を描いたり、県別あるいは市区町村別の建物数などを算出してみよう。
このデータをOSMバイナリレコード形式に近い形に変換したい。データは北(北海道)から南(沖縄)へ順に 並んでいるようであるから、並び替えはいらないかも知れない。
並び替えるとしたら、各レコードの中心座標(経度、緯度に 107をかけて整数化したもの)の 経度、緯度をビット単位でインタリーブしたMortonコード(64ビット)の昇順あるいは降順とすれば、 距離的に近い行政区域データレコードはファイル上でも近くになり、空間検索時の効率が良い。
しかし、管理上は、並び替えを行わない方がよいと思われるので、少なくとも、当面は、元のファイルのままとする。
詳細はベージを改める。