From e9662e6a51f336a253c004836f69f70ba97c311b Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Wed, 17 Aug 2016 18:19:13 +0200 Subject: [PATCH] [android] Sources: peer model, mutability --- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 25 ++++ .../mapbox/mapboxsdk/maps/NativeMapView.java | 10 +- .../style/sources/GeoJsonOptions.java | 63 +++++++++ .../style/sources/GeoJsonSource.java | 123 +++++++++++++++--- .../mapboxsdk/style/sources/RasterSource.java | 31 +++-- .../mapboxsdk/style/sources/Source.java | 35 ++++- .../mapboxsdk/style/sources/VectorSource.java | 20 ++- .../style/GeoJsonClusteringActivity.java | 12 +- .../activity/style/RuntimeStyleActivity.java | 75 ++++++++++- .../src/main/res/menu/menu_runtime_style.xml | 4 + platform/android/config.cmake | 11 +- platform/android/src/jni.cpp | 43 ++++-- .../geojson.hpp} | 11 +- .../src/style/conversion/url_or_tileset.hpp | 38 ++++++ .../src/style/sources/geojson_source.cpp | 77 +++++++++++ .../src/style/sources/geojson_source.hpp | 34 +++++ .../src/style/sources/raster_source.cpp | 54 ++++++++ .../src/style/sources/raster_source.hpp | 30 +++++ platform/android/src/style/sources/source.cpp | 72 ++++++++++ platform/android/src/style/sources/source.hpp | 56 ++++++++ .../android/src/style/sources/sources.cpp | 54 +++++--- .../android/src/style/sources/sources.hpp | 12 +- .../src/style/sources/vector_source.cpp | 53 ++++++++ .../src/style/sources/vector_source.hpp | 30 +++++ 24 files changed, 886 insertions(+), 87 deletions(-) create mode 100644 platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java rename platform/android/src/style/{android_geojson.hpp => conversion/geojson.hpp} (86%) create mode 100644 platform/android/src/style/conversion/url_or_tileset.hpp create mode 100644 platform/android/src/style/sources/geojson_source.cpp create mode 100644 platform/android/src/style/sources/geojson_source.hpp create mode 100644 platform/android/src/style/sources/raster_source.cpp create mode 100644 platform/android/src/style/sources/raster_source.hpp create mode 100644 platform/android/src/style/sources/source.cpp create mode 100644 platform/android/src/style/sources/source.hpp create mode 100644 platform/android/src/style/sources/vector_source.cpp create mode 100644 platform/android/src/style/sources/vector_source.hpp diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index a97395695d5..d99800863ee 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -152,6 +152,31 @@ public void removeLayer(@NonNull String layerId) throws NoSuchLayerException { getMapView().getNativeMapView().removeLayer(layerId); } + @Nullable + @UiThread + public Source getSource(@NonNull String sourceId) { + return getMapView().getNativeMapView().getSource(sourceId); + } + + /** + * Tries to cast the Source to T, returns null if it's another type. + * + * @param sourceId the id used to look up a layer + * @param the generic type of a Source + * @return the casted Source, null if another type + */ + @Nullable + @UiThread + public T getSourceAs(@NonNull String sourceId) { + try { + //noinspection unchecked + return (T) getMapView().getNativeMapView().getSource(sourceId); + } catch (ClassCastException e) { + Log.e(TAG, String.format("Source: %s is a different type: %s", sourceId, e.getMessage())); + return null; + } + } + @UiThread public void addSource(@NonNull Source source) { getMapView().getNativeMapView().addSource(source); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 0750c987eab..30aa7c31992 100755 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -494,8 +494,12 @@ public void removeLayer(@NonNull String layerId) throws NoSuchLayerException { nativeRemoveLayer(nativeMapViewPtr, layerId); } + public Source getSource(@NonNull String sourceId) { + return nativeGetSource(nativeMapViewPtr, sourceId); + } + public void addSource(@NonNull Source source) { - nativeAddSource(nativeMapViewPtr, source.getId(), source); + nativeAddSource(nativeMapViewPtr, source.getNativePtr()); } public void removeSource(@NonNull String sourceId) throws NoSuchSourceException { @@ -712,7 +716,9 @@ private native void nativeSetVisibleCoordinateBounds(long nativeMapViewPtr, LatL private native void nativeRemoveLayer(long nativeMapViewPtr, String layerId) throws NoSuchLayerException; - private native void nativeAddSource(long nativeMapViewPtr, String id, Source source); + private native Source nativeGetSource(long nativeMapViewPtr, String sourceId); + + private native void nativeAddSource(long nativeMapViewPtr, long nativeSourcePtr); private native void nativeRemoveSource(long nativeMapViewPtr, String sourceId) throws NoSuchSourceException; diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java new file mode 100644 index 00000000000..d7a92823719 --- /dev/null +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonOptions.java @@ -0,0 +1,63 @@ +package com.mapbox.mapboxsdk.style.sources; + +import java.util.HashMap; + +/** + * Options for the GeoJsonSource + */ +public class GeoJsonOptions extends HashMap { + + /** + * Defaults to 18. + * Maximum zoom level at which to create vector tiles (higher means greater detail at high zoom levels). + */ + public GeoJsonOptions withMaxZoom(int maxZoom) { + this.put("maxzoom", maxZoom); + return this; + } + + /** + * Defaults to 128. + * Tile buffer size on each side (measured in 1/512ths of a tile; higher means fewer rendering artifacts near tile edges but slower performance). + */ + public GeoJsonOptions withBuffer(int buffer) { + this.put("buffer", buffer); + return this; + } + + /** + * Defaults to 0.375. + * Douglas-Peucker simplification tolerance (higher means simpler geometries and faster performance). + */ + public GeoJsonOptions withTolerance(float tolerance) { + this.put("tolerance", tolerance); + return this; + } + + /** + * Defaults to false. + * If the data is a collection of point features, setting this to true clusters the points by radius into groups. + */ + public GeoJsonOptions withCluster(boolean cluster) { + this.put("cluster", cluster); + return this; + } + + /** + * Defaults to 50. + * Radius of each cluster when clustering points, measured in 1/512ths of a tile. + */ + public GeoJsonOptions withClusterMaxZoom(int clusterMaxZoom) { + this.put("clusterMaxZoom", clusterMaxZoom); + return this; + } + + /** + * Max zoom to cluster points on. Defaults to one zoom less than maxzoom (so that last zoom features are not clustered). + */ + public GeoJsonOptions withClusterRadius(int clusterRadius) { + this.put("clusterRadius", clusterRadius); + return this; + } + +} diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java index fd226a00606..a66e0a4adca 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java @@ -1,9 +1,10 @@ package com.mapbox.mapboxsdk.style.sources; -import com.google.gson.Gson; +import com.mapbox.services.commons.geojson.Feature; import com.mapbox.services.commons.geojson.FeatureCollection; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; /** @@ -12,8 +13,34 @@ * @see the style specification */ public class GeoJsonSource extends Source { - public static final String TYPE = "geojson"; - private static final String DATA_KEY = "data"; + + /** + * Internal use + */ + public GeoJsonSource(long nativePtr) { + super(nativePtr); + } + + /** + * Create an empty GeoJsonSource + * + * @param id the source id + */ + public GeoJsonSource(String id) { + initialize(id, null); + setGeoJson(FeatureCollection.fromFeatures(new ArrayList())); + } + + /** + * Create an empty GeoJsonSource + * + * @param id the source id + * @param options options + */ + public GeoJsonSource(String id, GeoJsonOptions options) { + initialize(id, options); + setGeoJson(FeatureCollection.fromFeatures(new ArrayList())); + } /** * Create a GeoJsonSource from a raw json string @@ -22,11 +49,26 @@ public class GeoJsonSource extends Source { * @param geoJson raw Json body */ public GeoJsonSource(String id, String geoJson) { - super(id, TYPE); if (geoJson == null || geoJson.startsWith("http")) { throw new IllegalArgumentException("Expected a raw json body"); } - setRawJson(geoJson); + initialize(id, null); + setGeoJson(geoJson); + } + + /** + * Create a GeoJsonSource from a raw json string + * + * @param id the source id + * @param geoJson raw Json body + * @param options options + */ + public GeoJsonSource(String id, String geoJson, GeoJsonOptions options) { + if (geoJson == null || geoJson.startsWith("http")) { + throw new IllegalArgumentException("Expected a raw json body"); + } + initialize(id, options); + setGeoJson(geoJson); } /** @@ -36,8 +78,20 @@ public GeoJsonSource(String id, String geoJson) { * @param url remote json file */ public GeoJsonSource(String id, URL url) { - super(id, TYPE); - this.put(DATA_KEY, url.toExternalForm()); + initialize(id, null); + nativeSetUrl(url.toExternalForm()); + } + + /** + * Create a GeoJsonSource from a remote geo json file + * + * @param id the source id + * @param url remote json file + * @param options options + */ + public GeoJsonSource(String id, URL url, GeoJsonOptions options) { + initialize(id, options); + nativeSetUrl(url.toExternalForm()); } /** @@ -47,30 +101,57 @@ public GeoJsonSource(String id, URL url) { * @param features the features */ public GeoJsonSource(String id, FeatureCollection features) { - super(id, TYPE); - setRawJson(features.toJson()); + initialize(id, null); + setGeoJson(features); + } + + /** + * Create a GeoJsonSource from a FeatureCollection + * + * @param id the source id + * @param features the features + * @param options options + */ + public GeoJsonSource(String id, FeatureCollection features, GeoJsonOptions options) { + initialize(id, options); + setGeoJson(features); + } + + public void setGeoJson(FeatureCollection features) { + checkValidity(); + setGeoJson(features.toJson()); } - public GeoJsonSource withCluster(boolean cluster) { - this.put("cluster", cluster); - return this; + public void setGeoJson(String json) { + checkValidity(); + setRawJson(json); } - public GeoJsonSource withClusterMaxZoom(float zoom) { - this.put("clusterMaxZoom", zoom); - return this; + public void setUrl(URL url) { + checkValidity(); + setUrl(url.toExternalForm()); } - public GeoJsonSource withClusterRadius(float radius) { - this.put("clusterRadius", radius); - return this; + public void setUrl(String url) { + checkValidity(); + nativeSetUrl(url); } - private void setRawJson(String geoJson) { + protected void setRawJson(String geoJson) { //Wrap the String in a map as an Object is expected by the //style conversion template HashMap wrapper = new HashMap<>(); - wrapper.put(DATA_KEY, geoJson); - this.put(DATA_KEY, wrapper); + wrapper.put("data", geoJson); + nativeSetGeoJson(wrapper); } + + protected native void initialize(String layerId, Object options); + + protected native void nativeSetUrl(String url); + + private native void nativeSetGeoJson(Object geoJson); + + @Override + protected native void finalize() throws Throwable; + } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java index f5db6f2a37f..14bd8bb9ed5 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java @@ -8,26 +8,37 @@ * @see The style specificition */ public class RasterSource extends Source { - public static final String TYPE = "raster"; - private static final String URL_KEY = "url"; - private static final String TILE_SIZE_KEY = "tileSize"; + public static final int DEFAULT_TILE_SIZE = 512; + + /** + * Internal use + */ + public RasterSource(long nativePtr) { + super(nativePtr); + } public RasterSource(String id, URL url) { this(id, url.toExternalForm()); } public RasterSource(String id, String url) { - super(id, TYPE); - this.put(URL_KEY, url); + initialize(id, url, DEFAULT_TILE_SIZE); + } + + public RasterSource(String id, String url, int tileSize) { + initialize(id, url, tileSize); } public RasterSource(String id, TileSet tileSet) { - super(id, TYPE); - this.putAll(tileSet.toValueObject()); + initialize(id, tileSet.toValueObject(), DEFAULT_TILE_SIZE); } - public RasterSource withTileSize(int tileSize) { - this.put(TILE_SIZE_KEY, (float) tileSize); - return this; + public RasterSource(String id, TileSet tileSet, int tileSize) { + initialize(id, tileSet.toValueObject(), tileSize); } + + protected native void initialize(String layerId, Object payload, int tileSize); + + @Override + protected native void finalize() throws Throwable; } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java index d9aacdc80da..58ffa12e35f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java @@ -2,15 +2,38 @@ import java.util.HashMap; -public abstract class Source extends HashMap { - private final String id; +/** + * Base Peer class for sources. see source.hpp for the other half of the peer. + */ +public abstract class Source { + private long nativePtr; + private boolean invalidated; - protected Source(String id, String type) { - this.put("type", type); - this.id = id; + public Source(long nativePtr) { + this.nativePtr = nativePtr; + } + + public Source() { } public String getId() { - return id; + checkValidity(); + return nativeGetId(); + } + + public long getNativePtr() { + return nativePtr; + } + + protected native String nativeGetId(); + + protected void checkValidity() { + if (invalidated) { + throw new RuntimeException("Layer has been invalidated. Request a new reference after adding"); + } + } + + public final void invalidate() { + this.invalidated = true; } } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java index 381294083a4..a9c191f96a6 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java @@ -8,8 +8,13 @@ * @see the style specification */ public class VectorSource extends Source { - public static final String TYPE = "vector"; - private static final String URL_KEY = "url"; + + /** + * Internal use + */ + public VectorSource(long nativePtr) { + super(nativePtr); + } /** * Create a vector source from a remote url @@ -28,8 +33,7 @@ public VectorSource(String id, URL url) { * @param url the url */ public VectorSource(String id, String url) { - super(id, TYPE); - this.put(URL_KEY, url); + initialize(id, url); } /** @@ -39,7 +43,11 @@ public VectorSource(String id, String url) { * @param tileSet the tileset */ public VectorSource(String id, TileSet tileSet) { - super(id, TYPE); - this.putAll(tileSet.toValueObject()); + initialize(id, tileSet.toValueObject()); } + + protected native void initialize(String layerId, Object payload); + + @Override + protected native void finalize() throws Throwable; } diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java index ca4460eac2a..9d13ee165df 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/GeoJsonClusteringActivity.java @@ -16,6 +16,7 @@ import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; import com.mapbox.mapboxsdk.style.layers.CircleLayer; import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonOptions; import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; import com.mapbox.mapboxsdk.testapp.R; @@ -110,10 +111,13 @@ private void addClusteredGeoJsonSource() { //Add a clustered source try { mapboxMap.addSource( - new GeoJsonSource("earthquakes", new URL("https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson")) - .withCluster(true) - .withClusterMaxZoom(14) - .withClusterRadius(50) + new GeoJsonSource("earthquakes", + new URL("https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"), + new GeoJsonOptions() + .withCluster(true) + .withClusterMaxZoom(14) + .withClusterRadius(50) + ) ); } catch (MalformedURLException malformedUrlException) { Log.e(TAG, "That's not an url... " + malformedUrlException.getMessage()); diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java index d8a82168fd0..84467b3aee7 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java @@ -2,6 +2,7 @@ import android.graphics.Color; import android.os.Bundle; +import android.os.Handler; import android.support.annotation.RawRes; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; @@ -30,6 +31,8 @@ import com.mapbox.mapboxsdk.style.sources.TileSet; import com.mapbox.mapboxsdk.style.sources.VectorSource; import com.mapbox.mapboxsdk.testapp.R; +import com.mapbox.services.commons.geojson.Feature; +import com.mapbox.services.commons.geojson.FeatureCollection; import java.io.BufferedReader; import java.io.IOException; @@ -38,6 +41,8 @@ import java.io.Reader; import java.io.StringWriter; import java.io.Writer; +import java.util.ArrayList; +import java.util.List; import static com.mapbox.mapboxsdk.style.layers.Filter.all; import static com.mapbox.mapboxsdk.style.layers.Filter.eq; @@ -154,6 +159,9 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.action_add_parks_layer: addParksLayer(); return true; + case R.id.action_add_dynamic_parks_layer: + addDynamicParksLayer(); + return true; case R.id.action_add_terrain_layer: addTerrainLayer(); return true; @@ -272,6 +280,71 @@ private void addParksLayer() { mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12)); } + private void addDynamicParksLayer() { + //Load some data + FeatureCollection parks; + try { + String json = readRawResource(R.raw.amsterdam); + parks = FeatureCollection.fromJson(json); + } catch (IOException e) { + Toast.makeText(RuntimeStyleActivity.this, "Couldn't add source: " + e.getMessage(), Toast.LENGTH_SHORT).show(); + return; + } + + //Add an empty source + mapboxMap.addSource(new GeoJsonSource("dynamic-park-source")); + + FillLayer layer = new FillLayer("dynamic-parks-layer", "dynamic-park-source"); + layer.setProperties( + fillColor(Color.GREEN), + fillOutlineColor(Color.GREEN), + fillOpacity(0.8f), + fillAntialias(true) + ); + + //Only show me parks + layer.setFilter(all(eq("type", "park"))); + + mapboxMap.addLayer(layer); + + //Get a good look at it all + mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12)); + + //Animate the parks source + animateParksSource(parks, 0); + } + + private void animateParksSource(final FeatureCollection parks, final int counter) { + Handler handler = new Handler(getMainLooper()); + handler.postDelayed(new Runnable() { + @Override + public void run() { + if (mapboxMap == null) { + return; + } + + Log.d(TAG, "Updating parks source"); + //change the source + int park = counter < parks.getFeatures().size() - 1 ? counter : 0; + + GeoJsonSource source = mapboxMap.getSourceAs("dynamic-park-source"); + + if (source == null) { + Log.e(TAG, "Source not found"); + Toast.makeText(RuntimeStyleActivity.this, "Source not found", Toast.LENGTH_SHORT).show(); + return; + } + + List features = new ArrayList<>(); + features.add(parks.getFeatures().get(park)); + source.setGeoJson(FeatureCollection.fromFeatures(features)); + + //Re-post + animateParksSource(parks, park + 1); + } + }, counter == 0 ? 100 : 1000); + } + private void addTerrainLayer() { //Add a source Source source = new VectorSource("my-terrain-source", "mapbox://mapbox.mapbox-terrain-v2"); @@ -302,7 +375,7 @@ private void addTerrainLayer() { private void addSatelliteLayer() { //Add a source - Source source = new RasterSource("my-raster-source", "mapbox://mapbox.satellite").withTileSize(512); + Source source = new RasterSource("my-raster-source", "mapbox://mapbox.satellite", 512); mapboxMap.addSource(source); //Add a layer diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml index 16713ae147c..81e808e93ca 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml @@ -22,6 +22,10 @@ android:id="@+id/action_add_parks_layer" android:title="Add a parks layer" mapbox:showAsAction="never" /> + C++ platform/android/src/style/android_conversion.hpp - platform/android/src/style/android_geojson.hpp + platform/android/src/style/conversion/geojson.hpp platform/android/src/style/value.cpp platform/android/src/style/value.hpp + platform/android/src/style/conversion/url_or_tileset.hpp # Style platform/android/src/style/layers/background_layer.cpp @@ -109,8 +110,16 @@ add_library(mapbox-gl SHARED platform/android/src/style/layers/raster_layer.hpp platform/android/src/style/layers/symbol_layer.cpp platform/android/src/style/layers/symbol_layer.hpp + platform/android/src/style/sources/geojson_source.cpp + platform/android/src/style/sources/geojson_source.hpp + platform/android/src/style/sources/source.cpp + platform/android/src/style/sources/source.hpp platform/android/src/style/sources/sources.cpp platform/android/src/style/sources/sources.hpp + platform/android/src/style/sources/raster_source.cpp + platform/android/src/style/sources/raster_source.hpp + platform/android/src/style/sources/vector_source.cpp + platform/android/src/style/sources/vector_source.hpp # Native map platform/android/src/native_map_view.cpp diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp index 6752c1bbdbe..1a5efce7261 100755 --- a/platform/android/src/jni.cpp +++ b/platform/android/src/jni.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1185,23 +1186,35 @@ void nativeRemoveLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, j } } -void nativeAddSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id, jni::jobject* jsource) { - mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddSource"); +jni::jobject* nativeGetSource(JNIEnv *env, jni::jobject* obj, jni::jlong nativeMapViewPtr, jni::jstring* sourceId) { + mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetSource"); + + assert(env); assert(nativeMapViewPtr != 0); - assert(id != nullptr); - assert(jsource != nullptr); - //Convert - mbgl::optional> source = convertToNativeSource( - *env, - jni::Object(jsource), jni::String(id) - ); + //Get the native map peer + NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - //Add to map view - if (source) { - NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); - nativeMapView->getMap().addSource(std::move(*source)); + //Find the source + mbgl::style::Source* coreSource = nativeMapView->getMap().getSource(std_string_from_jstring(env, sourceId)); + if (!coreSource) { + mbgl::Log::Debug(mbgl::Event::JNI, "No source found"); + return jni::Object(); } + + //Create and return the source's native peer + return createJavaSourcePeer(*env, nativeMapView->getMap(), *coreSource); +} + +void nativeAddSource(JNIEnv *env, jni::jobject* obj, jni::jlong nativeMapViewPtr, jni::jlong nativeSourcePtr) { + mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddSource"); + assert(nativeMapViewPtr != 0); + assert(nativeSourcePtr != 0); + + NativeMapView *nativeMapView = reinterpret_cast(nativeMapViewPtr); + Source *source = reinterpret_cast(nativeSourcePtr); + + nativeMapView->getMap().addSource(source->releaseCoreSource()); } void nativeRemoveSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) { @@ -1687,6 +1700,7 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { java::registerNatives(env); registerNativeLayers(env); + registerNativeSources(env); latLngClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/LatLng"); latLngClass = jni::NewGlobalRef(env, latLngClass).release(); @@ -1838,7 +1852,8 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) { MAKE_NATIVE_METHOD(nativeGetLayer, "(JLjava/lang/String;)Lcom/mapbox/mapboxsdk/style/layers/Layer;"), MAKE_NATIVE_METHOD(nativeAddLayer, "(JJLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeRemoveLayer, "(JLjava/lang/String;)V"), - MAKE_NATIVE_METHOD(nativeAddSource, "(JLjava/lang/String;Lcom/mapbox/mapboxsdk/style/sources/Source;)V"), + MAKE_NATIVE_METHOD(nativeGetSource, "(JLjava/lang/String;)Lcom/mapbox/mapboxsdk/style/sources/Source;"), + MAKE_NATIVE_METHOD(nativeAddSource, "(JJ)V"), MAKE_NATIVE_METHOD(nativeRemoveSource, "(JLjava/lang/String;)V"), MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V"), MAKE_NATIVE_METHOD(nativeScheduleTakeSnapshot, "(J)V"), diff --git a/platform/android/src/style/android_geojson.hpp b/platform/android/src/style/conversion/geojson.hpp similarity index 86% rename from platform/android/src/style/android_geojson.hpp rename to platform/android/src/style/conversion/geojson.hpp index b2e0ca0a4ad..920c670fcbb 100644 --- a/platform/android/src/style/android_geojson.hpp +++ b/platform/android/src/style/conversion/geojson.hpp @@ -1,6 +1,6 @@ #pragma once -#include "value.hpp" +#include "../value.hpp" #include #include @@ -43,6 +43,15 @@ Result convertGeoJSON(const mbgl::android::Value& value) { return geoJSON; } +template <> +struct Converter { + + Result operator()(const mbgl::android::Value& value) const { + return convertGeoJSON(value); + } + +}; + } // namespace conversion } // namespace style } // namespace mbgl \ No newline at end of file diff --git a/platform/android/src/style/conversion/url_or_tileset.hpp b/platform/android/src/style/conversion/url_or_tileset.hpp new file mode 100644 index 00000000000..c1801f56d09 --- /dev/null +++ b/platform/android/src/style/conversion/url_or_tileset.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include +#include + +#include +#include +#include + +#include + +#include + +namespace mbgl { +namespace style { +namespace conversion { + +template <> +struct Converter> { + + template + Result> operator()(const V& value) const { + if (isObject(value)) { + Result tileset = convert(value); + if (!tileset) { + return tileset.error(); + } + return *tileset; + } else { + return *toString(value); + } + } + +}; + +} +} +} \ No newline at end of file diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp new file mode 100644 index 00000000000..cb03cc06c62 --- /dev/null +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -0,0 +1,77 @@ +#include "geojson_source.hpp" + +#include "../android_conversion.hpp" +#include "../conversion/geojson.hpp" +#include +#include + +#include + +namespace mbgl { +namespace android { + + GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<> options) + : Source(env, std::make_unique( + jni::Make(env, sourceId), + options ? *style::conversion::convert(Value(env, options)) : style::GeoJSONOptions() + ) + ) { + } + + GeoJSONSource::GeoJSONSource(mbgl::Map& map, mbgl::style::GeoJSONSource& coreSource) + : Source(map, coreSource) { + } + + GeoJSONSource::~GeoJSONSource() = default; + + void GeoJSONSource::setGeoJSON(jni::JNIEnv& env, jni::Object<> json) { + using namespace mbgl::style::conversion; + + //Convert the jni object + Result converted = convert(Value(env, json)); + if(!converted) { + mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + converted.error().message); + return; + } + + //Update the core source + source.as()->GeoJSONSource::setGeoJSON(*converted); + + //Repaint + updateStyle(false); + } + + void GeoJSONSource::setURL(jni::JNIEnv& env, jni::String url) { + //Update the core source + source.as()->GeoJSONSource::setURL(jni::Make(env, url)); + + //Repaint + updateStyle(false); + } + + jni::Class GeoJSONSource::javaClass; + + jni::jobject* GeoJSONSource::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = GeoJSONSource::javaClass.template GetConstructor(env); + return GeoJSONSource::javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void GeoJSONSource::registerNative(jni::JNIEnv& env) { + //Lookup the class + GeoJSONSource::javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + //Register the peer + jni::RegisterNativePeer( + env, GeoJSONSource::javaClass, "nativePtr", + std::make_unique>, + "initialize", + "finalize", + METHOD(&GeoJSONSource::setGeoJSON, "nativeSetGeoJson"), + METHOD(&GeoJSONSource::setURL, "nativeSetUrl") + ); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/geojson_source.hpp b/platform/android/src/style/sources/geojson_source.hpp new file mode 100644 index 00000000000..10c51e81b25 --- /dev/null +++ b/platform/android/src/style/sources/geojson_source.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "source.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class GeoJSONSource : public Source { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/GeoJsonSource"; }; + + static jni::Class javaClass; + + static void registerNative(jni::JNIEnv&); + + GeoJSONSource(jni::JNIEnv&, jni::String, jni::Object<>); + + GeoJSONSource(mbgl::Map&, mbgl::style::GeoJSONSource&); + + ~GeoJSONSource(); + + void setGeoJSON(jni::JNIEnv&, jni::Object<>); + + void setURL(jni::JNIEnv&, jni::String); + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class GeoJSONSource + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp new file mode 100644 index 00000000000..b56b56676da --- /dev/null +++ b/platform/android/src/style/sources/raster_source.cpp @@ -0,0 +1,54 @@ +#include "raster_source.hpp" + +#include "../android_conversion.hpp" +#include "../value.hpp" +#include "../conversion/url_or_tileset.hpp" + +#include + +#include + +namespace mbgl { +namespace android { + + RasterSource::RasterSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<> urlOrTileSet, jni::jint tileSize) + : Source( + env, + std::make_unique( + jni::Make(env, sourceId), + *style::conversion::convert>(Value(env, urlOrTileSet)), + tileSize + ) + ) { + } + + RasterSource::RasterSource(mbgl::Map& map, mbgl::style::RasterSource& coreSource) + : Source(map, coreSource) { + } + + RasterSource::~RasterSource() = default; + + jni::Class RasterSource::javaClass; + + jni::jobject* RasterSource::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = RasterSource::javaClass.template GetConstructor(env); + return RasterSource::javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void RasterSource::registerNative(jni::JNIEnv& env) { + //Lookup the class + RasterSource::javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + //Register the peer + jni::RegisterNativePeer( + env, RasterSource::javaClass, "nativePtr", + std::make_unique, jni::jint>, + "initialize", + "finalize" + ); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/raster_source.hpp b/platform/android/src/style/sources/raster_source.hpp new file mode 100644 index 00000000000..6600096f6de --- /dev/null +++ b/platform/android/src/style/sources/raster_source.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "source.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class RasterSource : public Source { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/RasterSource"; }; + + static jni::Class javaClass; + + static void registerNative(jni::JNIEnv&); + + RasterSource(jni::JNIEnv&, jni::String, jni::Object<>, jni::jint); + + RasterSource(mbgl::Map&, mbgl::style::RasterSource&); + + ~RasterSource(); + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class RasterSource + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/source.cpp b/platform/android/src/style/sources/source.cpp new file mode 100644 index 00000000000..4f306e7c54a --- /dev/null +++ b/platform/android/src/style/sources/source.cpp @@ -0,0 +1,72 @@ +#include "source.hpp" +#include "../android_conversion.hpp" + +#include + +#include + +//Java -> C++ conversion +#include +#include + +//C++ -> Java conversion +#include "../conversion/property_value.hpp" + +#include + +namespace mbgl { +namespace android { + + /** + * Invoked when the construction is initiated from the jvm through a subclass + */ + Source::Source(jni::JNIEnv&, std::unique_ptr coreSource) + : ownedSource(std::move(coreSource)) + , source(*ownedSource) { + } + + Source::Source(mbgl::Map& coreMap, mbgl::style::Source& coreSource) : source(coreSource) , map(&coreMap) { + } + + Source::~Source() { + } + + jni::String Source::getId(jni::JNIEnv& env) { + return jni::Make(env, source.getID()); + } + + void Source::updateStyle(jni::jboolean updateClasses) { + //Update the style only if attached + if (ownedSource == nullptr) { + Update flags = mbgl::Update::RecalculateStyle; + if(updateClasses) { + flags = flags | mbgl::Update::Classes; + } + map->update(flags); + } else { + mbgl::Log::Debug(mbgl::Event::JNI, "Not updating as source is not attached to map (yet)"); + } + } + + std::unique_ptr Source::releaseCoreSource() { + assert(ownedSource != nullptr); + return std::move(ownedSource); + } + + jni::Class Source::javaClass; + + void Source::registerNative(jni::JNIEnv& env) { + //Lookup the class + Source::javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + //Register the peer + jni::RegisterNativePeer(env, Source::javaClass, "nativePtr", + METHOD(&Source::getId, "nativeGetId") + ); + + } + +} //android +} //mbgl \ No newline at end of file diff --git a/platform/android/src/style/sources/source.hpp b/platform/android/src/style/sources/source.hpp new file mode 100644 index 00000000000..7fdc43a8335 --- /dev/null +++ b/platform/android/src/style/sources/source.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +#include "../value.hpp" + +#include + +namespace mbgl { +namespace android { + +class Source : private mbgl::util::noncopyable { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/Source"; }; + + static jni::Class javaClass; + + static void registerNative(jni::JNIEnv&); + + /* + * Called when a Java object is created on the c++ side + */ + Source(mbgl::Map&, mbgl::style::Source&); + + /* + * Called when a Java object was created from the jvm side + */ + Source(jni::JNIEnv&, std::unique_ptr); + + virtual ~Source(); + + virtual jni::jobject* createJavaPeer(jni::JNIEnv&) = 0; + + jni::String getId(jni::JNIEnv&); + + //Release the owned view and return it + std::unique_ptr releaseCoreSource(); + +protected: + void updateStyle(jni::jboolean); + + std::unique_ptr ownedSource; + mbgl::style::Source& source; + mbgl::Map* map; + +}; + +} //android +} //mbgl + + + + diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp index 47c9757e9d3..210acd607f2 100644 --- a/platform/android/src/style/sources/sources.cpp +++ b/platform/android/src/style/sources/sources.cpp @@ -1,28 +1,46 @@ #include "sources.hpp" -#include "../value.hpp" -#include "../android_conversion.hpp" -#include "../android_geojson.hpp" +#include +#include +#include +#include -#include -#include -#include - -#include +#include "source.hpp" +#include "geojson_source.hpp" +#include "raster_source.hpp" +#include "vector_source.hpp" namespace mbgl { namespace android { - mbgl::optional> convertToNativeSource(jni::JNIEnv& env, jni::Object jvalue, jni::String id) { - using namespace mbgl::style; - - Value value(env, jvalue); - conversion::Result> source = conversion::convert>(value, jni::Make(env, id)); - if (!source) { - mbgl::Log::Error(mbgl::Event::JNI, "Unable to add source: " + source.error().message); - return {}; - } - return std::move(*source); +Source* initializeSourcePeer(mbgl::Map& map, mbgl::style::Source& coreSource) { + Source* source; + if (coreSource.is()) { + source = new VectorSource(map, *coreSource.as()); + } else if (coreSource.is()) { + source = new RasterSource(map, *coreSource.as()); + } else if (coreSource.is()) { + source = new GeoJSONSource(map, *coreSource.as()); + } else { + throw new std::runtime_error("Source type not implemented"); } + + return source; +} + +jni::jobject* createJavaSourcePeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style::Source& coreSource) { + std::unique_ptr peerSource = std::unique_ptr(initializeSourcePeer(map, coreSource)); + jni::jobject* result = peerSource->createJavaPeer(env); + peerSource.release(); + return result; +} + +void registerNativeSources(jni::JNIEnv& env) { + Source::registerNative(env); + VectorSource::registerNative(env); + RasterSource::registerNative(env); + GeoJSONSource::registerNative(env); +} + } } \ No newline at end of file diff --git a/platform/android/src/style/sources/sources.hpp b/platform/android/src/style/sources/sources.hpp index b967685dfba..30388737330 100644 --- a/platform/android/src/style/sources/sources.hpp +++ b/platform/android/src/style/sources/sources.hpp @@ -1,14 +1,20 @@ #pragma once +#include #include -#include + +#include "source.hpp" #include namespace mbgl { namespace android { - - mbgl::optional> convertToNativeSource(jni::JNIEnv& env, jni::Object jsource, jni::String id); + + mbgl::android::Source* initializeSourcePeer(mbgl::Map&, mbgl::style::Source&); + + jni::jobject* createJavaSourcePeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Source&); + + void registerNativeSources(jni::JNIEnv&); } } \ No newline at end of file diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp new file mode 100644 index 00000000000..0d065a33481 --- /dev/null +++ b/platform/android/src/style/sources/vector_source.cpp @@ -0,0 +1,53 @@ +#include "vector_source.hpp" + +#include "../android_conversion.hpp" +#include "../value.hpp" +#include "../conversion/url_or_tileset.hpp" + +#include + +#include + +namespace mbgl { +namespace android { + + VectorSource::VectorSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<> urlOrTileSet) + : Source( + env, + std::make_unique( + jni::Make(env, sourceId), + *style::conversion::convert>(Value(env, urlOrTileSet)) + ) + ) { + } + + VectorSource::VectorSource(mbgl::Map& map, mbgl::style::VectorSource& coreSource) + : Source(map, coreSource) { + } + + VectorSource::~VectorSource() = default; + + jni::Class VectorSource::javaClass; + + jni::jobject* VectorSource::createJavaPeer(jni::JNIEnv& env) { + static auto constructor = VectorSource::javaClass.template GetConstructor(env); + return VectorSource::javaClass.New(env, constructor, reinterpret_cast(this)); + } + + void VectorSource::registerNative(jni::JNIEnv& env) { + //Lookup the class + VectorSource::javaClass = *jni::Class::Find(env).NewGlobalRef(env).release(); + + #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod(name) + + //Register the peer + jni::RegisterNativePeer( + env, VectorSource::javaClass, "nativePtr", + std::make_unique>, + "initialize", + "finalize" + ); + } + +} // namespace android +} // namespace mbgl diff --git a/platform/android/src/style/sources/vector_source.hpp b/platform/android/src/style/sources/vector_source.hpp new file mode 100644 index 00000000000..95d22ef7b7b --- /dev/null +++ b/platform/android/src/style/sources/vector_source.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "source.hpp" +#include +#include + +namespace mbgl { +namespace android { + +class VectorSource : public Source { +public: + + static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/sources/VectorSource"; }; + + static jni::Class javaClass; + + static void registerNative(jni::JNIEnv&); + + VectorSource(jni::JNIEnv&, jni::String, jni::Object<>); + + VectorSource(mbgl::Map&, mbgl::style::VectorSource&); + + ~VectorSource(); + + jni::jobject* createJavaPeer(jni::JNIEnv&); + +}; // class VectorSource + +} // namespace android +} // namespace mbgl