MBTiles vector tile source (#740)
Support offline vector maps (MBTiles, MVT, gzipped pbf)
This commit is contained in:
parent
83aed13683
commit
a9e18a2add
@ -3,6 +3,7 @@ include ':vtm'
|
||||
include ':vtm-android'
|
||||
include ':vtm-android-example'
|
||||
include ':vtm-android-gdx'
|
||||
include ':vtm-android-mvt'
|
||||
include ':vtm-app'
|
||||
include ':vtm-desktop'
|
||||
include ':vtm-desktop-lwjgl'
|
||||
|
@ -79,7 +79,10 @@
|
||||
android:name=".MarkerOverlayActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||
<activity
|
||||
android:name=".MBTilesBitmapTileActivity"
|
||||
android:name=".MBTilesBitmapActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||
<activity
|
||||
android:name=".MBTilesMvtActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize" />
|
||||
<activity
|
||||
android:name=".NextzenGeojsonActivity"
|
||||
|
@ -9,6 +9,7 @@ configurations.all {
|
||||
|
||||
dependencies {
|
||||
implementation project(':vtm-android')
|
||||
implementation project(':vtm-android-mvt')
|
||||
implementation project(':vtm-extras')
|
||||
implementation project(':vtm-http')
|
||||
implementation project(':vtm-jeo')
|
||||
|
@ -26,7 +26,7 @@
|
||||
<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="startup_message_mbtiles">To run this sample activity, you need an MBTiles database installed on storage.\n\nadb push %s %s</string>
|
||||
<string name="exit">Exit</string>
|
||||
|
||||
</resources>
|
||||
|
@ -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
|
||||
@ -19,6 +20,7 @@ import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import org.oscim.android.tiling.source.mbtiles.MBTilesBitmapTileSource;
|
||||
import org.oscim.android.tiling.source.mbtiles.MBTilesTileSource;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
@ -27,19 +29,19 @@ import org.oscim.layers.tile.bitmap.BitmapTileLayer;
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* An example activity making use of MBTiles.
|
||||
* An example activity making use of raster MBTiles.
|
||||
*/
|
||||
public class MBTilesBitmapTileActivity extends BitmapTileActivity {
|
||||
public class MBTilesBitmapActivity extends BitmapTileActivity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
File file = new File(getExternalFilesDir(null), "test.mbtiles");
|
||||
File file = new File(getExternalFilesDir(null), "raster.mbtiles");
|
||||
if (!file.exists()) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(R.string.startup_message_mbtiles)
|
||||
.setMessage(getResources().getString(R.string.startup_message_mbtiles, file.getName(), file.getAbsolutePath()))
|
||||
.setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
@ -50,7 +52,8 @@ public class MBTilesBitmapTileActivity extends BitmapTileActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
MBTilesBitmapTileSource tileSource = new MBTilesBitmapTileSource(file.getAbsolutePath(), 128, null);
|
||||
MBTilesTileSource tileSource = new MBTilesBitmapTileSource(file.getAbsolutePath(), 128, null);
|
||||
|
||||
BitmapTileLayer bitmapLayer = new BitmapTileLayer(mMap, tileSource);
|
||||
mMap.layers().add(bitmapLayer);
|
||||
|
||||
@ -58,7 +61,7 @@ public class MBTilesBitmapTileActivity extends BitmapTileActivity {
|
||||
MapPosition pos = new MapPosition();
|
||||
mMap.getMapPosition(pos);
|
||||
if (pos.x == 0.5 && pos.y == 0.5) {
|
||||
BoundingBox bbox = tileSource.getBounds();
|
||||
BoundingBox bbox = tileSource.getDataSource().getBounds();
|
||||
if (bbox != null) {
|
||||
pos.setByBoundingBox(bbox, Tile.SIZE * 4, Tile.SIZE * 4);
|
||||
mMap.setMapPosition(pos);
|
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* 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.test;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import org.oscim.android.mvt.tiling.source.mbtiles.MBTilesMvtTileSource;
|
||||
import org.oscim.android.tiling.source.mbtiles.MBTilesTileSource;
|
||||
import org.oscim.android.tiling.source.mbtiles.MBTilesUnsupportedException;
|
||||
import org.oscim.core.BoundingBox;
|
||||
import org.oscim.core.MapPosition;
|
||||
import org.oscim.core.Tile;
|
||||
import org.oscim.layers.tile.buildings.BuildingLayer;
|
||||
import org.oscim.layers.tile.vector.VectorTileLayer;
|
||||
import org.oscim.layers.tile.vector.labeling.LabelLayer;
|
||||
import org.oscim.theme.VtmThemes;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* An example activity making use of vector MBTiles.
|
||||
*/
|
||||
public class MBTilesMvtActivity extends MapActivity {
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
File file = new File(getExternalFilesDir(null), "vector.mbtiles");
|
||||
if (!file.exists()) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(getResources().getString(R.string.startup_message_mbtiles, file.getName(), file.getAbsolutePath()))
|
||||
.setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
return;
|
||||
}
|
||||
|
||||
MBTilesTileSource tileSource;
|
||||
try {
|
||||
tileSource = new MBTilesMvtTileSource(file.getAbsolutePath(), "en");
|
||||
} catch (MBTilesUnsupportedException e) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.warning)
|
||||
.setMessage(e.getMessage())
|
||||
.setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
return;
|
||||
}
|
||||
|
||||
VectorTileLayer l = mMap.setBaseMap(tileSource);
|
||||
mMap.setTheme(VtmThemes.OPENMAPTILES);
|
||||
|
||||
mMap.layers().add(new BuildingLayer(mMap, l));
|
||||
mMap.layers().add(new LabelLayer(mMap, l));
|
||||
|
||||
/* 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.getDataSource().getBounds();
|
||||
if (bbox != null) {
|
||||
pos.setByBoundingBox(bbox, Tile.SIZE * 4, Tile.SIZE * 4);
|
||||
mMap.setMapPosition(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
* Copyright 2018 boldtrn
|
||||
* Copyright 2018-2019 Gustl22
|
||||
* Copyright 2019 Andrea Antonello
|
||||
* Copyright 2019 Kostas Tzounopoulos
|
||||
*
|
||||
* This file is part of the OpenScienceMap project (http://www.opensciencemap.org).
|
||||
*
|
||||
@ -88,6 +89,7 @@ public class Samples extends Activity {
|
||||
linearLayout.addView(createLabel(null));
|
||||
linearLayout.addView(createButton(SimpleMapActivity.class));
|
||||
linearLayout.addView(createButton(MapsforgeActivity.class));
|
||||
linearLayout.addView(createButton(MBTilesMvtActivity.class));
|
||||
linearLayout.addView(createButton(MapilionMvtActivity.class));
|
||||
/*linearLayout.addView(createButton(MapzenMvtActivity.class));
|
||||
linearLayout.addView(createButton(MapzenGeojsonActivity.class));
|
||||
@ -114,7 +116,7 @@ public class Samples extends Activity {
|
||||
|
||||
linearLayout.addView(createLabel("Raster Maps"));
|
||||
linearLayout.addView(createButton(BitmapTileActivity.class));
|
||||
linearLayout.addView(createButton(MBTilesBitmapTileActivity.class));
|
||||
linearLayout.addView(createButton(MBTilesBitmapActivity.class));
|
||||
|
||||
linearLayout.addView(createLabel("Overlays"));
|
||||
linearLayout.addView(createButton(MarkerOverlayActivity.class));
|
||||
|
2
vtm-android-mvt/AndroidManifest.xml
Normal file
2
vtm-android-mvt/AndroidManifest.xml
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<manifest package="org.oscim.android.mvt" />
|
55
vtm-android-mvt/build.gradle
Normal file
55
vtm-android-mvt/build.gradle
Normal file
@ -0,0 +1,55 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.github.dcendents.android-maven'
|
||||
|
||||
dependencies {
|
||||
api project(':vtm-android')
|
||||
api project(':vtm-mvt')
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion androidCompileSdk()
|
||||
buildToolsVersion "$androidBuildVersionTools"
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
versionCode project.versionCode()
|
||||
versionName project.versionName()
|
||||
minSdkVersion androidMinSdk()
|
||||
targetSdkVersion androidTargetSdk()
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
java.srcDirs = ['src']
|
||||
}
|
||||
debug.setRoot('build-types/debug')
|
||||
release.setRoot('build-types/release')
|
||||
}
|
||||
|
||||
lintOptions { abortOnError false }
|
||||
}
|
||||
|
||||
android.libraryVariants.all { variant ->
|
||||
def name = variant.buildType.name
|
||||
if (name == "debug")
|
||||
return
|
||||
|
||||
def jar = project.tasks.create "jar${name.capitalize()}", Jar
|
||||
jar.dependsOn variant.javaCompileProvider
|
||||
jar.exclude '**/BuildConfig.class'
|
||||
jar.exclude '**/R.class'
|
||||
jar.exclude '**/R$*.class'
|
||||
jar.from variant.javaCompileProvider.get().destinationDir
|
||||
artifacts.add('archives', jar)
|
||||
}
|
||||
|
||||
if (project.hasProperty("SONATYPE_USERNAME")) {
|
||||
afterEvaluate {
|
||||
project.apply from: "${rootProject.projectDir}/deploy.gradle"
|
||||
}
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.mvt.tiling.source.mbtiles;
|
||||
|
||||
import android.database.Cursor;
|
||||
import org.oscim.android.tiling.source.mbtiles.MBTilesTileDataSource;
|
||||
import org.oscim.android.tiling.source.mbtiles.MBTilesUnsupportedException;
|
||||
import org.oscim.core.MercatorProjection;
|
||||
import org.oscim.layers.tile.MapTile;
|
||||
import org.oscim.tiling.ITileDataSink;
|
||||
import org.oscim.tiling.OverzoomDataSink;
|
||||
import org.oscim.tiling.QueryResult;
|
||||
import org.oscim.tiling.source.mvt.MvtTileDecoder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
/**
|
||||
* A tile data source for MBTiles vector databases.
|
||||
*/
|
||||
public class MBTilesMvtTileDataSource extends MBTilesTileDataSource {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MBTilesMvtTileDataSource.class);
|
||||
|
||||
private static final List<String> SUPPORTED_FORMATS = Collections.singletonList("pbf");
|
||||
private static final String WHERE_FORMAT = "zoom_level=%d AND tile_column=%d AND tile_row=%d";
|
||||
|
||||
private final String mLanguage;
|
||||
|
||||
private final ThreadLocal<MvtTileDecoder> mThreadLocalDecoders = new ThreadLocal<MvtTileDecoder>() {
|
||||
@Override
|
||||
protected MvtTileDecoder initialValue() {
|
||||
return new MvtTileDecoder(mLanguage);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a tile data source for MBTiles vector databases.
|
||||
*
|
||||
* @param path the path to the MBTiles database.
|
||||
* @param language the language to use when rendering the MBTiles.
|
||||
*/
|
||||
public MBTilesMvtTileDataSource(String path, String language) {
|
||||
super(path);
|
||||
mLanguage = language != null ? language : "en";
|
||||
|
||||
try {
|
||||
assertDatabaseFormat();
|
||||
} catch (MBTilesUnsupportedException e) {
|
||||
log.error("Invalid MBTiles database", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (mDatabase != null && mDatabase.isOpen())
|
||||
mDatabase.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getSupportedFormats() {
|
||||
return SUPPORTED_FORMATS;
|
||||
}
|
||||
|
||||
private MapTile mapTile(Cursor cursor) {
|
||||
int tileX = cursor.getInt(cursor.getColumnIndexOrThrow("tile_column"));
|
||||
int tileY = cursor.getInt(cursor.getColumnIndexOrThrow("tile_row"));
|
||||
int zoomLevel = cursor.getInt(cursor.getColumnIndexOrThrow("zoom_level"));
|
||||
long tmsTileY = MercatorProjection.tileYToTMS(tileY, (byte) zoomLevel);
|
||||
return new MapTile(tileX, (int) tmsTileY, zoomLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overzoom on the DB layer: generate a query for all tiles with lower zoomLevel than the one requested.
|
||||
*/
|
||||
private String overzoomQuery(MapTile tile) {
|
||||
long tmsTileY = MercatorProjection.tileYToTMS(tile.tileY, tile.zoomLevel);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (int zoomLevel = tile.zoomLevel - 1; zoomLevel > 0; zoomLevel--) {
|
||||
int diff = tile.zoomLevel - zoomLevel;
|
||||
sb.append(String.format(Locale.US, WHERE_FORMAT, zoomLevel, tile.tileX >> diff, tmsTileY >> diff));
|
||||
if (zoomLevel > 1) // Not the last iteration
|
||||
sb.append(") OR (");
|
||||
}
|
||||
sb.append(")");
|
||||
return String.format(SELECT_TILES_FORMAT, sb.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void query(MapTile requestTile, ITileDataSink requestDataSink) {
|
||||
Cursor cursor = null;
|
||||
ITileDataSink responseDataSink = requestDataSink;
|
||||
try {
|
||||
long tmsTileY = MercatorProjection.tileYToTMS(requestTile.tileY, requestTile.zoomLevel);
|
||||
cursor = mDatabase.rawQuery(String.format(SELECT_TILES_FORMAT, String.format(Locale.US, WHERE_FORMAT, requestTile.zoomLevel, requestTile.tileX, tmsTileY)), null);
|
||||
|
||||
if (cursor.getCount() == 0) {
|
||||
cursor.close();
|
||||
cursor = mDatabase.rawQuery(overzoomQuery(requestTile), null);
|
||||
}
|
||||
|
||||
if (cursor.moveToFirst()) {
|
||||
byte[] bytes = cursor.getBlob(cursor.getColumnIndexOrThrow("tile_data"));
|
||||
|
||||
MapTile responseTile = mapTile(cursor);
|
||||
if (requestTile.zoomLevel != responseTile.zoomLevel)
|
||||
responseDataSink = new OverzoomDataSink(requestDataSink, responseTile, requestTile);
|
||||
|
||||
boolean success = mThreadLocalDecoders.get().decode(responseTile, responseDataSink, new GZIPInputStream(new ByteArrayInputStream(bytes)));
|
||||
responseDataSink.completed(success ? QueryResult.SUCCESS : QueryResult.FAILED);
|
||||
} else
|
||||
responseDataSink.completed(QueryResult.TILE_NOT_FOUND);
|
||||
} catch (IOException e) {
|
||||
responseDataSink.completed(QueryResult.FAILED);
|
||||
} finally {
|
||||
if (cursor != null)
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.mvt.tiling.source.mbtiles;
|
||||
|
||||
import org.oscim.android.tiling.source.mbtiles.MBTilesTileSource;
|
||||
|
||||
/**
|
||||
* A tile source for MBTiles vector databases.
|
||||
*/
|
||||
public class MBTilesMvtTileSource extends MBTilesTileSource {
|
||||
|
||||
/**
|
||||
* Create a tile source for MBTiles vector databases.
|
||||
*
|
||||
* @param path the path to the MBTiles database.
|
||||
*/
|
||||
public MBTilesMvtTileSource(String path) {
|
||||
this(path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tile source for MBTiles vector databases.
|
||||
*
|
||||
* @param path the path to the MBTiles database.
|
||||
* @param language the language to use when rendering the MBTiles.
|
||||
*/
|
||||
public MBTilesMvtTileSource(String path, String language) {
|
||||
super(new MBTilesMvtTileDataSource(path, language));
|
||||
}
|
||||
}
|
@ -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() {
|
||||
if (mDatabase != null && mDatabase.isOpen())
|
||||
mDatabase.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
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);
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ import org.oscim.core.Tile;
|
||||
import org.oscim.utils.geom.TileClipper;
|
||||
import org.oscim.utils.geom.TileSeparator;
|
||||
|
||||
class OverzoomDataSink implements ITileDataSink {
|
||||
public class OverzoomDataSink implements ITileDataSink {
|
||||
|
||||
private final ITileDataSink sink;
|
||||
|
||||
@ -30,7 +30,7 @@ class OverzoomDataSink implements ITileDataSink {
|
||||
private final TileSeparator separator;
|
||||
private final float dx, dy, scale;
|
||||
|
||||
OverzoomDataSink(ITileDataSink sink, Tile overzoomTile, Tile tile) {
|
||||
public OverzoomDataSink(ITileDataSink sink, Tile overzoomTile, Tile tile) {
|
||||
this.sink = sink;
|
||||
|
||||
int diff = tile.zoomLevel - overzoomTile.zoomLevel;
|
||||
|
Loading…
x
Reference in New Issue
Block a user