android - studio - TileProvider usando fichas locales
mapview android studio (2)
Me gustaría utilizar la nueva funcionalidad TileProvider
de la última API de Android Maps (v2) para superponer algunas fichas personalizadas en GoogleMap
. Sin embargo, como mis usuarios no tendrán acceso a Internet la mayor parte del tiempo, quiero mantener los mosaicos almacenados en una estructura de archivo zip / carpeta en el dispositivo. Maptiler
mis fichas usando Maptiler
con geotiffs
. Mis preguntas son:
- ¿Cuál sería la mejor manera de almacenar las fichas en el dispositivo?
- ¿Cómo podría crear un TileProvider que devuelva fichas locales?
Puede poner fichas en la carpeta de activos (si es aceptable para el tamaño de la aplicación) o descargarlas todas al primer inicio y colocarlas en el almacenamiento del dispositivo (tarjeta SD).
Puede implementar TileProvider de esta manera:
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";
}
}
Y ahora puedes usarlo con tu instancia de 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);
}
En mi caso, también tuve un problema con la coordenada y de los mosaicos generados por MapTiler, pero lo logré agregando este método en 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;
}
y llámalo del método getTile () como este:
@Override
public Tile getTile(int x, int y, int zoom) {
y = fixYCoordinate(y, zoom);
...
}
[Upd]
Si conoce el área exac de su mapa personalizado, debe devolver NO_TILE
para las NO_TILE
faltantes del getTile(...)
.
Así es como lo hice:
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);
}
La posibilidad de agregar proveedores personalizados de azulejos en la nueva API (v2) es excelente, sin embargo, usted menciona que sus usuarios están mayormente desconectados. Si un usuario está fuera de línea cuando inicia la aplicación por primera vez, no puede usar la API nueva, ya que requiere que el usuario esté en línea (al menos una vez para construir una memoria caché); de lo contrario, solo se mostrará una pantalla negra.
EDITAR 22 / 22-14: Recientemente volví a encontrar el mismo problema: tener fichas personalizadas para una aplicación que tenía que funcionar sin conexión. Lo resolvió agregando una vista de mapa invisible (w / h 0/0) a una vista inicial donde el cliente tenía que descargar cierto contenido. Esto parece funcionar, y me permite usar una vista de mapa en modo fuera de línea más adelante.