トップ地図アプリMap3 > OSMレンダリング
前ページ

OSMレンダリング

はじめに

OSM(OpenStreetMap)地図の場合、国土地理院地図とは異なり、デジタルデータを使ってレンダリングを行う。 自由度は極めて高く、自分好みの地図を実現できるが、プログラム規模は何十倍もの大きさとなる。

元となるデータは xml形式の巨大なファイルであるが、予め、パソコンで加工して、 レンダリングに都合のいいバイナリ形式のファイルとする。一つのレコードは point、line、polygon を表す。 polygon には単純な一つの閉ループからなる多角形データだけではなく、ポリゴン(outer polygon)の中に、 複数の穴(inner polygon)があるマルチポリゴンも含まれる。

xmlファイルからバイナリレコードを作るのも簡単な仕事ではないが、これにつては別途詳細に述べる。

OSMレンダリングでは、現在(MapX)、ベースとなるクラスとして Block、OSM、TileToRender、SuperRenderer があるが、SuperRenderer はなくしたい。

BlockはOSMバイナリファイルを管理するものであり、OSMは個々のバイナリレコードを管理するものである。

自作OSM地図は日本地図が対象であるが、陸地については世界全体が対象となる。 OSMデータベースにとっては、海岸線データであるが、レンダリング上は polygonデータとして扱う。 特に、大陸は巨大であり、日本地図に限っても、本州、北海道、九州などは巨大なポリゴンであり、 中高ズームではそのままでは描画できない。 このため、小さな無数のポリゴンに分割したバイナリレコードにより、陸地を描画する。

陸地は単なるポリゴンであるため、陸地だけのレンダリングであれば比較的簡単である。 この陸地のレンダリングにより、OSMレンダリングの基礎を築きたい。

タイル地図のレンダリング

国土地理院地図、OSM地図は共に、Google Mapに準拠しており、パソコン時代に生まれたものであり、 タイルのサイズは 256x256画素である。近年のスマホはパソコンよりもはるかに高解像度であり、 縦横3倍の 768x768画素に拡大すると文字が丁度よい大きさとなる。 アンチエイリアシングにより、文字の見栄えは悪くはないが、小さな文字はややシャープさに欠ける。

国土地理院地図のタイル画像は 256x256画素であるため、これを縦横3倍の 768x768画素に拡大するしか方法はないが、 OSM地図の場合には、レンダリングでは 256x256画素のタイル画像(ビットマップ)を作成して、表示するときに拡大する方法と 最初からレンダリングで 768x768画素のタイル画像を作成する方法がある。

道路、境界線や森林、公園、建物など図形は 256x256画素の方がレンダリング時間が短縮され、拡大しても見栄えはさほど損なわれない。 しかし、特に小さな文字の見栄えはやや悪くなる。

最初に図形だけを256x256画素タイルにレンダリングして、それを 768x768画素に拡大してから文字を描画するならば、都合がいいが、 実際の地図のレンダリングでは、図形と文字の描画を前半と後半にまとめることはできない。

まず、陸地、広域森林など時間のかかる強大なポリゴンのレンダリングを 256x256画素で行い、 それを768x768画素に拡大してから残りの全てのレンダリングを行う。 少し複雑になるが、レンダリング時間の短縮と文字の見栄えを良くするために、この方法を採用したい。

現在は 64タイルをキャッシュしている。タイルサイズが256x256画素(256x256x4=256KB)であれば、合計16MBであるが、 タイルサイズを縦横3倍にすると、144MB になる。Androidアプリとしては小さな負担ではないが、 現在は十分に余裕のある値である。

国土地理院地図を表示している場合は、キャッシュするタイルのサイズを 256x256画素にできるが、 切り替えには負担がかかるため、キャッシュ上のタイル画像は 768x768画素とする。 国土地理院地図タイルの場合は768x768画素に拡大したビットマップをキャッシュする。

タブレットの場合、パソコンよりも画面サイズは小さいが、256x256画素のままで、拡大処理は一切行わない。 しかし、スマホでの実機デバッグを極力抑えるため、当面、タブレットのタイルサイズを 512x512 とする。


いざ、この方針でプログラムを書くと、道路幅が狭くなったり、描画位置に誤りが生じた。

全ての描画メソッドをタイルサイズ可変とすると応用は自在になるが、描画メソッドはその分複雑になる。 少し時間をかけてメソッドを見直そう。

スマホ用アイコン

256x256画素タイルでのアイコンは 14x14画素が主でおよびそれより小さいものや大きいものがいくつかある。 MapXではまず256x256画素タイルを作成して、それを 768x768画素に拡大表示しているが、 map3 では地名などの文字は 768x768画素タイルに描画している。その方が、より見やすい地図になる。

