Ich möchte die neue TileProvider
Funktionalität der neuesten Android Maps API (v2) verwenden, um einige benutzerdefinierte Kacheln auf dem zu überlagern GoogleMap
. Da meine Benutzer jedoch häufig nicht über das Internet verfügen, möchte ich die Kacheln in einer Zipfile- / Ordnerstruktur auf dem Gerät speichern. Ich werde meine Kacheln mit Maptiler
mit generieren geotiffs
. Meine Fragen sind:
- Was wäre der beste Weg, um die Kacheln auf dem Gerät zu speichern?
- Wie würde ich einen TileProvider erstellen, der lokale Kacheln zurückgibt?
android
google-maps
android-maps-v2
Gyroskop
quelle
quelle
Antworten:
Sie können Kacheln in den Assets-Ordner legen (sofern dies für die App-Größe akzeptabel ist) oder sie alle beim ersten Start herunterladen und in den Gerätespeicher (SD-Karte) legen.
Sie können TileProvider folgendermaßen implementieren:
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"; } }
Und jetzt können Sie es mit Ihrer GoogleMap-Instanz verwenden:
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); }
In meinem Fall hatte ich auch ein Problem mit der y-Koordinate der von MapTiler generierten Kacheln, aber ich habe es durch Hinzufügen dieser Methode zu CustomMapTileProvider verwaltet:
/** * 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; }
und rufen Sie es von der getTile () -Methode wie folgt auf:
@Override public Tile getTile(int x, int y, int zoom) { y = fixYCoordinate(y, zoom); ... }
[Upd]
Wenn Sie den genauen Bereich Ihrer benutzerdefinierten Karte kennen, sollten Sie
NO_TILE
für fehlende Kacheln aus dergetTile(...)
Methode zurückkehren.So habe ich es gemacht:
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); }
quelle
Die Möglichkeit, benutzerdefinierte Kachelanbieter in die neue API (v2) aufzunehmen, ist großartig. Sie erwähnen jedoch, dass Ihre Benutzer größtenteils offline sind. Wenn ein Benutzer beim ersten Start der Anwendung offline ist, können Sie die neue API nicht verwenden, da der Benutzer online sein muss (mindestens einmal, um einen Cache zu erstellen, wie es scheint). Andernfalls wird nur ein schwarzer Bildschirm angezeigt.
EDIT 2 / 22-14: Ich bin kürzlich wieder auf dasselbe Problem gestoßen - mit benutzerdefinierten Kacheln für eine App, die offline funktionieren musste. Es wurde behoben, indem einer unsichtbaren Ansicht, in der der Client Inhalte herunterladen musste, eine unsichtbare Kartenansicht (mit 0/0) hinzugefügt wurde. Dies scheint zu funktionieren und ermöglicht mir später, eine Kartenansicht im Offline-Modus zu verwenden.
quelle