MBTiles raster tile source (#708)

This commit is contained in:
Andrea Antonello 2019-04-05 09:53:33 +02:00 committed by Emux
parent db6be82ce2
commit 71f94f3f79
No known key found for this signature in database
GPG Key ID: 64ED9980896038C3
7 changed files with 417 additions and 1 deletions

View File

@ -91,6 +91,9 @@
<activity
android:name=".MarkerOverlayActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".MBTilesBitmapTileActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />
<activity
android:name=".MultiMapViewActivity"
android:configChanges="keyboardHidden|orientation|screenSize" />

View File

@ -25,5 +25,8 @@
<string name="search_key">\'*\' or OSM key</string>
<string name="search_value">void or value</string>
<string name="now">Now</string>
<string name="warning">Warning</string>
<string name="startup_message_mbtiles">To run this sample activity, you need any MBTiles with filename test.mbtiles installed on storage.\n\nadb push file.mbtiles /sdcard/test.mbtiles</string>
<string name="exit">Exit</string>
</resources>

View File

@ -0,0 +1,69 @@
/*
* Copyright 2019 Andrea Antonello
* Copyright 2019 devemux86
*
* 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.test;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Environment;
import org.oscim.android.tiling.source.mbtiles.MBTilesBitmapTileSource;
import org.oscim.core.BoundingBox;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.layers.tile.bitmap.BitmapTileLayer;
import java.io.File;
/**
* An example activity making use of MBTiles.
*/
public class MBTilesBitmapTileActivity extends BitmapTileActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
File file = new File(Environment.getExternalStorageDirectory(), "test.mbtiles");
if (!file.exists()) {
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(R.string.warning)
.setMessage(R.string.startup_message_mbtiles)
.setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
});
builder.show();
return;
}
MBTilesBitmapTileSource tileSource = new MBTilesBitmapTileSource(file.getAbsolutePath(), 128, null);
BitmapTileLayer bitmapLayer = new BitmapTileLayer(mMap, tileSource);
mMap.layers().add(bitmapLayer);
/* set initial position on first run */
MapPosition pos = new MapPosition();
mMap.getMapPosition(pos);
if (pos.x == 0.5 && pos.y == 0.5) {
BoundingBox bbox = tileSource.getBounds();
if (bbox != null) {
pos.setByBoundingBox(bbox, Tile.SIZE * 4, Tile.SIZE * 4);
mMap.setMapPosition(pos);
}
}
}
}

View File

@ -8,6 +8,7 @@
* Copyright 2017 nebular
* Copyright 2018 boldtrn
* Copyright 2018-2019 Gustl22
* Copyright 2019 Andrea Antonello
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@ -115,6 +116,7 @@ public class Samples extends Activity {
linearLayout.addView(createLabel("Raster Maps"));
linearLayout.addView(createButton(BitmapTileActivity.class));
linearLayout.addView(createButton(MBTilesBitmapTileActivity.class));
linearLayout.addView(createLabel("Overlays"));
linearLayout.addView(createButton(MarkerOverlayActivity.class));

View File

@ -0,0 +1,228 @@
/*
* Copyright 2019 Andrea Antonello
* Copyright 2019 devemux86
*
* 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.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;
/**
* A tile data source for MBTiles raster databases.
*/
public class MBTilesBitmapTileDataSource implements ITileDataSource {
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 final Integer mAlpha;
private final SQLiteDatabase mDatabase;
private Map<String, String> mMetadata;
private final Integer mTransparentColor;
/**
* Create a MBTiles tile data source.
*
* @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);
mAlpha = alpha;
mTransparentColor = transparentColor;
}
@Override
public void cancel() {
mDatabase.close();
}
@Override
public void dispose() {
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");
}
private static android.graphics.Bitmap processAlpha(android.graphics.Bitmap bitmap, int alpha) {
android.graphics.Bitmap newBitmap = android.graphics.Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), android.graphics.Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(newBitmap);
Paint paint = new Paint();
paint.setAlpha(alpha);
canvas.drawBitmap(bitmap, 0, 0, paint);
return newBitmap;
}
private static android.graphics.Bitmap processTransparentColor(android.graphics.Bitmap bitmap, int colorToRemove) {
int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
for (int i = 0; i < pixels.length; i++) {
if (pixels[i] == colorToRemove)
pixels[i] = Color.alpha(Color.TRANSPARENT);
}
android.graphics.Bitmap newBitmap = android.graphics.Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), android.graphics.Bitmap.Config.ARGB_8888);
newBitmap.setPixels(pixels, 0, newBitmap.getWidth(), 0, 0, newBitmap.getWidth(), newBitmap.getHeight());
return newBitmap;
}
@Override
public void query(MapTile tile, ITileDataSink sink) {
QueryResult res = QueryResult.FAILED;
try {
byte[] bytes = readTile(tile.tileX, tile.tileY, tile.zoomLevel);
if (mTransparentColor != null || mAlpha != null) {
android.graphics.Bitmap androidBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (mTransparentColor != null)
androidBitmap = processTransparentColor(androidBitmap, mTransparentColor);
if (mAlpha != null)
androidBitmap = processAlpha(androidBitmap, mAlpha);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
androidBitmap.compress(android.graphics.Bitmap.CompressFormat.PNG, 100, bos);
bytes = bos.toByteArray();
}
Bitmap bitmap = CanvasAdapter.decodeBitmap(new ByteArrayInputStream(bytes));
sink.setTileImage(bitmap);
res = QueryResult.SUCCESS;
} catch (Exception e) {
log.debug("{} invalid bitmap", tile);
} finally {
sink.completed(res);
}
}
/**
* Read a Tile's image bytes from the MBTiles database.
*
* @param tileX the x tile index.
* @param tileY the y tile index (OSM notation).
* @param zoomLevel the zoom level.
* @return the tile image bytes.
*/
private byte[] readTile(int tileX, int tileY, byte zoomLevel) {
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)});
if (cursor.moveToFirst())
return cursor.getBlob(0);
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2019 Andrea Antonello
* Copyright 2019 devemux86
*
* 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.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;
/**
* Create a MBTiles tile source.
*
* @param path the path to the MBTiles database.
*/
public MBTilesBitmapTileSource(String path) {
this(path, null, null);
}
/**
* Create a MBTiles tile source.
*
* @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;
}
}

View File

@ -2,7 +2,8 @@
* Copyright 2010, 2011, 2012 mapsforge.org
* Copyright 2012 Hannes Janetzek
* Copyright 2014 Ludwig M Brinckmann
* Copyright 2016-2017 devemux86
* Copyright 2016-2019 devemux86
* Copyright 2019 Andrea Antonello
*
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
*
@ -577,6 +578,17 @@ public final class MercatorProjection {
return pixelYToLatitude(tileY * Tile.SIZE, getMapSize(zoomLevel));
}
/**
* Converts a tile Y number at a certain zoom level to TMS notation.
*
* @param tileY the tile Y number that should be converted.
* @param zoomLevel the zoom level at which the number should be converted.
* @return the TMS value of the tile Y number.
*/
public static long tileYToTMS(long tileY, byte zoomLevel) {
return (long) (zoomLevelToScale(zoomLevel) - tileY - 1);
}
public static double toLatitude(double y) {
return 90 - 360 * Math.atan(Math.exp((y - 0.5) * (2 * Math.PI))) / Math.PI;
}