アイコンについては例えば 14x14画素ファイルを読み込んで、それを 42x42画素に拡大したものを 768x768画素タイルに描画するよりも、最初から 42x42画素のアイコンファイルを用意した方がより鮮明な表示になる。

当面は利用頻度の高い目立つアイコンだけについて、大きなアイコンファイルを用意したい。

アイコンファイルはアプリ起動時に全てメモリ上に読込むため、レンダリングは影響を受けない。

MapXではアイコンは次の配列で管理している。 配列の添え字は (key.ordinal() << 10) + val.ordinal() で決める。

    final static Bitmap[] bmps = new Bitmap[128*1024];

タブレット用 14x14画素ファイル、スマホ用 42x42 画素ファイルを別のディレクトリに置く。 スマホの場合は最初に 42x42画素ファイルを探し、見つかれば、それを読み込み bmps配列に登録する。 そこになければ 14x14画素ファイルを探し、見つかれば、42x42画素に拡大して bmps配列に登録する。

レンダリングは現状のままでよい。

プログラム上は最初に 256x256画素タイル用を読み込み、次に、768x768画素タイル用を読み込んだ。 処理時間上は少し無駄があるが、後者が少ない場合は問題ではない。 後者の読み込み時間が大幅に増加したときは再考する。

svgからpngへの変換はインターネットエクスプローラーを使った。対象が増えた場合は、 ユティリテイプログラムの作成も検討する。

履歴およびメモ

2023.12.6 レンダリングはほぼ完了した

MapXに比べて文字がきれいになった。

アイコンについては、現在はパソコン用を読み込み時に縦横3倍に拡大している。 最初から縦横3倍にした画像ファイルを用意すればもっときれいに表示される。 いずれ少しずつ置き換えていく。

2023.12.5 ロープウエイ等の描画を確認した

2023.12.5 タイル描画エラー

今日の修正で基本的なバグが混入した。傾いたタイルが大きくずれた位置に表示される。

Landcoverのレンダリングで起きる。matrixが関係しているようだ。

reset() によって解決した。

2023.12.5 PointF[] getPointArray() と float[] getPoints()

似たメソッドで紛らわしい。多分、後者は、連続する二つの float が点のX座標、Y座標であろう。最初に getPoints を作り、 その後、この方が分かりやすいかと思い getPointArray を作ったのであろう。

simmplfyなどでは PointF[] ではなく float[] を使っているので、とりあえずは、 PointF[] を float[] に変換する。

横浜の港の見える丘公園の海よりに natural=cliff があるが、線は表示されるが、アイコンによるトゲトゲが表示されない。 どこかにバグがある。

タブレットのアイコンファイルを全て消していた。コピーしなおすと正しく描画された。

2023.12.5 テーマパークなどの描画を確認した

drawTextの仕様を少しかえたことの影響か、地名の位置がずれた。また、近くに二つ以上同じ地名が描画されることがある。 多分、バグであろう。

drawPlacename() の引数を少し修正し、標準OSM地図と同じ位置に表示されることを確認した。

2023.12.5 川および川名描画を確認した

2023.12.4 道路名描画を確認した

2023.12.3 中高ズームでの道路・鉄道描画を確認した

2023.12.3 建物の描画を確認した

2023.12.3 ブロックの管理を簡単化した

配列による管理をやめ、二つの変数による管理に変えた。

2023.12.1 しばらくスクロール等を行うとブロックが確保できなくなる

ブロックの解放が行われないようだ。参照カウンタをチェックしよう。

修正したが、少々分かりにくい。レンダリングで使用するブロックの参照カウンタを +1 して、 レンダリングが終われば、参照カウンタを -1 している。全ブロックをチェックするのではなく、 参照が起きたブロック番号を配列に記憶させている。

2023.12.1 地名の描画を確認した

文字はスマホの場合、拡大してから描画できた。

2023.12.1 フェリー航路の描画を確認した

2023.12.1 スマホでの動作確認

描画時間についても特に問題はない。

2023.12.1 行政境界線の破線描画でエラーがおきた

実線は描画できたが、破線の場合、Paintオブジェクト生成でエラーがおきた。

初期化を実行していなかった。

初期化を入れて、エラーは起きなくなり、破線も描画されるようになった。

    java.lang.NullPointerException: Attempt to get length of null array
        at android.graphics.DashPathEffect.(DashPathEffect.java:35)
        at com.example.map3.Renderer.getPaint(Renderer.java:162)

2023.12.1 水域(湖、川)等の描画を確認した

2023.11.30 森林描画を確認した

思ったよりトラブルはなかった。現時点では、inner polygonは無視している。また、葉っぱアイコンは描画していない。

2023.11.30 日本地図領域線の描画を確認した

2023.11.30 タイル境界線の描画を確認した

2023.11.30 陸地の描画を完成した

リファレンス

[1] 世界は1枚の画像から : グーグルマップのしくみを探る(1)
[2] 画像を拡大縮小する方法