Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(android): mbtiles overlay #590

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion android/manifest
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# this is your module manifest and used by Titanium
# during compilation, packaging, distribution, etc.
#
version: 5.5.0
version: 5.6.0
apiversion: 4
architectures: arm64-v8a armeabi-v7a x86 x86_64
description: External version of Map module using native Google Maps library
Expand Down
225 changes: 225 additions & 0 deletions android/src/ti/map/MapBoxOfflineTileProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
package ti.map;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.LatLngBounds;
import com.google.android.gms.maps.model.Tile;
import com.google.android.gms.maps.model.TileProvider;
import java.io.Closeable;
import java.io.File;
import org.appcelerator.kroll.common.Log;

public class MapBoxOfflineTileProvider implements TileProvider, Closeable
{

// ------------------------------------------------------------------------
// Instance Variables
// ------------------------------------------------------------------------

private int mMinimumZoom = Integer.MIN_VALUE;

private int mMaximumZoom = Integer.MAX_VALUE;

private LatLngBounds mBounds;

private SQLiteDatabase mDatabase;

private final Object mDatabaseLock = new Object();

// ------------------------------------------------------------------------
// Constructors
// ------------------------------------------------------------------------

public MapBoxOfflineTileProvider(File file)
{
this(file.getAbsolutePath());
}

public MapBoxOfflineTileProvider(String pathToFile)
{
int flags = SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS;
try {
this.mDatabase = SQLiteDatabase.openDatabase(pathToFile, null, flags);
this.calculateZoomConstraints();
this.calculateBounds();
} catch (Error e) {
Log.e("TiUIMapView", "Error loading mbtiles file");
}
}

// ------------------------------------------------------------------------
// TileProvider Interface
// ------------------------------------------------------------------------

@Override
public Tile getTile(int x, int y, int z)
{
Tile tile = NO_TILE;
synchronized (mDatabaseLock) {
if (this.isZoomLevelAvailable(z) && this.isDatabaseAvailable()) {
String[] projection = { "tile_data" };
int row = ((int) (Math.pow(2, z) - y) - 1);
String predicate = "tile_row = ? AND tile_column = ? AND zoom_level = ?";
String[] values = { String.valueOf(row), String.valueOf(x), String.valueOf(z) };
Cursor c = this.mDatabase.query("tiles", projection, predicate, values, null, null, null);
if (c != null) {
c.moveToFirst();
if (!c.isAfterLast()) {
tile = new Tile(256, 256, c.getBlob(0));
}
c.close();
}
}
}
return tile;
}

// ------------------------------------------------------------------------
// Closeable Interface
// ------------------------------------------------------------------------

/**
* Closes the provider, cleaning up any background resources.
*
* <p>
* You must call {@link #close()} when you are finished using an instance of
* this provider. Failing to do so may leak resources, such as the backing
* SQLiteDatabase.
* </p>
*/
@Override
public void close()
{
synchronized (mDatabaseLock) {
if (this.mDatabase != null) {
this.mDatabase.close();
this.mDatabase = null;
}
}
}

// ------------------------------------------------------------------------
// Public Methods
// ------------------------------------------------------------------------

public void swapDatabase(File newDatabaseFile)
{
synchronized (mDatabaseLock) {
mDatabase.close();
int flags = SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS;
this.mDatabase = SQLiteDatabase.openDatabase(newDatabaseFile.getAbsolutePath(), null, flags);
}
}
/**
* The minimum zoom level supported by this provider.
*
* @return the minimum zoom level supported or {@link Integer.MIN_VALUE} if
* it could not be determined.
*/
public int getMinimumZoom()
{
return this.mMinimumZoom;
}

/**
* The maximum zoom level supported by this provider.
*
* @return the maximum zoom level supported or {@link Integer.MAX_VALUE} if
* it could not be determined.
*/
public int getMaximumZoom()
{
return this.mMaximumZoom;
}

/**
* The geographic bounds available from this provider.
*
* @return the geographic bounds available or {@link null} if it could not
* be determined.
*/
public LatLngBounds getBounds()
{
return this.mBounds;
}

/**
* Determines if the requested zoom level is supported by this provider.
*
* @param zoom The requested zoom level.
* @return {@code true} if the requested zoom level is supported by this
* provider.
*/
public boolean isZoomLevelAvailable(int zoom)
{
return (zoom >= this.mMinimumZoom) && (zoom <= this.mMaximumZoom);
}

// ------------------------------------------------------------------------
// Private Methods
// ------------------------------------------------------------------------

private void calculateZoomConstraints()
{
if (this.isDatabaseAvailable()) {
String[] projection = new String[] { "value" };

String[] minArgs = new String[] { "minzoom" };

String[] maxArgs = new String[] { "maxzoom" };

Cursor c;

c = this.mDatabase.query("metadata", projection, "name = ?", minArgs, null, null, null);

c.moveToFirst();
if (!c.isAfterLast()) {
this.mMinimumZoom = c.getInt(0);
}
c.close();

c = this.mDatabase.query("metadata", projection, "name = ?", maxArgs, null, null, null);

c.moveToFirst();
if (!c.isAfterLast()) {
this.mMaximumZoom = c.getInt(0);
}
c.close();
}
}

private void calculateBounds()
{
if (this.isDatabaseAvailable()) {
String[] projection = new String[] { "value" };

String[] subArgs = new String[] { "bounds" };

Cursor c = this.mDatabase.query("metadata", projection, "name = ?", subArgs, null, null, null);

c.moveToFirst();
if (!c.isAfterLast()) {
String[] parts = c.getString(0).split(",\\s*");

double w = Double.parseDouble(parts[0]);
double s = Double.parseDouble(parts[1]);
double e = Double.parseDouble(parts[2]);
double n = Double.parseDouble(parts[3]);

LatLng ne = new LatLng(n, e);
LatLng sw = new LatLng(s, w);

this.mBounds = new LatLngBounds(sw, ne);
}
c.close();
}
}

private boolean isDatabaseAvailable()
{
synchronized (mDatabaseLock) {
return (this.mDatabase != null) && (this.mDatabase.isOpen());
}
}
}
2 changes: 2 additions & 0 deletions android/src/ti/map/MapModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class MapModule extends KrollModule implements OnMapsSdkInitializedCallba
public static final String PROPERTY_HEADING = "heading";
public static final String PROPERTY_PITCH = "pitch";

