MBTiles vector tile source (#740)
Support offline vector maps (MBTiles, MVT, gzipped pbf)
This commit is contained in:
committed by
Emux
parent
83aed13683
commit
a9e18a2add
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright 2019 Andrea Antonello
|
||||
* Copyright 2019 devemux86
|
||||
* Copyright 2019 Kostas Tzounopoulos
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@@ -16,144 +17,70 @@
|
||||
package org.oscim.android.tiling.source.mbtiles;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import org.oscim.backend.CanvasAdapter;
|
||||
import org.oscim.backend.canvas.Bitmap;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.map.Viewport;
|
||||
import org.oscim.tiling.ITileDataSink;
|
||||
import org.oscim.tiling.ITileDataSource;
|
||||
import org.oscim.tiling.QueryResult;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A tile data source for MBTiles raster databases.
|
||||
*/
|
||||
public class MBTilesBitmapTileDataSource implements ITileDataSource {
|
||||
public class MBTilesBitmapTileDataSource extends MBTilesTileDataSource {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MBTilesBitmapTileDataSource.class);
|
||||
|
||||
private static final String TABLE_TILES = "tiles";
|
||||
private static final String COL_TILES_ZOOM_LEVEL = "zoom_level";
|
||||
private static final String COL_TILES_TILE_COLUMN = "tile_column";
|
||||
private static final String COL_TILES_TILE_ROW = "tile_row";
|
||||
private static final String COL_TILES_TILE_DATA = "tile_data";
|
||||
private static final String SELECT_TILES = "SELECT " + COL_TILES_TILE_DATA + " from " + TABLE_TILES + " where "
|
||||
+ COL_TILES_ZOOM_LEVEL + "=? AND " + COL_TILES_TILE_COLUMN + "=? AND " + COL_TILES_TILE_ROW + "=?";
|
||||
|
||||
private static final String TABLE_METADATA = "metadata";
|
||||
private static final String COL_METADATA_NAME = "name";
|
||||
private static final String COL_METADATA_VALUE = "value";
|
||||
private static final String SELECT_METADATA = "select " + COL_METADATA_NAME + "," + COL_METADATA_VALUE + " from "
|
||||
+ TABLE_METADATA;
|
||||
private static final List<String> SUPPORTED_FORMATS = Arrays.asList("png", "jpg", "jpeg");
|
||||
|
||||
private final Integer mAlpha;
|
||||
private final SQLiteDatabase mDatabase;
|
||||
private Map<String, String> mMetadata;
|
||||
private final Integer mTransparentColor;
|
||||
|
||||
/**
|
||||
* Create a MBTiles tile data source.
|
||||
* Create a tile data source for MBTiles raster databases.
|
||||
*
|
||||
* @param path the path to the MBTiles database.
|
||||
* @param alpha an optional alpha value [0-255] to make the tiles transparent.
|
||||
* @param transparentColor an optional color that will be made transparent in the bitmap.
|
||||
*/
|
||||
MBTilesBitmapTileDataSource(String path, Integer alpha, Integer transparentColor) {
|
||||
mDatabase = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
|
||||
public MBTilesBitmapTileDataSource(String path, Integer alpha, Integer transparentColor) {
|
||||
super(path);
|
||||
mAlpha = alpha;
|
||||
mTransparentColor = transparentColor;
|
||||
|
||||
try {
|
||||
assertDatabaseFormat();
|
||||
} catch (MBTilesUnsupportedException e) {
|
||||
log.error("Invalid MBTiles database", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
mDatabase.close();
|
||||
if (mDatabase != null && mDatabase.isOpen())
|
||||
mDatabase.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
mDatabase.close();
|
||||
if (mDatabase != null && mDatabase.isOpen())
|
||||
mDatabase.close();
|
||||
}
|
||||
|
||||
String getAttribution() {
|
||||
return getMetadata().get("attribution");
|
||||
}
|
||||
|
||||
BoundingBox getBounds() {
|
||||
String bounds = getMetadata().get("bounds");
|
||||
if (bounds != null) {
|
||||
String[] split = bounds.split(",");
|
||||
double w = Double.parseDouble(split[0]);
|
||||
double s = Double.parseDouble(split[1]);
|
||||
double e = Double.parseDouble(split[2]);
|
||||
double n = Double.parseDouble(split[3]);
|
||||
return new BoundingBox(s, w, n, e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return getMetadata().get("description");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the image format (jpg, png)
|
||||
*/
|
||||
public String getFormat() {
|
||||
return getMetadata().get("format");
|
||||
}
|
||||
|
||||
int getMaxZoom() {
|
||||
String maxZoom = getMetadata().get("maxzoom");
|
||||
if (maxZoom != null)
|
||||
return Integer.parseInt(maxZoom);
|
||||
return Viewport.MAX_ZOOM_LEVEL;
|
||||
}
|
||||
|
||||
private Map<String, String> getMetadata() {
|
||||
if (mMetadata == null) {
|
||||
mMetadata = new HashMap<>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = mDatabase.rawQuery(SELECT_METADATA, null);
|
||||
while (cursor.moveToNext()) {
|
||||
String key = cursor.getString(0);
|
||||
String value = cursor.getString(1);
|
||||
mMetadata.put(key, value);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return mMetadata;
|
||||
}
|
||||
|
||||
int getMinZoom() {
|
||||
String minZoom = getMetadata().get("minzoom");
|
||||
if (minZoom != null)
|
||||
return Integer.parseInt(minZoom);
|
||||
return Viewport.MIN_ZOOM_LEVEL;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return getMetadata().get("name");
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return getMetadata().get("version");
|
||||
@Override
|
||||
public List<String> getSupportedFormats() {
|
||||
return SUPPORTED_FORMATS;
|
||||
}
|
||||
|
||||
private static android.graphics.Bitmap processAlpha(android.graphics.Bitmap bitmap, int alpha) {
|
||||
@@ -216,9 +143,9 @@ public class MBTilesBitmapTileDataSource implements ITileDataSource {
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
long tmsTileY = MercatorProjection.tileYToTMS(tileY, zoomLevel);
|
||||
cursor = mDatabase.rawQuery(SELECT_TILES, new String[]{String.valueOf(zoomLevel), String.valueOf(tileX), String.valueOf(tmsTileY)});
|
||||
cursor = mDatabase.rawQuery(String.format(MBTilesTileDataSource.SELECT_TILES_FORMAT, MBTilesTileDataSource.WHERE_FORMAT), new String[]{String.valueOf(zoomLevel), String.valueOf(tileX), String.valueOf(tmsTileY)});
|
||||
if (cursor.moveToFirst())
|
||||
return cursor.getBlob(0);
|
||||
return cursor.getBlob(cursor.getColumnIndexOrThrow("tile_data"));
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright 2019 Andrea Antonello
|
||||
* Copyright 2019 devemux86
|
||||
* Copyright 2019 Kostas Tzounopoulos
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
@@ -15,19 +16,13 @@
|
||||
*/
|
||||
package org.oscim.android.tiling.source.mbtiles;
|
||||
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.tiling.ITileDataSource;
|
||||
import org.oscim.tiling.TileSource;
|
||||
|
||||
/**
|
||||
* A tile source for MBTiles raster databases.
|
||||
*/
|
||||
public class MBTilesBitmapTileSource extends TileSource {
|
||||
|
||||
private final MBTilesBitmapTileDataSource mTileDataSource;
|
||||
public class MBTilesBitmapTileSource extends MBTilesTileSource {
|
||||
|
||||
/**
|
||||
* Create a MBTiles tile source.
|
||||
* Create a tile source for MBTiles raster databases.
|
||||
*
|
||||
* @param path the path to the MBTiles database.
|
||||
*/
|
||||
@@ -36,64 +31,13 @@ public class MBTilesBitmapTileSource extends TileSource {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MBTiles tile source.
|
||||
* Create a tile source for MBTiles raster databases.
|
||||
*
|
||||
* @param path the path to the MBTiles database.
|
||||
* @param alpha an optional alpha value [0-255] to make the tiles transparent.
|
||||
* @param transparentColor an optional color that will be made transparent in the bitmap.
|
||||
*/
|
||||
public MBTilesBitmapTileSource(String path, Integer alpha, Integer transparentColor) {
|
||||
mTileDataSource = new MBTilesBitmapTileDataSource(path, alpha, transparentColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
mTileDataSource.dispose();
|
||||
}
|
||||
|
||||
|
||||
public String getAttribution() {
|
||||
return mTileDataSource.getAttribution();
|
||||
}
|
||||
|
||||
public BoundingBox getBounds() {
|
||||
return mTileDataSource.getBounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ITileDataSource getDataSource() {
|
||||
return mTileDataSource;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mTileDataSource.getDescription();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the image format (jpg, png)
|
||||
*/
|
||||
public String getFormat() {
|
||||
return mTileDataSource.getFormat();
|
||||
}
|
||||
|
||||
public int getMaxZoom() {
|
||||
return mTileDataSource.getMaxZoom();
|
||||
}
|
||||
|
||||
public int getMinZoom() {
|
||||
return mTileDataSource.getMinZoom();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mTileDataSource.getName();
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return mTileDataSource.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenResult open() {
|
||||
return OpenResult.SUCCESS;
|
||||
super(new MBTilesBitmapTileDataSource(path, alpha, transparentColor));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 2019 Andrea Antonello
|
||||
* Copyright 2019 devemux86
|
||||
* Copyright 2019 Kostas Tzounopoulos
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.tiling.source.mbtiles;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.text.TextUtils;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.map.Viewport;
|
||||
import org.oscim.tiling.ITileDataSource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A tile data source for MBTiles databases.
|
||||
*/
|
||||
public abstract class MBTilesTileDataSource implements ITileDataSource {
|
||||
|
||||
private static final String SELECT_METADATA = "SELECT name, value FROM metadata";
|
||||
protected static final String SELECT_TILES_FORMAT =
|
||||
"SELECT zoom_level, tile_column, tile_row, tile_data " +
|
||||
"FROM tiles " +
|
||||
"WHERE %s " +
|
||||
"ORDER BY zoom_level DESC " +
|
||||
"LIMIT 1";
|
||||
static final String WHERE_FORMAT = "zoom_level=? AND tile_column=? AND tile_row=?";
|
||||
|
||||
protected final SQLiteDatabase mDatabase;
|
||||
private Map<String, String> mMetadata;
|
||||
|
||||
public MBTilesTileDataSource(String path) {
|
||||
mDatabase = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY);
|
||||
}
|
||||
|
||||
protected void assertDatabaseFormat() {
|
||||
String format = getFormat();
|
||||
|
||||
if (format == null)
|
||||
throw new RuntimeException("'metadata.format' field was not found. Is this an MBTiles database?");
|
||||
|
||||
List<String> supportedFormats = getSupportedFormats();
|
||||
if (!supportedFormats.contains(format))
|
||||
throw new MBTilesUnsupportedException(String.format("Unsupported MBTiles 'metadata.format: %s'. Supported format(s) are: %s", format, TextUtils.join(", ", supportedFormats)));
|
||||
}
|
||||
|
||||
public String getAttribution() {
|
||||
return getMetadata().get("attribution");
|
||||
}
|
||||
|
||||
public BoundingBox getBounds() {
|
||||
String bounds = getMetadata().get("bounds");
|
||||
if (bounds == null)
|
||||
return null;
|
||||
String[] split = bounds.split(",");
|
||||
double w = Double.parseDouble(split[0]);
|
||||
double s = Double.parseDouble(split[1]);
|
||||
double e = Double.parseDouble(split[2]);
|
||||
double n = Double.parseDouble(split[3]);
|
||||
return new BoundingBox(s, w, n, e);
|
||||
}
|
||||
|
||||
public MapPosition getCenter() {
|
||||
String center = getMetadata().get("center");
|
||||
if (center == null)
|
||||
return null;
|
||||
String[] split = center.split(",");
|
||||
double latitude = Double.parseDouble(split[1]);
|
||||
double longitude = Double.parseDouble(split[0]);
|
||||
int zoomLevel = Integer.parseInt(split[2]);
|
||||
return new MapPosition(latitude, longitude, 1 << zoomLevel);
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return getMetadata().get("description");
|
||||
}
|
||||
|
||||
public String getFormat() {
|
||||
return getMetadata().get("format");
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return getMetadata().get("id");
|
||||
}
|
||||
|
||||
public String getJson() {
|
||||
return getMetadata().get("json");
|
||||
}
|
||||
|
||||
public int getMaxZoom() {
|
||||
String maxZoom = getMetadata().get("maxzoom");
|
||||
return maxZoom != null ? Integer.parseInt(maxZoom) : Viewport.MAX_ZOOM_LEVEL;
|
||||
}
|
||||
|
||||
private Map<String, String> getMetadata() {
|
||||
if (mMetadata == null) {
|
||||
mMetadata = new HashMap<>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = mDatabase.rawQuery(SELECT_METADATA, null);
|
||||
while (cursor.moveToNext()) {
|
||||
String key = cursor.getString(0);
|
||||
String value = cursor.getString(1);
|
||||
mMetadata.put(key, value);
|
||||
}
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return mMetadata;
|
||||
}
|
||||
|
||||
public int getMinZoom() {
|
||||
String minZoom = getMetadata().get("minzoom");
|
||||
return minZoom != null ? Integer.parseInt(minZoom) : Viewport.MIN_ZOOM_LEVEL;
|
||||
}
|
||||
|
||||
public Long getMTime() {
|
||||
String mTime = getMetadata().get("mtime");
|
||||
return mTime != null ? Long.parseLong(mTime) : null;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return getMetadata().get("name");
|
||||
}
|
||||
|
||||
public Integer getPixelScale() {
|
||||
String pixelScale = getMetadata().get("pixel_scale");
|
||||
return pixelScale != null ? Integer.parseInt(pixelScale) : null;
|
||||
}
|
||||
|
||||
abstract public List<String> getSupportedFormats();
|
||||
|
||||
public String getVersion() {
|
||||
return getMetadata().get("version");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2019 Andrea Antonello
|
||||
* Copyright 2019 devemux86
|
||||
* Copyright 2019 Kostas Tzounopoulos
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.tiling.source.mbtiles;
|
||||
|
||||
import org.oscim.tiling.TileSource;
|
||||
|
||||
/**
|
||||
* A tile source for MBTiles databases.
|
||||
*/
|
||||
public abstract class MBTilesTileSource extends TileSource {
|
||||
|
||||
private final MBTilesTileDataSource mTileDataSource;
|
||||
|
||||
public MBTilesTileSource(MBTilesTileDataSource tileDataSource) {
|
||||
mTileDataSource = tileDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
getDataSource().dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MBTilesTileDataSource getDataSource() {
|
||||
return mTileDataSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenResult open() {
|
||||
return OpenResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2019 Kostas Tzounopoulos
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the
|
||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License along with
|
||||
* this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.oscim.android.tiling.source.mbtiles;
|
||||
|
||||
public class MBTilesUnsupportedException extends RuntimeException {
|
||||
public MBTilesUnsupportedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user