トップ地図アプリMap > 地図アプリの基本技術

地図アプリの基本技術

はじめに

アルゴリズムとか地図システム基礎技術と重なるものもあるが、 地図アプリのベースとなる基本技術をここにまとめておく。

タイルの識別

タイルの管理や検索を効率よく行うために、

タイルは一つの 8バイト整数で識別する。

地図ソース番号(src)、zoom、x、y は全て非負の整数である。src、zoom に1バイト、x、y に3バイト割り当てる。

地図ソースデータが不要なときは、このパートを 0 とする。

 long key(Tile.Source src, int zoom, int x, int y) {
     return  (((long)src.ordinal())<<56) + (((long)zoom)<<48) + (((long)x)<<24) + y;
 }

タイル(zoom, x, y)の原点(左上=北西)のXY平面座標

自作地図アプリでは世界XY平面座標は原点(左上)が (0, 0)、右下が (230, 230) である。

このことから、タイル(zoom, x, y)の原点(左上)の世界XY平面座標は (x*230-zoom, y*230-zoom) となる。

プログラムでは x*(1<<(30-zoom))、 y*(1<<(30-zoom)) である。 x / (1<タイル(zoom, x, y)とレコード境界ボックスとの交差矩形

タイル単位のレンダリングではタイル矩形と レンダリング対象のレコード境界ボックス(矩形)が交差する範囲内でレンダリングが生じる。 このタイル矩形 rectTile とレコード矩形 rectRecord の交差範囲をレンダリング前に求めておくと、効率的にレンダリングが行える。

Android Java の場合は、次のようにすれば、交差矩形 rect を求められる。 交差する部分があれば fIntersect が true となり、rect にその交差矩形の値がセットされる。 交差範囲がなかった場合は fIntersect は false となり、rect の値は最初にセットした rectTile と同じ値のままである (幅、高さが0になるわけではない)。

  Rect rect = new Rect(rectTile);
  boolean fIntersect = rect.intersect(rectRecord);

タイル(zoom, x, y)の矩形 rectTile は次のようにして得られる。

 Rect rectTile = new Rect(x*(1<<(30-zoom)), y*(1<<(30-zoom), (x+1)*(1<<(30-zoom)), (y+1)*(1<<(30-zoom)); 

一方、OSMバイナリレコードの境界ボックスはレコードのデータから得られる。 左、上、右、下を x0, y0, x1, y1 とすれば

 Rect rectRecord = new Rect(x0, y0, x1, y1); 

タイル単位のレンダリングでは、現在レンダリング中のタイルの左上を原点とする相対座標値を得る必要がある。 また、単位は画素単位にする必要がある。

タイルの左上座標は x*(1<<(30-zoom)), y*(1<<(30-zoom)) である。タイルの縦横サイズは (1<<(30-zoom)) である。 これを 256(画素) とすればよい。

したがって、left に対しては (left - x*(1<<(30-zoom))) * 256 / (1<<(30-zoom)) が求める相対座標値である。 right についても同様である。

top については (top - y*(1<<(30-zoom))) * 256 / (1<<(30-zoom)) が求める相対座標値である。 bottom についても同様である。

最初から相対座標を使う場合

説明は長くなったが、最初から相対座標を使えば、プログラム的には以下に示すように簡単となる。

  double fact = 1.0 / (1 << (22-zoom));
  RectF rectRecord = new RectF( (float)(x0*fact - x*256), (float)(y0*fact - y*256), 
                                (float)(x1*fact - x*256), (float)(y1*fact - y*256) );
  RectF rect = new RectF(0, 0, 256, 256); 
  boolean fIntersect = rect.intersect(rectRecord);