Ich möchte die neue verwenden TileProvider
Funktionalität der neuesten Android Maps API (v2), um einige benutzerdefinierte Kacheln auf der zu überlagern GoogleMap
. Da meine Benutzer jedoch die meiste Zeit nicht über das Internet verfügen, möchte ich die Kacheln in einer ZIP-Datei/Ordnerstruktur auf dem Gerät speichern. Ich werde meine Kacheln mit generieren Maptiler
mit geotiffs
. Meine Fragen sind:
- Was wäre der beste Weg, um die Kacheln auf dem Gerät zu speichern?
- Wie würde ich vorgehen, um einen TileProvider zu erstellen, der lokale Kacheln zurückgibt?
-
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 im Gerätespeicher (SD-Karte) ablegen.
-
Sie können TileProvider wie folgt 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 + "https://stackoverflow.com/" + x + "https://stackoverflow.com/" + 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 geschafft, indem ich diese Methode zu CustomMapTileProvider hinzugefügt habe:
/**
* 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 Methode getTile () 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 zurückkehren NO_TILE
für fehlende Kacheln aus getTile(...)
Methode.
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);
}
Die Möglichkeit, benutzerdefinierte Tileprovider in der neuen API (v2) hinzuzufügen, ist großartig, aber Sie erwähnen, dass Ihre Benutzer meistens 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 (anscheinend mindestens einmal, um einen Cache zu erstellen) – andernfalls wird nur ein schwarzer Bildschirm angezeigt.
BEARBEITEN 22.02.14: Ich bin kürzlich wieder auf das gleiche Problem gestoßen – mit benutzerdefinierten Kacheln für eine App, die offline funktionieren musste. Gelöst wurde es durch Hinzufügen einer unsichtbaren (w/h 0/0) Kartenansicht zu einer anfänglichen Ansicht, in der der Client einige Inhalte herunterladen musste. Dies scheint zu funktionieren und ermöglicht es mir, später eine Kartenansicht im Offline-Modus zu verwenden.
So habe ich das in Kotlin implementiert:
class LocalTileProvider : TileProvider
{
override fun getTile(x: Int, y: Int, zoom: Int): Tile?
{
// This is for my case only
if (zoom > 11)
return TileProvider.NO_TILE
val path = "${getImagesFolder()}/tiles/$zoom/$x/$y/filled.png"
val file = File(path)
if (!file.exists())
return TileProvider.NO_TILE
return try {
Tile(TILE_SIZE, TILE_SIZE, file.readBytes())
}
catch (e: Exception)
{
TileProvider.NO_TILE
}
}
companion object {
const val TILE_SIZE = 512
}
}
Außerdem würde ich gerne eine Sache wissen: Bietet Google Map V2 eine Möglichkeit zum Herunterladen/Zwischenspeichern der Kachel? (prntscr.com/3cyiqf) b’cos Ich bin in diesem Fall verwirrt, bedeutet, wenn sie angeben, wie Kacheln mit der TileProvider-Klasse geladen / verwendet werden, sollte es etwas für das Caching / Herunterladen von Kacheln verfügbar sein. Meine eigentliche Anforderung ist, dass ich die Karte gemäß den Anforderungen des Benutzers herunterladen / zwischenspeichern muss. Ich habe bereits OSMDROID lib überprüft, möchte aber nur Google Map v2 verwenden
– Rajan
25. April 2014 um 7:15 Uhr
@Rajan Ich bin auf das gleiche Problem gestoßen. Es scheint möglich zu sein, tileProvide für das Caching zu verwenden. Wofür haben Sie sich entschieden?
– ar-g
30. Oktober 2014 um 11:07 Uhr
@Gyroscope, woher hast du diese Kacheln? stehen sie zum download bereit?
– Heshan Sandeepa
23. Mai 2016 um 17:39 Uhr