トップ地図アプリMap4 > タイル画像の管理

タイル画像の管理

はじめに

Google Map準拠地図では、zoom 0 では世界地図全体が 256x256画素の一つのタイル画像で表される。 zoom 1では縦横を二分して世界地図が計4枚のタイルで表される。

現在使用中のスマホは高精細なため、タイルのサイズを 768x768画素としている。 256画素かどうかは本質的な話ではない。zoom 0 の世界地図の原点(西北)が (0,0) で、 東南が (1, 1) と考えた方が分かりやすい。

class Tile

Map4 はとりあえず Map3 を踏襲する。特に、スマホでの文字は、256x256画素のタイルを作成して、 それを 768x768画素に拡大するよりも、最初から、768x768画素のタイルを作成する方がきれいな文字となる。

その場合、タイルキャッシュに使われるメモリは9倍になる。 256x256画素では 256KBx64 = 16MB で済むが、768x768画素では 144MB となる。

現在はメモリが逼迫しているわけではないが、キャッシュに 144MB 使うのには抵抗がある。 ひとまずは、キャッシュサイズを 32 としておく。

キャッシュを階層化して、Bitmap は 24~32タイル、png/jpeg を 128~512タイルとすれば、 パフォーマンスは向上するであろう。

public class Tile {
    enum Status { free, waiting, busy, ready }
    enum Source { lands, japan, kanto, hot, gsi, ort }

    final static String GSI = "https://cyberjapandata.gsi.go.jp/xyz/std/";
    final static String ORT = "https://cyberjapandata.gsi.go.jp/xyz/ort/";

    final static Tile[] cache = new Tile[64];
    final static Tile[] cache = new Tile[32];
    static int ixEntry = 0;

    Status status;
    Bitmap bmp;             // PX x PX画素
    Canvas canvas;
    Source src;             // 地図のソース
    int zoom, x, y;         // タイルアドレス
    String url, dir, path;
    long key;

    public Tile() {
        bmp = Bitmap.createBitmap(Map.PX, Map.PX, Bitmap.Config.ARGB_8888);
    }

    static void initialize() {
        for (int i = 0; i < cache.length; i++) {
            cache[i] = new Tile();
            cache[i].status = Status.free;
        }
    }

    void set(Source src, int zoom, int x, int y, long key) {
        this.src = src;
        this.zoom = zoom;
        this.x = x;
        this.y = y;
        this.key = key;     // ユニーク
        this.dir = Map.DIR + "GSI/" + src.name() + "/" + zoom + "/" + x;
        this.path = this.dir + "/" + y + ".jpg";      // download先
        if (src == Source.japan || src == Source.kanto || src == Source.hot) {
            this.url = null;
        } else {
            String zxy = zoom + "/" + x + "/" + y;
            this.url = src == Source.gsi ? GSI + zxy + ".png" : null;
        }
    }

    // サイクリックに使用
    static void alloc(Source src, int zoom, int x, int y, long key) {
        synchronized (cache) {
            for (int n = 0; n < cache.length; n++) {
                Tile tile = cache[ixEntry];
                ixEntry = (ixEntry + 1) & 0x3f;     // キャッシュサイズ=64
                ixEntry = (ixEntry + 1) & 0x1f;     // キャッシュサイズ=32
                if (tile.status != Status.busy &&
                        (tile.status != Status.waiting || tile.zoom != zoom)) {
                    tile.set(src, zoom, x, y, key);
                    tile.status = Status.waiting;
                    break;
                } else {
                    System.out.printf("allocできず: key=%X %s\n", key, src.name());
                }
            }
        }
    }

    static Tile get(long key) {
        synchronized(cache) {
            for (Tile tile : cache) {
                if (tile != null && tile.bmp != null && tile.key == key) {
                    return tile;
                }
            }
            return null;
        }
    }

    static Tile getRequest(Source src, int zoom) {
        synchronized(cache) {
            for (Tile tile : cache) {
                if (tile != null && tile.status == Status.waiting) {
                    if (tile.src == src && tile.zoom == zoom) {
                        return tile;
                    }
                    tile.status = Status.free;
                }
            }
            return null;
        }
    }
}

リファレンス