TileProvider使用本地磁贴

时间:2013-02-09 04:06:30

标签: android google-maps android-maps-v2

我想使用最新Android Maps API(v2)的新TileProvider功能覆盖GoogleMap上的一些自定义图块。但是由于我的用户很多时候都不会上网,我想把瓷砖存储在设备上的zipfile /文件夹结构中。我将使用Maptilergeotiffs生成我的图块。我的问题是:

  1. 将瓷砖存放在设备上的最佳方法是什么?
  2. 我如何创建一个返回本地图块的TileProvider?

2 个答案:

答案 0 :(得分:170)

  1. 您可以将磁贴放入资源文件夹(如果可以接受应用程序大小),或者在第一次启动时将它们全部下载并将它们放入设备存储(SD卡)。

  2. 您可以像这样实现TileProvider:


  3. public class CustomMapTileProvider implements TileProvider {
        private static final int TILE_WIDTH = 256;
        private static final int TILE_HEIGHT = 256;
        private static final int BUFFER_SIZE = 16 * 1024;
    
        private AssetManager mAssets;
    
        public CustomMapTileProvider(AssetManager assets) {
            mAssets = assets;
        }
    
        @Override
        public Tile getTile(int x, int y, int zoom) {
            byte[] image = readTileImage(x, y, zoom);
            return image == null ? null : new Tile(TILE_WIDTH, TILE_HEIGHT, image);
        }
    
        private byte[] readTileImage(int x, int y, int zoom) {
            InputStream in = null;
            ByteArrayOutputStream buffer = null;
    
            try {
                in = mAssets.open(getTileFilename(x, y, zoom));
                buffer = new ByteArrayOutputStream();
    
                int nRead;
                byte[] data = new byte[BUFFER_SIZE];
    
                while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) {
                    buffer.write(data, 0, nRead);
                }
                buffer.flush();
    
                return buffer.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            } catch (OutOfMemoryError e) {
                e.printStackTrace();
                return null;
            } finally {
                if (in != null) try { in.close(); } catch (Exception ignored) {}
                if (buffer != null) try { buffer.close(); } catch (Exception ignored) {}
            }
        }
    
        private String getTileFilename(int x, int y, int zoom) {
            return "map/" + zoom + '/' + x + '/' + y + ".png";
        }
    }
    

    现在您可以将它与GoogleMap实例一起使用:

    private void setUpMap() {
        mMap.setMapType(GoogleMap.MAP_TYPE_NONE);
    
        mMap.addTileOverlay(new TileOverlayOptions().tileProvider(new CustomMapTileProvider(getResources().getAssets())));
    
        CameraUpdate upd = CameraUpdateFactory.newLatLngZoom(new LatLng(LAT, LON), ZOOM);
        mMap.moveCamera(upd);
    }
    

    在我的情况下,我也遇到了MapTiler生成的tile的y坐标问题,但我通过将此方法添加到CustomMapTileProvider来管理它:

    /**
     * Fixing tile's y index (reversing order)
     */
    private int fixYCoordinate(int y, int zoom) {
        int size = 1 << zoom; // size = 2^zoom
        return size - 1 - y;
    }
    

    并从getTile()方法调用它:

    @Override
    public Tile getTile(int x, int y, int zoom) {
        y = fixYCoordinate(y, zoom);
        ...
    }
    

    <强> [UPD]

    如果您知道自定义地图的exac区域,则应从NO_TILE方法返回getTile(...)个缺失的图块。

    我就这样做了:

    private static final SparseArray<Rect> TILE_ZOOMS = new SparseArray<Rect>() {{
        put(8,  new Rect(135,  180,  135,  181 ));
        put(9,  new Rect(270,  361,  271,  363 ));
        put(10, new Rect(541,  723,  543,  726 ));
        put(11, new Rect(1082, 1447, 1086, 1452));
        put(12, new Rect(2165, 2894, 2172, 2905));
        put(13, new Rect(4330, 5789, 4345, 5810));
        put(14, new Rect(8661, 11578, 8691, 11621));
    }};
    
    @Override
    public Tile getTile(int x, int y, int zoom) {
        y = fixYCoordinate(y, zoom);
    
        if (hasTile(x, y, zoom)) {
            byte[] image = readTileImage(x, y, zoom);
            return image == null ? null : new Tile(TILE_WIDTH, TILE_HEIGHT, image);
        } else {
            return NO_TILE;
        }
    }
    
    private boolean hasTile(int x, int y, int zoom) {
        Rect b = TILE_ZOOMS.get(zoom);
        return b == null ? false : (b.left <= x && x <= b.right && b.top <= y && y <= b.bottom);
    }
    

答案 1 :(得分:8)

在新API(v2)中添加自定义tile提供程序的可能性很大,但是您提到您的用户大部分都处于脱机状态。如果用户在首次启动应用程序时处于脱机状态,则无法使用新API,因为它需要用户联机(至少一次构建缓存) - 否则它将只显示黑屏。

编辑2 / 22-14: 我最近又遇到了同样的问题 - 为必须脱机工作的应用程序提供自定义磁贴。通过在初始视图中添加一个不可见(w / h 0/0)mapview来解决它,客户端必须下载一些内容。这似乎有效,并允许我稍后在离线模式下使用mapview。