OSMや国土地理院地図などラスター型のタイル地図は zoom 15、16 といった整数ズーム値のタイル画像ファイルしか 存在しない。zoom 15.25 とか 15.75 などは zoom 15 のタイル画像を拡大するか、zoom 16 のタイル画像を縮小することに より実現する。
zoom 15 から zoom 16 にズームインするときは、いきなり、zoom 15 のタイルから zoom 16 のタイルに切り替えるのではなく、 zoom 15 のタイル画像を拡大して zoom 15.25、15.5、15.75、16 を実現する。 zoom 16 のタイルの読み込みまたはレンダリング/ダウンロードの要求は、zoom 15 から zoom 15.25 に切り替える時点で発行しておく。 これにより、zoom 16 に達したときには、通常は、zoom 16 のタイル画像がタイルキャッシュに読み込まれている。 したがって、zoom 15 のタイルを2倍に拡大するのではなく、zoom 16 のタイル画像を表示する。
逆に、zoom 16 から zoom 15 にズームアウトする時には、通常は zoom 15 のタイル画像はキャッシュに存在しないため、 zoom 16 のタイルを少しずつ縮小して、zoom 15.75、15.5、15.25 を実現する。zoom 15 に達したときには zoom 15 のタイル画像が準備できているであろう。
zoom 16 から zoom 15 にズームアウトするとき、もし、タイルキャッシュに zoom 15 のタイル画像があるならば、 zoom 16 のタイル画像の縮小ではなく、zoom 15 のタイル画像の拡大の方がいいだろう。
zoom 15.25、15.5、15.75、16 になったとき、zoom 16 のタイルがまだ準備できていなかったときは、zoom 15 を 二倍に拡大したものでもよいが、さらに、zoom 16.25、16.5 となったときどうするかは未定である。 zoom 15 のタイル画像の拡大を続けると、画像がぼやける。
実際のところはこの機能を実装してから判断する。
ネット検索によると、例えば、「zoom 15.8 の場合、約1.74倍 (20.8倍) に拡大する」 となっている。したがって、 .25、.5、.75 に相当する倍率は 20.25(1.189)、20.5(1.414)、20.75(1.682) とする。
現在のプログラムを下に示す。
小数点zoom をサポートする場合、PX に倍率をかける。 また、Loaderへのリクエストの出し方を修正する必要がある。
例えば、zoom 15 から zoom 15.25 に変わったときには、zoom 16に対するリクエストを出す。 zoom 15.25、15.5、15.75 からの変更ではリクエストは出さない。
ズームアウトでは整数から .75 に変わったときにリクエストを出す。
タイルキャッシュには上限があるため、いざ、拡大あるいは縮小しょうとするとき、 元にするタイルがないケースがあるかも知れない。 もしこのような事態が起きた場合、対策は、そのとき考える。 キャッシュサイズは十分大きい値にしているため、おそらく、このケースはないだろう。
拡大は問題ないが、zoom 16 から 15 への縮小では、これに必要な zoom 16 のタイルがないケースが起こりうる。 15.75 では問題ないが、15.5 や 15.25 では該当するタイルがないケースが起こりうる。
16 から 15.75 に変更する時点で zoom 15 のリクエストを出しておくため、15.75 だけは zoom 16 を使い、 15.5、15.25 は zoom 15 の拡大で対応すべきか。
こう考えると、レンダリングには時間がかかるため、なめらかな拡大縮小は簡単ではない。
int offX = (CX - W / 2) % PX;
int offY = (CY - H / 2) % PX;
bx = (int) ((CX - W / 2.0) / PX); // 画面左端タイルX座標
ex = (int) ((CX + W / 2.0 + PX - 1) / PX); // 画面右端タイルX座標
by = (int) ((CY - H / 2.0) / PX);
ey = (int) ((CY + H / 2.0 + PX - 1) / PX);
int MAX = 1 << zoom;
for (int xx = bx-1; xx < ex+1; xx++) {
int x = (xx + MAX) % MAX;
for (int y = by-1; y < ey+1; y++) {
drawTile(canvas, src, zoom, x, y, bx, by, offX, offY);
}
}
void drawTile(Canvas canvas, Tile.Source src, int zoom, int x, int y,
int bx, int by, int offX, int offY) {
int MAX = 1 << zoom;
int tx = (x + MAX) % MAX;
long key = Map.key(src, zoom, x, y);
Tile tile = Tile.getCache(key);
if (tile != null && tile.bmp != null) {
drawBitmap(canvas, tile.bmp, PX * (tx - bx) - offX, PX * (y - by) - offY);
} else if (tile == null) {
//System.out.printf("Req: %d-%d-%d %d\n", zoom, x, y, Tile.queRequest.size());
tile = new Tile(src, zoom, x, y, key);
Tile.putCache(tile); // tile.bmp は null
try {
Loader.queRequest.putFirst(tile); // 先頭に挿入する
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
void drawBitmap(Canvas canvas, Bitmap bmp, int xoff, int yoff) {
if (src == Tile.Source.gsi || src == Tile.Source.ort) {
rSrc.set(xoff < 0 ? (int) (-xoff / Scale) : 0, yoff < 0 ? (int) (-yoff / Scale) : 0,
xoff / Scale + 256 < W / Scale ? 256 : (int) ((W - xoff) / Scale),
yoff / Scale + 256 < H / Scale ? 256 : (int) ((H - yoff) / Scale));
} else {
rSrc.set(xoff < 0 ? -xoff : 0, yoff < 0 ? -yoff : 0,
xoff + PX < W ? PX : (W - xoff),
yoff + PX < H ? PX : (H - yoff));
}
rDst.set(Math.max(xoff, 0), Math.max(yoff, 0),
Math.min(xoff + PX, W), Math.min(yoff + PX, H));
canvas.drawBitmap(bmp, rSrc, rDst, paintCanvas);
}