@Kroll.constant
public static final int TYPE_NONE = GoogleMap.MAP_TYPE_NONE;
@Kroll.constant
public static final int NORMAL_TYPE = GoogleMap.MAP_TYPE_NORMAL;
@Kroll.constant
Expand Down
15 changes: 14 additions & 1 deletion android/src/ti/map/TiUIMapView.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ public class TiUIMapView extends TiUIFragment
private DefaultClusterRenderer clusterRender;
private MarkerManager mMarkerManager;
private MarkerManager.Collection collection;
private float minZoom = -1;
private float maxZoom = -1;

public TiUIMapView(final TiViewProxy proxy, Activity activity)
{
Expand Down Expand Up @@ -239,7 +241,12 @@ public void onMapReady(GoogleMap gMap)
markerCollection.setInfoWindowAdapter(this);
markerCollection.setOnInfoWindowClickListener(this);
markerCollection.setOnMarkerDragListener(this);

if (minZoom != -1) {
map.setMinZoomPreference(minZoom);
}
if (maxZoom != -1) {
map.setMaxZoomPreference(maxZoom);
}
((ViewProxy) proxy).clearPreloadObjects();
}

Expand Down Expand Up @@ -322,6 +329,12 @@ public void processMapProperties(KrollDict d)
if (clusterRender != null)
clusterRender.setMinClusterSize(d.getInt(MapModule.PROPERTY_MIN_CLUSTER_SIZE));
}
if (d.containsKey("maxZoomLevel")) {
maxZoom = Float.parseFloat(d.getString("maxZoomLevel"));
}
if (d.containsKey("minZoomLevel")) {
minZoom = Float.parseFloat(d.getString("minZoomLevel"));
}
}

@Override
Expand Down
Loading