summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorIvo van Dongen <info@ivovandongen.nl>2016-07-08 18:31:55 +0200
committerIvo van Dongen <info@ivovandongen.nl>2016-07-26 16:29:26 -0400
commit9a3eadfd5baa8b070bf8d34fbd90ecae964f170c (patch)
treee8120b8f52821a9650ea2387d8eaf393404e0a0b /platform
parentb3c914fb3f52f6a2b96d663f1c57c97c34eb5e23 (diff)
downloadqtlocation-mapboxgl-9a3eadfd5baa8b070bf8d34fbd90ecae964f170c.tar.gz
[android] #5610 - Initial Runtime Style Bindings
[android] #5610 - Adjusted public api after discussion [android] #5610 - added jni binding for NativeMapView#getLayer [android] #5610 - Added initial test Activity [android] #5610 - Started on NativePeer implementation for Layer [android] #5610 - replaced low-level jni code with high-level for native getLayer [android] 5610 - completed basic layer peer class - constructible from java and c++ [android] #5610 - removed reference that was redundant and causing the finalizer exception [android] #5610 - Added a property peer [android] #5610 - added value type to do type conversions - wip [android] #5610 - simplified property conversion approach - wrapped value with jenv [android] #5610 - added some more value conversions [android] #5610 - Finished conversion for basic types [android] #5610 - allow color's to be set as either an android color int or a String to support all formats in the spec [android] #5610 - encode color ints as rgba to retain alpha channel [android] #5610 - recalculate classes after paint property [android] #5610 - more examples [android] #5610 - fixed the example [android] #5610 - cleaned up example code before continueing [android] #5610 - added layout property example [android] #5610 - set visibility on layer [android] #5610 - added removeLayer and example [android] #5610 - added more type conversions [android] #5610 - Started on peer classes for layer implementations - WIP [android] #5610 - First complete layer subclass peer implementation [android] #5610 - added a little bit of structure before adding the other layer types [android] #5610 - generate the c++ headers from the style spec [android] #5610 - make sure the visibility is set as a string [android] #5610 - Generate c++ layer peer class implementations from the style spec [android] #5610 - generate java layer peer classes [android] #5610 - cleanup comments [android] #5610 - register all c++ peer classes with jni [android] #5610 - addLayer [android] #5610 - comment out broken case [android] #5610 - Sources api - very much WIP [android] 5610 - GeoJson source implementation and geojson conversion [android] #5610 - cleanup add source/layer example a bit [android] #5610 - initial filter api [android] #5610 - Added filter api on the relevant Layer classes [android] #5610 - raster layer is not filterable [android] #5610 - actually make it compile [android] #5610 - completed filter implementation [android] #5610 - Vector and Raster Source + examples [android] #5610 - removed superfluous interface, moved filters to the correct package [android] #5610 - fixed comments [android] #5610 - moved tests to the right package [android] #5610 - hide difference between paint and layout properties in public api, make more performant set method for proeprties [android] #5610 - fix rebase issue
Diffstat (limited to 'platform')
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java36
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java39
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java19
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java115
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java68
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java11
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java9
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java226
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java403
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java31
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java17
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java16
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java18
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml8
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java299
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml37
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson2283
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml3
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java90
-rw-r--r--platform/android/platform.gyp13
-rw-r--r--platform/android/scripts/generate-style-code.js114
-rw-r--r--platform/android/scripts/layer.cpp.ejs64
-rw-r--r--platform/android/scripts/layer.hpp.ejs38
-rw-r--r--platform/android/scripts/layer.java.ejs44
-rw-r--r--platform/android/scripts/layer_property.java.ejs42
-rw-r--r--platform/android/scripts/layer_property_factory.java.ejs45
-rw-r--r--platform/android/src/java_types.cpp37
-rw-r--r--platform/android/src/java_types.hpp35
-rwxr-xr-xplatform/android/src/jni.cpp87
-rw-r--r--platform/android/src/style/android_conversion.hpp95
-rw-r--r--platform/android/src/style/android_geojson.hpp48
-rw-r--r--platform/android/src/style/layers/background_layer.cpp52
-rw-r--r--platform/android/src/style/layers/background_layer.hpp31
-rw-r--r--platform/android/src/style/layers/circle_layer.cpp52
-rw-r--r--platform/android/src/style/layers/circle_layer.hpp31
-rw-r--r--platform/android/src/style/layers/fill_layer.cpp52
-rw-r--r--platform/android/src/style/layers/fill_layer.hpp31
-rw-r--r--platform/android/src/style/layers/layer.cpp158
-rw-r--r--platform/android/src/style/layers/layer.hpp66
-rw-r--r--platform/android/src/style/layers/layers.cpp60
-rw-r--r--platform/android/src/style/layers/layers.hpp20
-rw-r--r--platform/android/src/style/layers/line_layer.cpp52
-rw-r--r--platform/android/src/style/layers/line_layer.hpp31
-rw-r--r--platform/android/src/style/layers/raster_layer.cpp52
-rw-r--r--platform/android/src/style/layers/raster_layer.hpp31
-rw-r--r--platform/android/src/style/layers/symbol_layer.cpp52
-rw-r--r--platform/android/src/style/layers/symbol_layer.hpp31
-rw-r--r--platform/android/src/style/sources/sources.cpp28
-rw-r--r--platform/android/src/style/sources/sources.hpp14
-rw-r--r--platform/android/src/style/value.cpp67
-rw-r--r--platform/android/src/style/value.hpp35
59 files changed, 5506 insertions, 1 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index a1a160dfcb..dafece6641 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -2660,6 +2660,10 @@ public class MapView extends FrameLayout {
return mMyLocationView;
}
+ NativeMapView getNativeMapView() {
+ return mNativeMapView;
+ }
+
@UiThread
void snapshot(@NonNull final MapboxMap.SnapshotReadyCallback callback, @Nullable final Bitmap bitmap) {
TextureView textureView = (TextureView) findViewById(R.id.textureView);
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 85cb9fabae..85287a62c1 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
@@ -40,6 +40,9 @@ import com.mapbox.mapboxsdk.constants.Style;
import com.mapbox.mapboxsdk.geometry.LatLng;
import com.mapbox.mapboxsdk.layers.CustomLayer;
import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
+import com.mapbox.mapboxsdk.style.sources.Source;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
@@ -103,6 +106,39 @@ public class MapboxMap {
mMarkerViewManager = new MarkerViewManager(this, mapView);
}
+ // Style
+
+ @Nullable
+ @UiThread
+ public Layer getLayer(@NonNull String layerId) {
+ return getMapView().getNativeMapView().getLayer(layerId);
+ }
+
+ @UiThread
+ public void addLayer(@NonNull Layer layer) {
+ addLayer(layer, null);
+ }
+
+ @UiThread
+ public void addLayer(@NonNull Layer layer, String before) {
+ getMapView().getNativeMapView().addLayer(layer, before);
+ }
+
+ @UiThread
+ public void removeLayer(@NonNull String layerId) throws NoSuchLayerException {
+ getMapView().getNativeMapView().removeLayer(layerId);
+ }
+
+ @UiThread
+ public void addSource(@NonNull Source source) {
+ getMapView().getNativeMapView().addSource(source);
+ }
+
+ @UiThread
+ public void removeSource(@NonNull String sourceId) {
+ getMapView().getNativeMapView().removeSource(sourceId);
+ }
+
//
// MinZoom
//
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 d050f76843..c6cb48fdc3 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
@@ -5,6 +5,8 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.view.Surface;
import com.mapbox.mapboxsdk.annotations.Icon;
@@ -16,6 +18,9 @@ import com.mapbox.mapboxsdk.geometry.LatLngBounds;
import com.mapbox.mapboxsdk.geometry.ProjectedMeters;
import com.mapbox.mapboxsdk.layers.CustomLayer;
import com.mapbox.mapboxsdk.offline.OfflineManager;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
+import com.mapbox.mapboxsdk.style.sources.Source;
import java.util.List;
@@ -470,6 +475,28 @@ final class NativeMapView {
return nativeGetCameraValues(mNativeMapViewPtr);
}
+ // Runtime style Api
+
+ public Layer getLayer(String layerId) {
+ return nativeGetLayer(mNativeMapViewPtr, layerId);
+ }
+
+ public void addLayer(@NonNull Layer layer, @Nullable String before) {
+ nativeAddLayer(mNativeMapViewPtr, layer.getNativePtr(), before);
+ }
+
+ public void removeLayer(@NonNull String layerId) throws NoSuchLayerException {
+ nativeRemoveLayer(mNativeMapViewPtr, layerId);
+ }
+
+ public void addSource(@NonNull Source source) {
+ nativeAddSource(mNativeMapViewPtr, source.getId(), source);
+ }
+
+ public void removeSource(@NonNull String sourceId) {
+ nativeRemoveSource(mNativeMapViewPtr, sourceId);
+ }
+
//
// Callbacks
//
@@ -633,7 +660,7 @@ final class NativeMapView {
private native LatLng nativeLatLngForPixel(long nativeMapViewPtr, float x, float y);
private native double nativeGetTopOffsetPixelsForAnnotationSymbol(long nativeMapViewPtr, String symbolName);
-
+
private native void nativeJumpTo(long nativeMapViewPtr, double angle, double latitude, double longitude, double pitch, double zoom);
private native void nativeEaseTo(long nativeMapViewPtr, double angle, double latitude, double longitude, long duration, double pitch, double zoom, boolean easingInterpolator);
@@ -645,4 +672,14 @@ final class NativeMapView {
private native void nativeRemoveCustomLayer(long nativeMapViewPtr, String id);
private native double[] nativeGetCameraValues(long mNativeMapViewPtr);
+
+ private native Layer nativeGetLayer(long nativeMapViewPtr, String layerId);
+
+ private native void nativeAddLayer(long nativeMapViewPtr, long layerPtr, String before);
+
+ private native void nativeRemoveLayer(long nativeMapViewPtr, String layerId) throws NoSuchLayerException;
+
+ private native void nativeAddSource(long mNativeMapViewPtr, String id, Source source);
+
+ private native void nativeRemoveSource(long mNativeMapViewPtr, String sourceId);
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
new file mode 100644
index 0000000000..7dcd9eee46
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/BackgroundLayer.java
@@ -0,0 +1,19 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * Background Layer
+ */
+public class BackgroundLayer extends Layer {
+
+ public BackgroundLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public BackgroundLayer(String layerId) {
+ initialize(layerId);
+ }
+
+ protected native void initialize(String layerId);
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
new file mode 100644
index 0000000000..8562ef1bf4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CircleLayer.java
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * Circle Layer
+ */
+public class CircleLayer extends Layer {
+
+ public CircleLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public CircleLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ nativeSetFilter(filter);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
new file mode 100644
index 0000000000..b3eb5a39c1
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/FillLayer.java
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * Fill Layer
+ */
+public class FillLayer extends Layer {
+
+ public FillLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public FillLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ nativeSetFilter(filter);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
new file mode 100644
index 0000000000..04da4da0cb
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Filter.java
@@ -0,0 +1,115 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * Utility to build filter expressions more easily:
+ *
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#types-filter">Style spec</a>
+ */
+public class Filter {
+
+ public abstract static class Statement {
+ protected final String operator;
+
+ public Statement(String operator) {
+ this.operator = operator;
+ }
+
+ public abstract Object[] toArray();
+ }
+
+ public static class SimpleStatement extends Statement {
+ private final String key;
+ private final Object[] values;
+
+ public SimpleStatement(String operator, String key, Object... values) {
+ super(operator);
+ this.key = key;
+ this.values = values;
+ }
+
+
+ @Override
+ public Object[] toArray() {
+ ArrayList<Object> array = new ArrayList<>(2 + values.length);
+ array.add(operator);
+ array.add(key);
+ array.addAll(Arrays.asList(values));
+ return array.toArray();
+ }
+ }
+
+ public static class CompoundStatement extends Statement {
+ private final Statement[] statements;
+
+ public CompoundStatement(String operator, Statement... statements) {
+ super(operator);
+ this.statements = statements;
+ }
+
+ @Override
+ public Object[] toArray() {
+ ArrayList<Object> array = new ArrayList<>(1 + statements.length);
+ array.add(operator);
+ for (Statement statement : statements) {
+ array.add(statement.toArray());
+ }
+ return array.toArray();
+ }
+ }
+
+ public static Statement all(Statement... statements) {
+ return new CompoundStatement("all", statements);
+ }
+
+ public static Statement any(Statement... statements) {
+ return new CompoundStatement("any", statements);
+ }
+
+ public static Statement none(Statement... statements) {
+ return new CompoundStatement("none", statements);
+ }
+
+ public static Statement has(String key) {
+ return new SimpleStatement("has", key);
+ }
+
+ public static Statement notHas(String key) {
+ return new SimpleStatement("!has", key);
+ }
+
+ public static Statement eq(String key, Object value) {
+ return new SimpleStatement("==", key, value);
+ }
+
+ public static Statement neq(String key, Object value) {
+ return new SimpleStatement("!=", key, value);
+ }
+
+ public static Statement gt(String key, Object value) {
+ return new SimpleStatement(">", key, value);
+ }
+
+ public static Statement gte(String key, Object value) {
+ return new SimpleStatement(">=", key, value);
+ }
+
+ public static Statement lt(String key, Object value) {
+ return new SimpleStatement("<", key, value);
+ }
+
+ public static Statement lte(String key, Object value) {
+ return new SimpleStatement("<=", key, value);
+ }
+
+ public static Statement in(String key, Object... values) {
+ return new SimpleStatement("in", key, values);
+ }
+
+ public static Statement notIn(String key, Object... values) {
+ return new SimpleStatement("!in", key, values);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
new file mode 100644
index 0000000000..eb316658ce
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Layer.java
@@ -0,0 +1,68 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+/**
+ * Base class for the different Layer types
+ */
+public abstract class Layer {
+
+ private long nativePtr;
+
+ public Layer(long nativePtr) {
+ Log.i(Layer.class.getSimpleName(), "Native pointer constructor: " + nativePtr);
+ this.nativePtr = nativePtr;
+ }
+
+ public Layer() {
+ Log.i(Layer.class.getSimpleName(), "Default constructor");
+ }
+
+ public void set(@NonNull Property<?>... properties) {
+ if (properties.length == 0) {
+ return;
+ }
+
+ boolean updateClasses = false;
+ for (Property<?> property : properties) {
+ if (property instanceof PaintProperty) {
+ updateClasses = true;
+ nativeSetPaintProperty(property.name, property.value);
+ } else {
+ nativeSetLayoutProperty(property.name, property.value);
+ }
+ }
+
+ nativeUpdateStyle(updateClasses);
+ }
+
+ public String getId() {
+ return nativeGetId();
+ }
+
+ @Override
+ protected native void finalize() throws Throwable;
+
+ protected native String nativeGetId();
+
+ protected native void nativeSetLayoutProperty(String name, Object value);
+
+ protected native void nativeSetPaintProperty(String name, Object value);
+
+ protected native void nativeSetFilter(Object[] filter);
+
+ protected native void nativeSetSourceLayer(String sourceLayer);
+
+ protected native void nativeUpdateStyle(boolean updateClasses);
+
+ @Override
+ public String toString() {
+ return "Layer: " + getId();
+ }
+
+ public long getNativePtr() {
+ return nativePtr;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java
new file mode 100644
index 0000000000..553554270e
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LayoutProperty.java
@@ -0,0 +1,9 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+class LayoutProperty<T> extends Property<T> {
+
+ LayoutProperty(String name, T value) {
+ super(name, value);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
new file mode 100644
index 0000000000..36c9d53d20
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/LineLayer.java
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * Line Layer
+ */
+public class LineLayer extends Layer {
+
+ public LineLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public LineLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ nativeSetFilter(filter);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java
new file mode 100644
index 0000000000..d6ef4f8824
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/NoSuchLayerException.java
@@ -0,0 +1,11 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * No such layer.
+ */
+public class NoSuchLayerException extends Exception {
+
+ public NoSuchLayerException(String message) {
+ super(message);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java
new file mode 100644
index 0000000000..6f1a9a0a16
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PaintProperty.java
@@ -0,0 +1,9 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+class PaintProperty<T> extends Property<T> {
+
+ PaintProperty(String name, T value) {
+ super(name, value);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
new file mode 100644
index 0000000000..66cc7df111
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java
@@ -0,0 +1,226 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Paint/Layout properties for Layer
+ */
+public abstract class Property<T> {
+
+ //line-cap
+ public static final String LINE_CAP_BUTT = "butt";
+ public static final String LINE_CAP_ROUND = "round";
+ public static final String LINE_CAP_SQUARE = "square";
+
+ @StringDef({
+ LINE_CAP_BUTT,
+ LINE_CAP_ROUND,
+ LINE_CAP_SQUARE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LINE_CAP {}
+
+ //line-join
+ public static final String LINE_JOIN_BEVEL = "bevel";
+ public static final String LINE_JOIN_ROUND = "round";
+ public static final String LINE_JOIN_MITER = "miter";
+
+ @StringDef({
+ LINE_JOIN_BEVEL,
+ LINE_JOIN_ROUND,
+ LINE_JOIN_MITER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LINE_JOIN {}
+
+ //symbol-placement
+ public static final String SYMBOL_PLACEMENT_POINT = "point";
+ public static final String SYMBOL_PLACEMENT_LINE = "line";
+
+ @StringDef({
+ SYMBOL_PLACEMENT_POINT,
+ SYMBOL_PLACEMENT_LINE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SYMBOL_PLACEMENT {}
+
+ //icon-rotation-alignment
+ public static final String ICON_ROTATION_ALIGNMENT_MAP = "map";
+ public static final String ICON_ROTATION_ALIGNMENT_VIEWPORT = "viewport";
+
+ @StringDef({
+ ICON_ROTATION_ALIGNMENT_MAP,
+ ICON_ROTATION_ALIGNMENT_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_ROTATION_ALIGNMENT {}
+
+ //icon-text-fit
+ public static final String ICON_TEXT_FIT_NONE = "none";
+ public static final String ICON_TEXT_FIT_BOTH = "both";
+ public static final String ICON_TEXT_FIT_WIDTH = "width";
+ public static final String ICON_TEXT_FIT_HEIGHT = "height";
+
+ @StringDef({
+ ICON_TEXT_FIT_NONE,
+ ICON_TEXT_FIT_BOTH,
+ ICON_TEXT_FIT_WIDTH,
+ ICON_TEXT_FIT_HEIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_TEXT_FIT {}
+
+ //text-pitch-alignment
+ public static final String TEXT_PITCH_ALIGNMENT_MAP = "map";
+ public static final String TEXT_PITCH_ALIGNMENT_VIEWPORT = "viewport";
+
+ @StringDef({
+ TEXT_PITCH_ALIGNMENT_MAP,
+ TEXT_PITCH_ALIGNMENT_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_PITCH_ALIGNMENT {}
+
+ //text-rotation-alignment
+ public static final String TEXT_ROTATION_ALIGNMENT_MAP = "map";
+ public static final String TEXT_ROTATION_ALIGNMENT_VIEWPORT = "viewport";
+
+ @StringDef({
+ TEXT_ROTATION_ALIGNMENT_MAP,
+ TEXT_ROTATION_ALIGNMENT_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_ROTATION_ALIGNMENT {}
+
+ //text-justify
+ public static final String TEXT_JUSTIFY_LEFT = "left";
+ public static final String TEXT_JUSTIFY_CENTER = "center";
+ public static final String TEXT_JUSTIFY_RIGHT = "right";
+
+ @StringDef({
+ TEXT_JUSTIFY_LEFT,
+ TEXT_JUSTIFY_CENTER,
+ TEXT_JUSTIFY_RIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_JUSTIFY {}
+
+ //text-anchor
+ public static final String TEXT_ANCHOR_CENTER = "center";
+ public static final String TEXT_ANCHOR_LEFT = "left";
+ public static final String TEXT_ANCHOR_RIGHT = "right";
+ public static final String TEXT_ANCHOR_TOP = "top";
+ public static final String TEXT_ANCHOR_BOTTOM = "bottom";
+ public static final String TEXT_ANCHOR_TOP_LEFT = "top-left";
+ public static final String TEXT_ANCHOR_TOP_RIGHT = "top-right";
+ public static final String TEXT_ANCHOR_BOTTOM_LEFT = "bottom-left";
+ public static final String TEXT_ANCHOR_BOTTOM_RIGHT = "bottom-right";
+
+ @StringDef({
+ TEXT_ANCHOR_CENTER,
+ TEXT_ANCHOR_LEFT,
+ TEXT_ANCHOR_RIGHT,
+ TEXT_ANCHOR_TOP,
+ TEXT_ANCHOR_BOTTOM,
+ TEXT_ANCHOR_TOP_LEFT,
+ TEXT_ANCHOR_TOP_RIGHT,
+ TEXT_ANCHOR_BOTTOM_LEFT,
+ TEXT_ANCHOR_BOTTOM_RIGHT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_ANCHOR {}
+
+ //text-transform
+ public static final String TEXT_TRANSFORM_NONE = "none";
+ public static final String TEXT_TRANSFORM_UPPERCASE = "uppercase";
+ public static final String TEXT_TRANSFORM_LOWERCASE = "lowercase";
+
+ @StringDef({
+ TEXT_TRANSFORM_NONE,
+ TEXT_TRANSFORM_UPPERCASE,
+ TEXT_TRANSFORM_LOWERCASE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_TRANSFORM {}
+
+ //fill-translate-anchor
+ public static final String FILL_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String FILL_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ FILL_TRANSLATE_ANCHOR_MAP,
+ FILL_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface FILL_TRANSLATE_ANCHOR {}
+
+ //line-translate-anchor
+ public static final String LINE_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String LINE_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ LINE_TRANSLATE_ANCHOR_MAP,
+ LINE_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface LINE_TRANSLATE_ANCHOR {}
+
+ //icon-translate-anchor
+ public static final String ICON_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String ICON_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ ICON_TRANSLATE_ANCHOR_MAP,
+ ICON_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ICON_TRANSLATE_ANCHOR {}
+
+ //text-translate-anchor
+ public static final String TEXT_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String TEXT_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ TEXT_TRANSLATE_ANCHOR_MAP,
+ TEXT_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TEXT_TRANSLATE_ANCHOR {}
+
+ //circle-translate-anchor
+ public static final String CIRCLE_TRANSLATE_ANCHOR_MAP = "map";
+ public static final String CIRCLE_TRANSLATE_ANCHOR_VIEWPORT = "viewport";
+
+ @StringDef({
+ CIRCLE_TRANSLATE_ANCHOR_MAP,
+ CIRCLE_TRANSLATE_ANCHOR_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CIRCLE_TRANSLATE_ANCHOR {}
+
+ //circle-pitch-scale
+ public static final String CIRCLE_PITCH_SCALE_MAP = "map";
+ public static final String CIRCLE_PITCH_SCALE_VIEWPORT = "viewport";
+
+ @StringDef({
+ CIRCLE_PITCH_SCALE_MAP,
+ CIRCLE_PITCH_SCALE_VIEWPORT,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CIRCLE_PITCH_SCALE {}
+
+
+ //Class definition
+ public final String name;
+ public final T value;
+
+ /* package */ Property(String name, T value) {
+ this.name = name;
+ this.value = value;
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
new file mode 100644
index 0000000000..4dcec0d9b3
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java
@@ -0,0 +1,403 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.ColorInt;
+
+/**
+ * Constructs paint/layout properties for Layers
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers>Layer style documentation</a>
+ *
+ */
+public class PropertyFactory {
+
+ public static Property<String> visibility(Boolean visible) {
+ return new LayoutProperty<>("visibility", visible? "visible": "none");
+ }
+
+ public static Property<Boolean> fillAntialias(Boolean value) {
+ return new PaintProperty<>("fill-antialias", value);
+ }
+
+ public static Property<Float> fillOpacity(Float value) {
+ return new PaintProperty<>("fill-opacity", value);
+ }
+
+ public static Property<String> fillColor(@ColorInt int value) {
+ return new PaintProperty<>("fill-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> fillColor(String value) {
+ return new PaintProperty<>("fill-color", value);
+ }
+
+ public static Property<String> fillOutlineColor(@ColorInt int value) {
+ return new PaintProperty<>("fill-outline-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> fillOutlineColor(String value) {
+ return new PaintProperty<>("fill-outline-color", value);
+ }
+
+ public static Property<Float[]> fillTranslate(Float[] value) {
+ return new PaintProperty<>("fill-translate", value);
+ }
+
+ public static Property<String> fillTranslateAnchor(@Property.FILL_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("fill-translate-anchor", value);
+ }
+
+ public static Property<String> fillPattern(String value) {
+ return new PaintProperty<>("fill-pattern", value);
+ }
+
+ public static Property<Float> lineOpacity(Float value) {
+ return new PaintProperty<>("line-opacity", value);
+ }
+
+ public static Property<String> lineColor(@ColorInt int value) {
+ return new PaintProperty<>("line-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> lineColor(String value) {
+ return new PaintProperty<>("line-color", value);
+ }
+
+ public static Property<Float[]> lineTranslate(Float[] value) {
+ return new PaintProperty<>("line-translate", value);
+ }
+
+ public static Property<String> lineTranslateAnchor(@Property.LINE_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("line-translate-anchor", value);
+ }
+
+ public static Property<Float> lineWidth(Float value) {
+ return new PaintProperty<>("line-width", value);
+ }
+
+ public static Property<Float> lineGapWidth(Float value) {
+ return new PaintProperty<>("line-gap-width", value);
+ }
+
+ public static Property<Float> lineOffset(Float value) {
+ return new PaintProperty<>("line-offset", value);
+ }
+
+ public static Property<Float> lineBlur(Float value) {
+ return new PaintProperty<>("line-blur", value);
+ }
+
+ public static Property<Float[]> lineDasharray(Float[] value) {
+ return new PaintProperty<>("line-dasharray", value);
+ }
+
+ public static Property<String> linePattern(String value) {
+ return new PaintProperty<>("line-pattern", value);
+ }
+
+ public static Property<Float> iconOpacity(Float value) {
+ return new PaintProperty<>("icon-opacity", value);
+ }
+
+ public static Property<String> iconColor(@ColorInt int value) {
+ return new PaintProperty<>("icon-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> iconColor(String value) {
+ return new PaintProperty<>("icon-color", value);
+ }
+
+ public static Property<String> iconHaloColor(@ColorInt int value) {
+ return new PaintProperty<>("icon-halo-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> iconHaloColor(String value) {
+ return new PaintProperty<>("icon-halo-color", value);
+ }
+
+ public static Property<Float> iconHaloWidth(Float value) {
+ return new PaintProperty<>("icon-halo-width", value);
+ }
+
+ public static Property<Float> iconHaloBlur(Float value) {
+ return new PaintProperty<>("icon-halo-blur", value);
+ }
+
+ public static Property<Float[]> iconTranslate(Float[] value) {
+ return new PaintProperty<>("icon-translate", value);
+ }
+
+ public static Property<String> iconTranslateAnchor(@Property.ICON_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("icon-translate-anchor", value);
+ }
+
+ public static Property<Float> textOpacity(Float value) {
+ return new PaintProperty<>("text-opacity", value);
+ }
+
+ public static Property<String> textColor(@ColorInt int value) {
+ return new PaintProperty<>("text-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> textColor(String value) {
+ return new PaintProperty<>("text-color", value);
+ }
+
+ public static Property<String> textHaloColor(@ColorInt int value) {
+ return new PaintProperty<>("text-halo-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> textHaloColor(String value) {
+ return new PaintProperty<>("text-halo-color", value);
+ }
+
+ public static Property<Float> textHaloWidth(Float value) {
+ return new PaintProperty<>("text-halo-width", value);
+ }
+
+ public static Property<Float> textHaloBlur(Float value) {
+ return new PaintProperty<>("text-halo-blur", value);
+ }
+
+ public static Property<Float[]> textTranslate(Float[] value) {
+ return new PaintProperty<>("text-translate", value);
+ }
+
+ public static Property<String> textTranslateAnchor(@Property.TEXT_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("text-translate-anchor", value);
+ }
+
+ public static Property<Float> circleRadius(Float value) {
+ return new PaintProperty<>("circle-radius", value);
+ }
+
+ public static Property<String> circleColor(@ColorInt int value) {
+ return new PaintProperty<>("circle-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> circleColor(String value) {
+ return new PaintProperty<>("circle-color", value);
+ }
+
+ public static Property<Float> circleBlur(Float value) {
+ return new PaintProperty<>("circle-blur", value);
+ }
+
+ public static Property<Float> circleOpacity(Float value) {
+ return new PaintProperty<>("circle-opacity", value);
+ }
+
+ public static Property<Float[]> circleTranslate(Float[] value) {
+ return new PaintProperty<>("circle-translate", value);
+ }
+
+ public static Property<String> circleTranslateAnchor(@Property.CIRCLE_TRANSLATE_ANCHOR String value) {
+ return new PaintProperty<>("circle-translate-anchor", value);
+ }
+
+ public static Property<String> circlePitchScale(@Property.CIRCLE_PITCH_SCALE String value) {
+ return new PaintProperty<>("circle-pitch-scale", value);
+ }
+
+ public static Property<Float> rasterOpacity(Float value) {
+ return new PaintProperty<>("raster-opacity", value);
+ }
+
+ public static Property<Float> rasterHueRotate(Float value) {
+ return new PaintProperty<>("raster-hue-rotate", value);
+ }
+
+ public static Property<Float> rasterBrightnessMin(Float value) {
+ return new PaintProperty<>("raster-brightness-min", value);
+ }
+
+ public static Property<Float> rasterBrightnessMax(Float value) {
+ return new PaintProperty<>("raster-brightness-max", value);
+ }
+
+ public static Property<Float> rasterSaturation(Float value) {
+ return new PaintProperty<>("raster-saturation", value);
+ }
+
+ public static Property<Float> rasterContrast(Float value) {
+ return new PaintProperty<>("raster-contrast", value);
+ }
+
+ public static Property<Float> rasterFadeDuration(Float value) {
+ return new PaintProperty<>("raster-fade-duration", value);
+ }
+
+ public static Property<String> backgroundColor(@ColorInt int value) {
+ return new PaintProperty<>("background-color", colorToRgbaString(value));
+ }
+
+ public static Property<String> backgroundColor(String value) {
+ return new PaintProperty<>("background-color", value);
+ }
+
+ public static Property<String> backgroundPattern(String value) {
+ return new PaintProperty<>("background-pattern", value);
+ }
+
+ public static Property<Float> backgroundOpacity(Float value) {
+ return new PaintProperty<>("background-opacity", value);
+ }
+
+ public static Property<String> lineCap(@Property.LINE_CAP String value) {
+ return new LayoutProperty<>("line-cap", value);
+ }
+
+ public static Property<String> lineJoin(@Property.LINE_JOIN String value) {
+ return new LayoutProperty<>("line-join", value);
+ }
+
+ public static Property<Float> lineMiterLimit(Float value) {
+ return new LayoutProperty<>("line-miter-limit", value);
+ }
+
+ public static Property<Float> lineRoundLimit(Float value) {
+ return new LayoutProperty<>("line-round-limit", value);
+ }
+
+ public static Property<String> symbolPlacement(@Property.SYMBOL_PLACEMENT String value) {
+ return new LayoutProperty<>("symbol-placement", value);
+ }
+
+ public static Property<Float> symbolSpacing(Float value) {
+ return new LayoutProperty<>("symbol-spacing", value);
+ }
+
+ public static Property<Boolean> symbolAvoidEdges(Boolean value) {
+ return new LayoutProperty<>("symbol-avoid-edges", value);
+ }
+
+ public static Property<Boolean> iconAllowOverlap(Boolean value) {
+ return new LayoutProperty<>("icon-allow-overlap", value);
+ }
+
+ public static Property<Boolean> iconIgnorePlacement(Boolean value) {
+ return new LayoutProperty<>("icon-ignore-placement", value);
+ }
+
+ public static Property<Boolean> iconOptional(Boolean value) {
+ return new LayoutProperty<>("icon-optional", value);
+ }
+
+ public static Property<String> iconRotationAlignment(@Property.ICON_ROTATION_ALIGNMENT String value) {
+ return new LayoutProperty<>("icon-rotation-alignment", value);
+ }
+
+ public static Property<Float> iconSize(Float value) {
+ return new LayoutProperty<>("icon-size", value);
+ }
+
+ public static Property<String> iconTextFit(@Property.ICON_TEXT_FIT String value) {
+ return new LayoutProperty<>("icon-text-fit", value);
+ }
+
+ public static Property<Float[]> iconTextFitPadding(Float[] value) {
+ return new LayoutProperty<>("icon-text-fit-padding", value);
+ }
+
+ public static Property<String> iconImage(String value) {
+ return new LayoutProperty<>("icon-image", value);
+ }
+
+ public static Property<Float> iconRotate(Float value) {
+ return new LayoutProperty<>("icon-rotate", value);
+ }
+
+ public static Property<Float> iconPadding(Float value) {
+ return new LayoutProperty<>("icon-padding", value);
+ }
+
+ public static Property<Boolean> iconKeepUpright(Boolean value) {
+ return new LayoutProperty<>("icon-keep-upright", value);
+ }
+
+ public static Property<Float[]> iconOffset(Float[] value) {
+ return new LayoutProperty<>("icon-offset", value);
+ }
+
+ public static Property<String> textPitchAlignment(@Property.TEXT_PITCH_ALIGNMENT String value) {
+ return new LayoutProperty<>("text-pitch-alignment", value);
+ }
+
+ public static Property<String> textRotationAlignment(@Property.TEXT_ROTATION_ALIGNMENT String value) {
+ return new LayoutProperty<>("text-rotation-alignment", value);
+ }
+
+ public static Property<String> textField(String value) {
+ return new LayoutProperty<>("text-field", value);
+ }
+
+ public static Property<String[]> textFont(String[] value) {
+ return new LayoutProperty<>("text-font", value);
+ }
+
+ public static Property<Float> textSize(Float value) {
+ return new LayoutProperty<>("text-size", value);
+ }
+
+ public static Property<Float> textMaxWidth(Float value) {
+ return new LayoutProperty<>("text-max-width", value);
+ }
+
+ public static Property<Float> textLineHeight(Float value) {
+ return new LayoutProperty<>("text-line-height", value);
+ }
+
+ public static Property<Float> textLetterSpacing(Float value) {
+ return new LayoutProperty<>("text-letter-spacing", value);
+ }
+
+ public static Property<String> textJustify(@Property.TEXT_JUSTIFY String value) {
+ return new LayoutProperty<>("text-justify", value);
+ }
+
+ public static Property<String> textAnchor(@Property.TEXT_ANCHOR String value) {
+ return new LayoutProperty<>("text-anchor", value);
+ }
+
+ public static Property<Float> textMaxAngle(Float value) {
+ return new LayoutProperty<>("text-max-angle", value);
+ }
+
+ public static Property<Float> textRotate(Float value) {
+ return new LayoutProperty<>("text-rotate", value);
+ }
+
+ public static Property<Float> textPadding(Float value) {
+ return new LayoutProperty<>("text-padding", value);
+ }
+
+ public static Property<Boolean> textKeepUpright(Boolean value) {
+ return new LayoutProperty<>("text-keep-upright", value);
+ }
+
+ public static Property<String> textTransform(@Property.TEXT_TRANSFORM String value) {
+ return new LayoutProperty<>("text-transform", value);
+ }
+
+ public static Property<Float[]> textOffset(Float[] value) {
+ return new LayoutProperty<>("text-offset", value);
+ }
+
+ public static Property<Boolean> textAllowOverlap(Boolean value) {
+ return new LayoutProperty<>("text-allow-overlap", value);
+ }
+
+ public static Property<Boolean> textIgnorePlacement(Boolean value) {
+ return new LayoutProperty<>("text-ignore-placement", value);
+ }
+
+ public static Property<Boolean> textOptional(Boolean value) {
+ return new LayoutProperty<>("text-optional", value);
+ }
+
+ @SuppressLint("DefaultLocale")
+ static String colorToRgbaString(@ColorInt int value) {
+ return String.format("rgba(%d, %d, %d, %d)", (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, (value >> 24) & 0xFF);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
new file mode 100644
index 0000000000..8e0817fe94
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/RasterLayer.java
@@ -0,0 +1,23 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * Raster Layer
+ */
+public class RasterLayer extends Layer {
+
+ public RasterLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public RasterLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
new file mode 100644
index 0000000000..52f42f72d5
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * Symbol Layer
+ */
+public class SymbolLayer extends Layer {
+
+ public SymbolLayer(long nativePtr) {
+ super(nativePtr);
+ }
+
+ public SymbolLayer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+
+ public void setFilter(Filter.Statement filter) {
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ nativeSetFilter(filter);
+ }
+
+}
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
new file mode 100644
index 0000000000..f1399daf12
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/GeoJsonSource.java
@@ -0,0 +1,23 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.net.URL;
+import java.util.HashMap;
+
+public class GeoJsonSource extends Source {
+ public static final String TYPE = "geojson";
+ private static final String DATA_KEY = "data";
+
+ public GeoJsonSource(String id, String geoJson) {
+ super(id, TYPE);
+ //Wrap the String in a map as an Object is expected by the
+ //style conversion template
+ HashMap<String, String> wrapper = new HashMap<>();
+ wrapper.put(DATA_KEY, geoJson);
+ this.put(DATA_KEY, wrapper);
+ }
+
+ public GeoJsonSource(String id, URL url) {
+ super(id, TYPE);
+ this.put(DATA_KEY, url.toExternalForm());
+ }
+}
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
new file mode 100644
index 0000000000..d0bc7ca2fe
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/RasterSource.java
@@ -0,0 +1,17 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.net.URL;
+
+public class RasterSource extends Source {
+ public static final String TYPE = "raster";
+ private static final String URL_KEY = "url";
+
+ public RasterSource(String id, URL url) {
+ this(id, url.toExternalForm());
+ }
+
+ public RasterSource(String id, String url) {
+ super(id, TYPE);
+ this.put(URL_KEY, url);
+ }
+}
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
new file mode 100644
index 0000000000..d9aacdc80d
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/Source.java
@@ -0,0 +1,16 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.util.HashMap;
+
+public abstract class Source extends HashMap<String, Object> {
+ private final String id;
+
+ protected Source(String id, String type) {
+ this.put("type", type);
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+}
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
new file mode 100644
index 0000000000..9fc6acba3f
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/sources/VectorSource.java
@@ -0,0 +1,18 @@
+package com.mapbox.mapboxsdk.style.sources;
+
+import java.net.URL;
+import java.util.HashMap;
+
+public class VectorSource extends Source {
+ public static final String TYPE = "vector";
+ private static final String URL_KEY = "url";
+
+ public VectorSource(String id, URL url) {
+ this(id, url.toExternalForm());
+ }
+
+ public VectorSource(String id, String url) {
+ super(id, TYPE);
+ this.put(URL_KEY, url);
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index b52f699d79..213d886c96 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -301,6 +301,14 @@
android:value="@string/category_fragment" />
</activity>
+ <activity android:name=".activity.style.RuntimeStyleActivity"
+ android:description="@string/description_runtime_style"
+ android:label="@string/activity_runtime_style">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_style" />
+ </activity>
+
<!-- Configuration Settings -->
<meta-data
android:name="com.mapbox.TestEventsServer"
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
new file mode 100644
index 0000000000..d0049eeb31
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/style/RuntimeStyleActivity.java
@@ -0,0 +1,299 @@
+package com.mapbox.mapboxsdk.testapp.activity.style;
+
+import android.graphics.Color;
+import android.os.Bundle;
+import android.support.annotation.RawRes;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.style.layers.FillLayer;
+import com.mapbox.mapboxsdk.style.layers.Layer;
+import com.mapbox.mapboxsdk.style.layers.LineLayer;
+import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
+import com.mapbox.mapboxsdk.style.layers.Property;
+import com.mapbox.mapboxsdk.style.layers.RasterLayer;
+import com.mapbox.mapboxsdk.style.sources.GeoJsonSource;
+import com.mapbox.mapboxsdk.style.sources.RasterSource;
+import com.mapbox.mapboxsdk.style.sources.Source;
+import com.mapbox.mapboxsdk.style.sources.VectorSource;
+import com.mapbox.mapboxsdk.testapp.R;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import static com.mapbox.mapboxsdk.style.layers.Filter.*;
+import static com.mapbox.mapboxsdk.style.layers.Property.*;
+import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.*;
+
+/**
+ * Sample Activity to show a typical location picker use case
+ */
+public class RuntimeStyleActivity extends AppCompatActivity {
+ private static final String TAG = RuntimeStyleActivity.class.getSimpleName();
+
+ private MapView mapView;
+ private MapboxMap mapboxMap;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_runtime_style);
+
+ setupActionBar();
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+
+
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(MapboxMap map) {
+ //Store for later
+ mapboxMap = map;
+
+ //Center and Zoom (Amsterdam, zoomed to streets)
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.379189, 4.899431), 14));
+ }
+ });
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.menu_runtime_style, menu);
+ return true;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ case R.id.action_water_color:
+ setWaterColor();
+ return true;
+ case R.id.action_background_opacity:
+ setBackgroundOpacity();
+ return true;
+ case R.id.action_road_avoid_edges:
+ setRoadSymbolPlacement();
+ return true;
+ case R.id.action_layer_visibility:
+ setLayerInvisible();
+ return true;
+ case R.id.action_remove_layer:
+ removeBuildings();
+ return true;
+ case R.id.action_add_parks_layer:
+ addParksLayer();
+ return true;
+ case R.id.action_add_terrain_layer:
+ addTerrainLayer();
+ return true;
+ case R.id.action_add_satellite_layer:
+ addSatelliteLayer();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void setLayerInvisible() {
+ String[] roadLayers = new String[]{"water"};
+ for (String roadLayer : roadLayers) {
+ Layer layer = mapboxMap.getLayer(roadLayer);
+ if (layer != null) {
+ layer.set(visibility(false));
+ }
+ }
+ }
+
+ private void setRoadSymbolPlacement() {
+ //Zoom so that the labels are visible first
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(14), new DefaultCallback() {
+ @Override
+ public void onFinish() {
+ String[] roadLayers = new String[]{"road-label-small", "road-label-medium", "road-label-large"};
+ for (String roadLayer : roadLayers) {
+ Layer layer = mapboxMap.getLayer(roadLayer);
+ if (layer != null) {
+ layer.set(symbolPlacement(SYMBOL_PLACEMENT_POINT));
+ }
+ }
+ }
+ });
+ }
+
+ private void setBackgroundOpacity() {
+ Layer background = mapboxMap.getLayer("background");
+ if (background != null) {
+ background.set(backgroundOpacity(0.2f));
+ }
+ }
+
+ private void setWaterColor() {
+ Layer water = mapboxMap.getLayer("water");
+ if (water != null) {
+ water.set(
+ visibility(true),
+ fillColor(Color.RED)
+ );
+ } else {
+ Toast.makeText(RuntimeStyleActivity.this, "No water layer in this style", Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void removeBuildings() {
+ //Zoom to see buildings first
+ try {
+ mapboxMap.removeLayer("building");
+ } catch (NoSuchLayerException e) {
+ Toast.makeText(RuntimeStyleActivity.this, e.getMessage(), Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ private void addParksLayer() {
+ //Add a source
+ Source source;
+ try {
+ source = new GeoJsonSource("amsterdam-spots", readRawResource(R.raw.amsterdam));
+ } catch (IOException e) {
+ Toast.makeText(RuntimeStyleActivity.this, "Couldn't add source: " + e.getMessage(), Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ mapboxMap.addSource(source);
+
+ FillLayer layer = new FillLayer("parksLayer", "amsterdam-spots");
+ layer.set(
+ fillColor(Color.RED),
+ fillOutlineColor(Color.BLUE),
+ fillOpacity(0.3f),
+ fillAntialias(true)
+ );
+
+ //Only show me parks
+ layer.setFilter(eq("type", "park"));
+
+ mapboxMap.addLayer(layer, "building");
+ //layer.setPaintProperty(fillColor(Color.RED)); //XXX But not after the object is attached
+
+ //Or get the object later and set it. It's all good.
+ mapboxMap.getLayer("parksLayer").set(fillColor(Color.RED));
+
+ //Get a good look at it all
+ mapboxMap.animateCamera(CameraUpdateFactory.zoomTo(12));
+ }
+
+ private void addTerrainLayer() {
+ //Add a source
+ Source source = new VectorSource("my-terrain-source", "mapbox://mapbox.mapbox-terrain-v2");
+ mapboxMap.addSource(source);
+
+ LineLayer layer = new LineLayer("terrainLayer", "my-terrain-source");
+ layer.setSourceLayer("contour");
+ layer.set(
+ lineJoin(Property.LINE_JOIN_ROUND),
+ lineCap(Property.LINE_CAP_ROUND),
+ lineColor(Color.RED),
+ lineWidth(20f)
+ );
+
+ mapboxMap.addLayer(layer);
+ }
+
+ private void addSatelliteLayer() {
+ //Add a source
+ Source source = new RasterSource("my-raster-source", "mapbox://mapbox.satellite");
+ mapboxMap.addSource(source);
+
+ //Add a layer
+ mapboxMap.addLayer(new RasterLayer("satellite-layer", "my-raster-source"));
+ }
+
+ private String readRawResource(@RawRes int rawResource) throws IOException {
+ InputStream is = getResources().openRawResource(rawResource);
+ Writer writer = new StringWriter();
+ char[] buffer = new char[1024];
+ try {
+ Reader reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
+ int n;
+ while ((n = reader.read(buffer)) != -1) {
+ writer.write(buffer, 0, n);
+ }
+ } finally {
+ is.close();
+ }
+
+ return writer.toString();
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+
+ private static class DefaultCallback implements MapboxMap.CancelableCallback {
+
+ @Override
+ public void onCancel() {
+ //noop
+ }
+
+ @Override
+ public void onFinish() {
+ //noop
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml
new file mode 100644
index 0000000000..4c0d067c14
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_runtime_style.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar"/>
+
+</RelativeLayout>
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
new file mode 100644
index 0000000000..69784584c0
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/menu/menu_runtime_style.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:mapbox="http://schemas.android.com/apk/res-auto">
+
+ <item
+ android:id="@+id/action_water_color"
+ android:title="Color the water"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_background_opacity"
+ android:title="Set background opacity"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_road_avoid_edges"
+ android:title="Set road symbol placement to Point"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_layer_visibility"
+ android:title="Set layer visibility to false"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_add_parks_layer"
+ android:title="Add a parks layer"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_remove_layer"
+ android:title="Remove buildings layer"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_add_terrain_layer"
+ android:title="Add a terrain layer"
+ mapbox:showAsAction="never"/>
+ <item
+ android:id="@+id/action_add_satellite_layer"
+ android:title="Add a satellite layer"
+ mapbox:showAsAction="never"/>
+</menu> \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson
new file mode 100644
index 0000000000..2629a7c3c4
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/raw/amsterdam.geojson
@@ -0,0 +1,2283 @@
+{
+ "type": "FeatureCollection",
+ "features": [
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Westerpark",
+ "type": "park",
+ "description": "The \"Westerpark\" is a public urban park in Amsterdam, Netherlands. The former borough of Westerpark is named after the park, as is the current neighborhood. The verdant space of the former Westergasfabriek (gasworks) along Haarlemmerweg has become a place for cultural avant-garde businesses and events. The park is stretched along the railway, offering a biotope area to experience nature in the city. In addition Westerpark is home to one of the Netherlands’ oldest volkstuin (Gardenpark)."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.88093376159668,
+ 52.38560608655206
+ ],
+ [
+ 4.881706237792968,
+ 52.3864966440161
+ ],
+ [
+ 4.870891571044922,
+ 52.388696767789725
+ ],
+ [
+ 4.864625930786133,
+ 52.38906344442449
+ ],
+ [
+ 4.85072135925293,
+ 52.389220590621235
+ ],
+ [
+ 4.846086502075195,
+ 52.38864438516467
+ ],
+ [
+ 4.84522819519043,
+ 52.38607756038855
+ ],
+ [
+ 4.845314025878906,
+ 52.38560608655206
+ ],
+ [
+ 4.84745979309082,
+ 52.38560608655206
+ ],
+ [
+ 4.848232269287109,
+ 52.38518699447024
+ ],
+ [
+ 4.88093376159668,
+ 52.38560608655206
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "vondelpark",
+ "type": "park",
+ "description": "Vondelpark has opened its gates since 1885 and is Amsterdam's busiest park, with 10 Million visitors per year, situated at the south-west corner of the canal ring. It is very popular in summer for both tourists and locals, and all year round as a training area for runners, with many bootcamps taking place all over the park."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.881491661071777,
+ 52.36194735288177
+ ],
+ [
+ 4.882135391235352,
+ 52.361711487760196
+ ],
+ [
+ 4.882307052612305,
+ 52.361475621379526
+ ],
+ [
+ 4.875826835632324,
+ 52.35966727063089
+ ],
+ [
+ 4.875226020812988,
+ 52.35846166234964
+ ],
+ [
+ 4.866771697998047,
+ 52.356207610808546
+ ],
+ [
+ 4.867458343505859,
+ 52.355159175569305
+ ],
+ [
+ 4.86668586730957,
+ 52.35497569684526
+ ],
+ [
+ 4.864239692687988,
+ 52.35563097450493
+ ],
+ [
+ 4.861965179443359,
+ 52.35578823969753
+ ],
+ [
+ 4.858918190002441,
+ 52.35437283281734
+ ],
+ [
+ 4.857029914855957,
+ 52.35468737159704
+ ],
+ [
+ 4.855892658233642,
+ 52.354634948622525
+ ],
+ [
+ 4.855034351348877,
+ 52.356391084418235
+ ],
+ [
+ 4.875226020812988,
+ 52.36126596131745
+ ],
+ [
+ 4.876556396484375,
+ 52.360453519180375
+ ],
+ [
+ 4.881491661071777,
+ 52.36194735288177
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Jordaan",
+ "type": "neighborhood",
+ "description": "The Jordan was originally a working-class neighbourhood, and has now become a more upscale neighborhood. It is home to many art galleries, particularly for modern art, and is also dotted with speciality shops and restaurants. Markets are held regularly at Noordermarkt, the Westerstraat and Lindengracht."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.888465404510498,
+ 52.38053742479665
+ ],
+ [
+ 4.883208274841309,
+ 52.374865596670936
+ ],
+ [
+ 4.882457256317139,
+ 52.36667749309006
+ ],
+ [
+ 4.882757663726807,
+ 52.36619270976844
+ ],
+ [
+ 4.879302978515624,
+ 52.36490866337324
+ ],
+ [
+ 4.874324798583984,
+ 52.37186565170666
+ ],
+ [
+ 4.8818135261535645,
+ 52.38427021667093
+ ],
+ [
+ 4.888465404510498,
+ 52.38053742479665
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Prinseneiland",
+ "type": "neighborhood",
+ "description": "Between 1610 and 1615 Prinseneiland was built as an extension of the harbor. Until the end of the 19th century this was an area with many wharfs, little industries and warehouses, related to the shipping trades. After the second World war the desolated area was discovered by many artists, who established their homes and studios in the vacant buildings. During the second half of the 20th century the old warehouses were transformed into apartments one after another, and new apartments were built. Nevertheless a lot of the atmosphere of the glorious past is still present in the old buildings and wooden drawbridges."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.883251190185547,
+ 52.38618233166844
+ ],
+ [
+ 4.889817237854004,
+ 52.38264616355127
+ ],
+ [
+ 4.896254539489746,
+ 52.38356297507495
+ ],
+ [
+ 4.891490936279297,
+ 52.390425359543386
+ ],
+ [
+ 4.884967803955078,
+ 52.39068726147953
+ ],
+ [
+ 4.883251190185547,
+ 52.38618233166844
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Sarphatipark",
+ "type": "park",
+ "description": "Sarphatipark is a small park in the popular De Pijp neighbourhood. It was openend in late 19th century, and named after Samuel Sarphati."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.894580841064453,
+ 52.35337022551748
+ ],
+ [
+ 4.899033308029174,
+ 52.354267986060016
+ ],
+ [
+ 4.89815354347229,
+ 52.35544094498385
+ ],
+ [
+ 4.893786907196045,
+ 52.35446457352601
+ ],
+ [
+ 4.894580841064453,
+ 52.35337022551748
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Museumsplein",
+ "type": "area",
+ "description": "Museumplein is a large open space which hosts different events throughout the year. Along the edges of the open square, some of the Dutch capitals most important art museums, such as Stedelijk Museum, Van Gogh Museum and Rijksmuseum."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.880322217941284,
+ 52.35625347928239
+ ],
+ [
+ 4.881459474563598,
+ 52.35610932106146
+ ],
+ [
+ 4.882693290710448,
+ 52.356921843071525
+ ],
+ [
+ 4.883508682250977,
+ 52.357996446011384
+ ],
+ [
+ 4.884324073791504,
+ 52.35783263627572
+ ],
+ [
+ 4.884721040725708,
+ 52.35903498560687
+ ],
+ [
+ 4.886341094970703,
+ 52.35875651523955
+ ],
+ [
+ 4.886770248413086,
+ 52.36003418836164
+ ],
+ [
+ 4.884881973266602,
+ 52.36113492327348
+ ],
+ [
+ 4.884538650512695,
+ 52.36066318309746
+ ],
+ [
+ 4.883229732513428,
+ 52.36028971855292
+ ],
+ [
+ 4.883841276168823,
+ 52.35953622784582
+ ],
+ [
+ 4.882038831710815,
+ 52.35897929167382
+ ],
+ [
+ 4.882339239120483,
+ 52.35861236518361
+ ],
+ [
+ 4.880794286727905,
+ 52.35790471263422
+ ],
+ [
+ 4.880847930908203,
+ 52.357780217032044
+ ],
+ [
+ 4.879699945449829,
+ 52.35691529053445
+ ],
+ [
+ 4.880322217941284,
+ 52.35625347928239
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Marineterrein",
+ "type": "area",
+ "description": "The Marineterrein exists already for more than 350 years. The area lies on the island known as ‘Kattenburg’, in close vicinity to Centraal Station and Amsterdam’s Maritime Museum. During the Golden Age the VOC used this area to build large warships. After years of use through the Dutch marines, the area has now been opened for the public and workspaces are filled by tech startups."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.914064407348633,
+ 52.37083068892153
+ ],
+ [
+ 4.917154312133789,
+ 52.37187875234601
+ ],
+ [
+ 4.921102523803711,
+ 52.37444640263532
+ ],
+ [
+ 4.921188354492187,
+ 52.37481319763409
+ ],
+ [
+ 4.914150238037109,
+ 52.37586116655898
+ ],
+ [
+ 4.913034439086914,
+ 52.374917995645625
+ ],
+ [
+ 4.914493560791016,
+ 52.37339840013861
+ ],
+ [
+ 4.914064407348633,
+ 52.37083068892153
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "erasmuspark"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.849905967712402,
+ 52.37300539279099
+ ],
+ [
+ 4.852695465087891,
+ 52.37350320150736
+ ],
+ [
+ 4.855098724365234,
+ 52.37428920384616
+ ],
+ [
+ 4.854240417480469,
+ 52.3765423330248
+ ],
+ [
+ 4.848318099975586,
+ 52.37552057938607
+ ],
+ [
+ 4.849905967712402,
+ 52.37300539279099
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Sloterdijk",
+ "type": "area",
+ "description": "To protect the area around Sloten from the as-yet undrained IJ the Spaarndammerdijk was laid along the south bank of this inlet. In this vicinity at the same time, a dam on the Slochter (or Slooter) river was built, the Slooterdam. Trade grew in the vicinity, and in the 15th century a weigh house and a church were built. The area is nowadays best known as a large intersection of train lines and a business and industrial centre north-west of Amsterdam."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.8665571212768555,
+ 52.39312287505632
+ ],
+ [
+ 4.849648475646973,
+ 52.400638383557414
+ ],
+ [
+ 4.845571517944336,
+ 52.39681532315127
+ ],
+ [
+ 4.8445844650268555,
+ 52.39411803332277
+ ],
+ [
+ 4.844756126403809,
+ 52.38911582655221
+ ],
+ [
+ 4.85072135925293,
+ 52.38935154535783
+ ],
+ [
+ 4.871063232421875,
+ 52.389299163509826
+ ],
+ [
+ 4.8665571212768555,
+ 52.39312287505632
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Nine Streets (Negen Straatjes)",
+ "type": "poi",
+ "description": "De Negen Straatjes (Dutch for \"the nine little streets\") are nine side streets of the Prinsengracht, Keizersgracht, Herengracht and Singel in central Amsterdam which have been promoting themselves with that name since the 1990s. Together they form a sub-neighborhood within the larger western Grachtengordel (\"Canal Belt\"), one with many small and diverse shops and restaurants. The construction in this area goes back to the first half of the 17th century."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.887650012969971,
+ 52.368629674781644
+ ],
+ [
+ 4.8884546756744385,
+ 52.37242897568859
+ ],
+ [
+ 4.883047342300415,
+ 52.372664783594274
+ ],
+ [
+ 4.882628917694092,
+ 52.36839384533322
+ ],
+ [
+ 4.887650012969971,
+ 52.368629674781644
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Artis",
+ "type": "poi",
+ "description": "Artis, short for Natura Artis Magistra (Latin for \"Nature is the teacher of art and science\"), is a zoo in the centre of Amsterdam. It is the oldest zoo in the Netherlands and one of the oldest zoos of mainland Europe. Artis Royal Zoo is not just a zoo, it also contains an aquarium and a planetarium. Artis also has an arboretum and a fairly large art collection. A part of the art collection is on display in the Aquarium building of the zoo. Artis contains 27 monumental buildings, most of which are used as enclosures for the animals, making Artis a unique cultural heritage of the 19th century."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.911768436431885,
+ 52.36642855096776
+ ],
+ [
+ 4.913313388824463,
+ 52.36802699702209
+ ],
+ [
+ 4.916939735412598,
+ 52.36679541255308
+ ],
+ [
+ 4.9175190925598145,
+ 52.3673456992188
+ ],
+ [
+ 4.921274185180664,
+ 52.366048583971256
+ ],
+ [
+ 4.921660423278808,
+ 52.36551138367574
+ ],
+ [
+ 4.918656349182129,
+ 52.36400456750192
+ ],
+ [
+ 4.911768436431885,
+ 52.36642855096776
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Oosterpark",
+ "type": "park",
+ "description": "Oosterpark was the first large park opened by the municipality of Amsterdam in 1891. The park was designed as an English garden by Dutch landscape architect Leonard Anthony Springer."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.915802478790282,
+ 52.36062387118878
+ ],
+ [
+ 4.9173688888549805,
+ 52.35804231262857
+ ],
+ [
+ 4.925351142883301,
+ 52.36002108420944
+ ],
+ [
+ 4.923892021179199,
+ 52.36192114570822
+ ],
+ [
+ 4.92213249206543,
+ 52.36150182881734
+ ],
+ [
+ 4.92161750793457,
+ 52.36223563076494
+ ],
+ [
+ 4.919493198394775,
+ 52.36159355472725
+ ],
+ [
+ 4.91987943649292,
+ 52.36101698870163
+ ],
+ [
+ 4.918956756591797,
+ 52.360741806809884
+ ],
+ [
+ 4.918656349182129,
+ 52.36097767710775
+ ],
+ [
+ 4.917240142822266,
+ 52.36063697516221
+ ],
+ [
+ 4.916982650756836,
+ 52.360899053815025
+ ],
+ [
+ 4.915802478790282,
+ 52.36062387118878
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Park Frankendael",
+ "type": "park",
+ "description": "As Amsterdam rapidly grew in the sixteen and the beginning of eighteen century, the real estate in the city became so expensive, that rich people who wanted to enjoy a bigger property had to move further from the capital. The Park Frankendael (7 acres) in East Amsterdam, was originally one of these wealthy estates. The entrance to the park with an old ornamented gate is at the Middenweg, less than one mile (1300m) from the Tropenmuseum, driving out of the city. The beautiful old land house Frankendael (built in 1659) is visible from the street – it is one of the few of these estates remaining in Amsterdam"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.925265312194824,
+ 52.35007391180776
+ ],
+ [
+ 4.9283552169799805,
+ 52.352079253210675
+ ],
+ [
+ 4.930479526519775,
+ 52.3535995821349
+ ],
+ [
+ 4.93483543395996,
+ 52.35080790353051
+ ],
+ [
+ 4.929170608520508,
+ 52.34772767795072
+ ],
+ [
+ 4.925265312194824,
+ 52.35007391180776
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Stedelijk Museum",
+ "type": "poi",
+ "description": "Colloquially known as the Stedelijk, it is a museum for modern art, contemporary art, and design. The 19th century building was designed by Adriaan Willem Weissman and the 21st century wing with the current entrance was designed by Benthem Crouwel Architects. The collection comprises art from the early 20th century up to the 21st century. "
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.879399538040161,
+ 52.357400175655954
+ ],
+ [
+ 4.88067626953125,
+ 52.3577933218488
+ ],
+ [
+ 4.880236387252808,
+ 52.35841579616774
+ ],
+ [
+ 4.8789381980896,
+ 52.35808817919812
+ ],
+ [
+ 4.879399538040161,
+ 52.357400175655954
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Rembrandtpark",
+ "type": "park",
+ "description": "Rembrandtpark is a hidden gem west of the 'famous' Vondelpark. It is often not known to tourists and expats, but loved by locals for the outdoor gym and kids' playgrounds. It's great to combine both, Vondelpark and Rembrandtpark on a run or walk."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.845314025878906,
+ 52.369179938598464
+ ],
+ [
+ 4.84522819519043,
+ 52.36763394187947
+ ],
+ [
+ 4.843039512634277,
+ 52.36755533043126
+ ],
+ [
+ 4.8429107666015625,
+ 52.36490866337324
+ ],
+ [
+ 4.8442840576171875,
+ 52.36456799173892
+ ],
+ [
+ 4.84419822692871,
+ 52.36323148534417
+ ],
+ [
+ 4.843082427978516,
+ 52.36302183361385
+ ],
+ [
+ 4.843254089355469,
+ 52.36019143788499
+ ],
+ [
+ 4.844799041748047,
+ 52.36027006243683
+ ],
+ [
+ 4.8451852798461905,
+ 52.35924793235075
+ ],
+ [
+ 4.843854904174805,
+ 52.35888100809126
+ ],
+ [
+ 4.844112396240234,
+ 52.35822577862119
+ ],
+ [
+ 4.848747253417969,
+ 52.35825198798652
+ ],
+ [
+ 4.8487043380737305,
+ 52.36079422254044
+ ],
+ [
+ 4.849519729614258,
+ 52.361292168879636
+ ],
+ [
+ 4.849262237548828,
+ 52.363572167284175
+ ],
+ [
+ 4.850249290466309,
+ 52.3637031981001
+ ],
+ [
+ 4.850249290466309,
+ 52.36540656334422
+ ],
+ [
+ 4.8484039306640625,
+ 52.369546777334904
+ ],
+ [
+ 4.845314025878906,
+ 52.369179938598464
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Sloterpark",
+ "type": "park",
+ "description": "Amsterdam's largest park is Sloter Park with 91 hectares. It was created in the 1950s using excavated soil of the former Sloterdijkermeer Polder."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.823555946350098,
+ 52.3639652585661
+ ],
+ [
+ 4.826860427856445,
+ 52.36414869996741
+ ],
+ [
+ 4.826817512512207,
+ 52.36585204803552
+ ],
+ [
+ 4.830508232116699,
+ 52.370280445668364
+ ],
+ [
+ 4.826602935791016,
+ 52.373031593389626
+ ],
+ [
+ 4.822740554809569,
+ 52.37360800262741
+ ],
+ [
+ 4.821324348449707,
+ 52.37235037277361
+ ],
+ [
+ 4.819135665893555,
+ 52.371695343041914
+ ],
+ [
+ 4.81201171875,
+ 52.37187875234601
+ ],
+ [
+ 4.807548522949219,
+ 52.372402774732464
+ ],
+ [
+ 4.805660247802734,
+ 52.37046386084771
+ ],
+ [
+ 4.80926513671875,
+ 52.36349354860812
+ ],
+ [
+ 4.8105525970458975,
+ 52.36328389812132
+ ],
+ [
+ 4.8113250732421875,
+ 52.36150182881734
+ ],
+ [
+ 4.807033538818359,
+ 52.360899053815025
+ ],
+ [
+ 4.809608459472655,
+ 52.35654834690599
+ ],
+ [
+ 4.815659523010254,
+ 52.3577540073869
+ ],
+ [
+ 4.818663597106934,
+ 52.357963684112846
+ ],
+ [
+ 4.818620681762695,
+ 52.359483810623004
+ ],
+ [
+ 4.823555946350098,
+ 52.3639652585661
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdamse Bos",
+ "type": "park",
+ "description": "Every year almost 4.5 million people visit the Amsterdamse Bos, which has a size of 1,000 hectares and is approximately three times the size of New York's Central Park. The park was designed as a landscape park and is great for running, walking and cycling."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.849991798400879,
+ 52.33090717211467
+ ],
+ [
+ 4.849863052368164,
+ 52.331746382485676
+ ],
+ [
+ 4.853982925415039,
+ 52.33187750766789
+ ],
+ [
+ 4.853467941284179,
+ 52.332323330379836
+ ],
+ [
+ 4.848232269287109,
+ 52.33258557693488
+ ],
+ [
+ 4.848318099975586,
+ 52.33321496232317
+ ],
+ [
+ 4.8319244384765625,
+ 52.33145790571652
+ ],
+ [
+ 4.8181915283203125,
+ 52.330828495326095
+ ],
+ [
+ 4.820079803466797,
+ 52.32327487205222
+ ],
+ [
+ 4.816474914550781,
+ 52.31299147898779
+ ],
+ [
+ 4.813899993896484,
+ 52.30879348896445
+ ],
+ [
+ 4.809093475341796,
+ 52.30585465906291
+ ],
+ [
+ 4.824028015136719,
+ 52.29000260620264
+ ],
+ [
+ 4.832954406738281,
+ 52.29441235610253
+ ],
+ [
+ 4.8427391052246085,
+ 52.29220753602784
+ ],
+ [
+ 4.844627380371094,
+ 52.30060626328963
+ ],
+ [
+ 4.842395782470703,
+ 52.30396530825102
+ ],
+ [
+ 4.842395782470703,
+ 52.30407027430016
+ ],
+ [
+ 4.839649200439453,
+ 52.30504119845803
+ ],
+ [
+ 4.841108322143555,
+ 52.30732409839935
+ ],
+ [
+ 4.840335845947266,
+ 52.31708413595253
+ ],
+ [
+ 4.848575592041015,
+ 52.317189070898415
+ ],
+ [
+ 4.8558712005615225,
+ 52.31645452105213
+ ],
+ [
+ 4.8566436767578125,
+ 52.323484712336324
+ ],
+ [
+ 4.856557846069336,
+ 52.32757639896581
+ ],
+ [
+ 4.855785369873047,
+ 52.33085472093785
+ ],
+ [
+ 4.849991798400879,
+ 52.33090717211467
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amstelpark",
+ "type": "park",
+ "description": "The Amstelpark is a park in Amsterdam-Zuid. The park includes a labyrinth, a café, a restaurant, two galleries, an orangery, petting zoo and a mini-golf course. The Amstelpark was built and opened for the 1972 Floriade gardening exhibition. The park offers the Amstel train which runs through the Rosarium, the rhododendron valley and the Riekermolen. The park lost about 30 percent of its larger trees due to disease. The rhododendron valley contains about 139 species of rhododendrons, blooming between April and May. At the Great Pond in the park is the Japanese Garden. At the time of the celebration of the 400-year relationship between the Netherlands and Japan in 2001, the gardens were renovated."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.892907142639159,
+ 52.32445521070514
+ ],
+ [
+ 4.891705513000488,
+ 52.325110940795255
+ ],
+ [
+ 4.890632629394531,
+ 52.32513716979672
+ ],
+ [
+ 4.89041805267334,
+ 52.3336869954871
+ ],
+ [
+ 4.892778396606445,
+ 52.33373944330546
+ ],
+ [
+ 4.894108772277832,
+ 52.333949233956965
+ ],
+ [
+ 4.897370338439941,
+ 52.33255935234935
+ ],
+ [
+ 4.897799491882323,
+ 52.33085472093785
+ ],
+ [
+ 4.897327423095702,
+ 52.32907134391899
+ ],
+ [
+ 4.895954132080078,
+ 52.32734035040776
+ ],
+ [
+ 4.894537925720215,
+ 52.32458635750065
+ ],
+ [
+ 4.892907142639159,
+ 52.32445521070514
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Martin Luther Kingpark",
+ "type": "park",
+ "description": "Martin Luther Kingpark is part of the Rivierenbuurt and has been renamed from Amstelpark, after the new Amstelpark in Buitenveldert was created. The park hosts the famous theater festival Parade every year in summer since the 1990s. "
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.906554222106933,
+ 52.33813172737753
+ ],
+ [
+ 4.906167984008789,
+ 52.33685997655858
+ ],
+ [
+ 4.90389347076416,
+ 52.33743685775091
+ ],
+ [
+ 4.901747703552246,
+ 52.33821039117558
+ ],
+ [
+ 4.901790618896484,
+ 52.3400982803501
+ ],
+ [
+ 4.90689754486084,
+ 52.34004584007248
+ ],
+ [
+ 4.905717372894287,
+ 52.33836771835187
+ ],
+ [
+ 4.906554222106933,
+ 52.33813172737753
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Beatrixpark",
+ "type": "park",
+ "description": "Beatrixpark, named after Queen Beatrix is located in the borough of Amsterdam-Zuid."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.87818717956543,
+ 52.34435884510934
+ ],
+ [
+ 4.88093376159668,
+ 52.34582700615622
+ ],
+ [
+ 4.882993698120117,
+ 52.34456858538671
+ ],
+ [
+ 4.8854827880859375,
+ 52.34435884510934
+ ],
+ [
+ 4.884710311889648,
+ 52.33984918847747
+ ],
+ [
+ 4.882392883300781,
+ 52.33995406943698
+ ],
+ [
+ 4.879045486450195,
+ 52.33984918847747
+ ],
+ [
+ 4.878787994384765,
+ 52.340688229188224
+ ],
+ [
+ 4.881620407104492,
+ 52.34074066870404
+ ],
+ [
+ 4.881706237792968,
+ 52.341789445960536
+ ],
+ [
+ 4.878444671630859,
+ 52.341789445960536
+ ],
+ [
+ 4.87818717956543,
+ 52.34435884510934
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Park de Schinkeleilanden",
+ "type": "Park",
+ "description": "This park was built between 2005 and 2010 and is popular for the neighborhoods surrounding the Schinkel waterway. It's allowed to BBQ here, and it's a popular spot for running and hiking."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.851171970367432,
+ 52.34405734171741
+ ],
+ [
+ 4.851021766662597,
+ 52.34514536601911
+ ],
+ [
+ 4.851686954498291,
+ 52.34628579648493
+ ],
+ [
+ 4.851021766662597,
+ 52.3465610683968
+ ],
+ [
+ 4.848833084106445,
+ 52.342785761313266
+ ],
+ [
+ 4.849659204483032,
+ 52.340747223639156
+ ],
+ [
+ 4.852405786514282,
+ 52.34110774357341
+ ],
+ [
+ 4.852041006088257,
+ 52.34196642466951
+ ],
+ [
+ 4.852041006088257,
+ 52.341979534175316
+ ],
+ [
+ 4.851665496826172,
+ 52.34280870252078
+ ],
+ [
+ 4.851171970367432,
+ 52.34405734171741
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Schiphol",
+ "type": "area",
+ "description": "Schiphol is the main international airport of the Netherlands. Schiphol Airport is an important European airport, ranking as Europe's fifth busiest and the world's fourteenth busiest by total passenger traffic in 2015. It also ranks as the world's fifth busiest by international passenger traffic. The entire airport is below sea level; the lowest point sits at 3.4 m (11 ft) below sea level."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.729099273681641,
+ 52.28811257899827
+ ],
+ [
+ 4.734764099121094,
+ 52.28559241729168
+ ],
+ [
+ 4.762744903564453,
+ 52.292522517043615
+ ],
+ [
+ 4.776134490966797,
+ 52.284857343123655
+ ],
+ [
+ 4.7907257080078125,
+ 52.293992398835414
+ ],
+ [
+ 4.793128967285156,
+ 52.30081621106509
+ ],
+ [
+ 4.803943634033203,
+ 52.30564473517634
+ ],
+ [
+ 4.810981750488281,
+ 52.311942018805624
+ ],
+ [
+ 4.796905517578125,
+ 52.319707434957024
+ ],
+ [
+ 4.788494110107422,
+ 52.320966563244205
+ ],
+ [
+ 4.779567718505859,
+ 52.32442898129939
+ ],
+ [
+ 4.77081298828125,
+ 52.3191827875965
+ ],
+ [
+ 4.756736755371094,
+ 52.31886799619451
+ ],
+ [
+ 4.750041961669922,
+ 52.310367781878
+ ],
+ [
+ 4.7454071044921875,
+ 52.30281066528705
+ ],
+ [
+ 4.7426605224609375,
+ 52.29798183210937
+ ],
+ [
+ 4.729099273681641,
+ 52.28811257899827
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Noorderpark",
+ "type": "park",
+ "description": "The Noorderpark is a park just 5 minutes north of the ferry that crosses the IJ canal behind Amsterdam Central Station. It came to exist in 2014 after combining Florapark and Volewijkspark."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.921703338623047,
+ 52.39046464493288
+ ],
+ [
+ 4.921939373016357,
+ 52.389652739777624
+ ],
+ [
+ 4.92436408996582,
+ 52.39068726147955
+ ],
+ [
+ 4.924213886260986,
+ 52.390922971893374
+ ],
+ [
+ 4.92460012435913,
+ 52.39105392157937
+ ],
+ [
+ 4.923999309539795,
+ 52.39326691251008
+ ],
+ [
+ 4.924793243408203,
+ 52.39486438729677
+ ],
+ [
+ 4.922282695770264,
+ 52.39698553494043
+ ],
+ [
+ 4.919922351837158,
+ 52.39847813327716
+ ],
+ [
+ 4.917948246002197,
+ 52.397705654476155
+ ],
+ [
+ 4.918398857116699,
+ 52.39618684316538
+ ],
+ [
+ 4.917948246002197,
+ 52.39527029380819
+ ],
+ [
+ 4.917197227478027,
+ 52.39200984251752
+ ],
+ [
+ 4.9161458015441895,
+ 52.39068726147955
+ ],
+ [
+ 4.918420314788818,
+ 52.389901551009025
+ ],
+ [
+ 4.920244216918945,
+ 52.39115868104846
+ ],
+ [
+ 4.921402931213379,
+ 52.39157771643836
+ ],
+ [
+ 4.922153949737548,
+ 52.39121106068977
+ ],
+ [
+ 4.921703338623047,
+ 52.39046464493288
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Vliegenbos",
+ "type": "park",
+ "description": "The Vliegenbos is the oldest city forest in Amsterdam. Here you've got plenty of space to run or cycle through a dense forest. It also hosts a camping ground, and is an amazingly well located starting point for excursions to the fishing villages along the IJsselmeer. "
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.925222396850586,
+ 52.389403927143704
+ ],
+ [
+ 4.925136566162109,
+ 52.39003250372539
+ ],
+ [
+ 4.926724433898926,
+ 52.39105392157937
+ ],
+ [
+ 4.933032989501953,
+ 52.391525337232174
+ ],
+ [
+ 4.93311882019043,
+ 52.390739641680284
+ ],
+ [
+ 4.938998222351074,
+ 52.38969202585476
+ ],
+ [
+ 4.9376678466796875,
+ 52.38686333892666
+ ],
+ [
+ 4.927024841308593,
+ 52.38906344442449
+ ],
+ [
+ 4.926466941833496,
+ 52.38948249970591
+ ],
+ [
+ 4.9253082275390625,
+ 52.38945630886739
+ ],
+ [
+ 4.925222396850586,
+ 52.389403927143704
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Flevopark",
+ "type": "park",
+ "description": "Flevopark is the jewel in the East of Amsterdam. Its offerings include an outdoor swimming pool, several lakes, restaurants. It's awesome for a bbq in summer or a foggy walk or run on a winter morning."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.948225021362305,
+ 52.36493486877479
+ ],
+ [
+ 4.9527740478515625,
+ 52.36517071668903
+ ],
+ [
+ 4.954404830932617,
+ 52.36517071668903
+ ],
+ [
+ 4.955005645751953,
+ 52.36459419734253
+ ],
+ [
+ 4.952559471130371,
+ 52.36215700971062
+ ],
+ [
+ 4.952859878540038,
+ 52.35953622784585
+ ],
+ [
+ 4.952731132507324,
+ 52.35809473156134
+ ],
+ [
+ 4.944963455200195,
+ 52.35859270832139
+ ],
+ [
+ 4.946165084838867,
+ 52.362314251679365
+ ],
+ [
+ 4.947195053100586,
+ 52.362602527168704
+ ],
+ [
+ 4.947667121887207,
+ 52.36417490581972
+ ],
+ [
+ 4.948225021362305,
+ 52.36493486877479
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdam Centraal",
+ "type": "station",
+ "description": "Amsterdam's Central Station."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.896576404571533,
+ 52.37985631995486
+ ],
+ [
+ 4.89715576171875,
+ 52.38066840529253
+ ],
+ [
+ 4.90389347076416,
+ 52.37820590695898
+ ],
+ [
+ 4.902949333190918,
+ 52.37738067732881
+ ],
+ [
+ 4.896576404571533,
+ 52.37985631995486
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdam Amstel",
+ "type": "station",
+ "description": "Amsterdam Amstel is a trainstation in the South East, close to the river Amstel."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.916896820068359,
+ 52.3468101224592
+ ],
+ [
+ 4.917690753936768,
+ 52.34704606711881
+ ],
+ [
+ 4.918248653411864,
+ 52.34604984776767
+ ],
+ [
+ 4.9173903465271,
+ 52.34584011451739
+ ],
+ [
+ 4.916896820068359,
+ 52.3468101224592
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Ouderkerk",
+ "type": "poi",
+ "description": "Ouderkerk aan de Amstel is a picturesque village in the Dutch province of North Holland. It lies about 9 km south of Amsterdam. The town is a popular destination for Amsterdammers on the weekends. The town is the location of the Beth Haim of Ouderkerk aan de Amstel, the oldest Jewish cemetery in the Netherlands."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.922518730163574,
+ 52.29236502681571
+ ],
+ [
+ 4.922819137573242,
+ 52.28777131549237
+ ],
+ [
+ 4.90788459777832,
+ 52.29034385252062
+ ],
+ [
+ 4.892692565917969,
+ 52.29456983905114
+ ],
+ [
+ 4.898228645324707,
+ 52.29824428222637
+ ],
+ [
+ 4.902563095092773,
+ 52.299267822821434
+ ],
+ [
+ 4.908742904663086,
+ 52.29879542240944
+ ],
+ [
+ 4.916167259216309,
+ 52.30149853446092
+ ],
+ [
+ 4.920544624328613,
+ 52.29666955819423
+ ],
+ [
+ 4.922518730163574,
+ 52.29236502681571
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "NDSM terrein",
+ "type": "poi",
+ "description": "The former NDSM Amsterdam ship wharf is a stunning hangout. Just 10 minutes by free ferry and a large area is there to explore. Restaurants, bars, terraces, skatepark, new and old architecture, all with the amazing view on the IJ-waters. Many of the old buildings that were in use for the making of large ships are still there, housing creative enterprises."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.890182018280029,
+ 52.401711923144106
+ ],
+ [
+ 4.894495010375977,
+ 52.40307344797437
+ ],
+ [
+ 4.899129867553711,
+ 52.400939500955296
+ ],
+ [
+ 4.896554946899414,
+ 52.39881854337136
+ ],
+ [
+ 4.8909544944763175,
+ 52.39813772055684
+ ],
+ [
+ 4.891490936279297,
+ 52.400847856747404
+ ],
+ [
+ 4.890182018280029,
+ 52.401711923144106
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amsterdam Zuid",
+ "type": "station",
+ "description": "Station Amsterdam Zuid"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.8720502853393555,
+ 52.33950832364112
+ ],
+ [
+ 4.874711036682129,
+ 52.33969186657182
+ ],
+ [
+ 4.8749041557312,
+ 52.338459492279576
+ ],
+ [
+ 4.872071743011475,
+ 52.33835460777523
+ ],
+ [
+ 4.8720502853393555,
+ 52.33950832364112
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Het Twiske",
+ "type": "park",
+ "description": "Het Twiske recreational area lies to the north of Amsterdam between Zaanstad and Purmerend. It’s the ideal place for a day out on your bicycle, as Amsterdam’s bike paths connect directly to the paths in Het Twiske."
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.888744354248047,
+ 52.43555429631541
+ ],
+ [
+ 4.900503158569336,
+ 52.43445541622349
+ ],
+ [
+ 4.910116195678711,
+ 52.43586825702301
+ ],
+ [
+ 4.910888671875,
+ 52.442199320554714
+ ],
+ [
+ 4.907197952270508,
+ 52.44852947442261
+ ],
+ [
+ 4.90565299987793,
+ 52.449941863962756
+ ],
+ [
+ 4.905910491943359,
+ 52.45135420821245
+ ],
+ [
+ 4.903764724731445,
+ 52.4549633266463
+ ],
+ [
+ 4.903507232666016,
+ 52.45846754991504
+ ],
+ [
+ 4.904794692993164,
+ 52.461866903001194
+ ],
+ [
+ 4.906854629516602,
+ 52.46489995032684
+ ],
+ [
+ 4.906940460205078,
+ 52.465736616263186
+ ],
+ [
+ 4.9031639099121085,
+ 52.4673576112622
+ ],
+ [
+ 4.896640777587891,
+ 52.468560246396606
+ ],
+ [
+ 4.895782470703125,
+ 52.46793278868704
+ ],
+ [
+ 4.892778396606445,
+ 52.465422868400594
+ ],
+ [
+ 4.890289306640625,
+ 52.46416785458775
+ ],
+ [
+ 4.882049560546875,
+ 52.46191919869101
+ ],
+ [
+ 4.876041412353516,
+ 52.45982732264483
+ ],
+ [
+ 4.87492561340332,
+ 52.45825835038316
+ ],
+ [
+ 4.873895645141601,
+ 52.45648011423114
+ ],
+ [
+ 4.873037338256836,
+ 52.454911022694276
+ ],
+ [
+ 4.873552322387695,
+ 52.452714200611055
+ ],
+ [
+ 4.874839782714844,
+ 52.44805866784458
+ ],
+ [
+ 4.879388809204102,
+ 52.44277482667677
+ ],
+ [
+ 4.883165359497069,
+ 52.43926935464697
+ ],
+ [
+ 4.887542724609374,
+ 52.43597291009513
+ ],
+ [
+ 4.888744354248047,
+ 52.43555429631541
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Diemerpark",
+ "type": "park"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.974660873413086,
+ 52.35694150067703
+ ],
+ [
+ 4.980497360229492,
+ 52.357989893633615
+ ],
+ [
+ 4.985218048095703,
+ 52.35827819733633
+ ],
+ [
+ 4.986248016357422,
+ 52.35720360124792
+ ],
+ [
+ 4.985218048095703,
+ 52.355971715048284
+ ],
+ [
+ 4.985218048095703,
+ 52.3552640202125
+ ],
+ [
+ 4.986376762390137,
+ 52.3545563140442
+ ],
+ [
+ 4.987921714782714,
+ 52.353901020450564
+ ],
+ [
+ 4.988865852355957,
+ 52.353901020450564
+ ],
+ [
+ 4.989681243896484,
+ 52.353219504806525
+ ],
+ [
+ 4.991183280944824,
+ 52.352642829515084
+ ],
+ [
+ 4.992728233337402,
+ 52.35169916280845
+ ],
+ [
+ 4.994831085205078,
+ 52.34996905485244
+ ],
+ [
+ 4.995818138122559,
+ 52.34991662628147
+ ],
+ [
+ 4.997320175170898,
+ 52.348763181988105
+ ],
+ [
+ 4.997320175170898,
+ 52.348081587122245
+ ],
+ [
+ 4.99852180480957,
+ 52.34658728467996
+ ],
+ [
+ 4.999551773071289,
+ 52.34535510256526
+ ],
+ [
+ 4.999337196350098,
+ 52.345197800248926
+ ],
+ [
+ 4.976506233215332,
+ 52.35573581802885
+ ],
+ [
+ 4.974660873413086,
+ 52.35694150067703
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Amstelfeld",
+ "type": "poi"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.897080659866332,
+ 52.36253045847271
+ ],
+ [
+ 4.8978424072265625,
+ 52.36269425079362
+ ],
+ [
+ 4.89815354347229,
+ 52.362137354425165
+ ],
+ [
+ 4.896329641342163,
+ 52.36185562770631
+ ],
+ [
+ 4.896275997161865,
+ 52.36240597590272
+ ],
+ [
+ 4.897080659866332,
+ 52.36253045847271
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Albert Cuyp Markt",
+ "type": "poi"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.890514612197876,
+ 52.35491672137934
+ ],
+ [
+ 4.894967079162598,
+ 52.35580134510498
+ ],
+ [
+ 4.896436929702759,
+ 52.356168294935955
+ ],
+ [
+ 4.899580478668213,
+ 52.3570725511568
+ ],
+ [
+ 4.899430274963379,
+ 52.35731499351983
+ ],
+ [
+ 4.897252321243286,
+ 52.356587662440496
+ ],
+ [
+ 4.895503520965576,
+ 52.356076557763856
+ ],
+ [
+ 4.893561601638794,
+ 52.355683396297984
+ ],
+ [
+ 4.8904502391815186,
+ 52.35510020034824
+ ],
+ [
+ 4.890514612197876,
+ 52.35491672137934
+ ]
+ ]
+ ]
+ }
+ },
+ {
+ "type": "Feature",
+ "properties": {
+ "stroke": "#555555",
+ "stroke-width": 2,
+ "stroke-opacity": 1,
+ "fill": "#555555",
+ "fill-opacity": 0.5,
+ "name": "Noordermarkt",
+ "type": "poi"
+ },
+ "geometry": {
+ "type": "Polygon",
+ "coordinates": [
+ [
+ [
+ 4.885987043380737,
+ 52.37978100483205
+ ],
+ [
+ 4.886265993118286,
+ 52.37907041669766
+ ],
+ [
+ 4.886485934257507,
+ 52.37900819875896
+ ],
+ [
+ 4.886732697486877,
+ 52.37907041669766
+ ],
+ [
+ 4.887698292732239,
+ 52.38007244089837
+ ],
+ [
+ 4.885987043380737,
+ 52.37978100483205
+ ]
+ ]
+ ]
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 9ca4f45811..c14e97fb2d 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -54,6 +54,7 @@
<string name="activity_minmax_zoom">Min/Max Zoom</string>
<string name="activity_viewpager">ViewPager</string>
<string name="title_activity_navigation_drawer">Navigation Drawer</string>
+ <string name="activity_runtime_style">Runtime Style</string>
<!-- Description -->
<string name="description_user_location_tracking">Tracks the location of the user</string>
@@ -92,6 +93,7 @@
<string name="description_location_picker">Use a fixed Marker to select your location</string>
<string name="description_viewpager">Use SupportMapFragments in a ViewPager</string>
<string name="description_navigation_drawer">Test for Navigation Drawer support</string>
+ <string name="description_runtime_style">Adopt the map style on the fly</string>
<string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>
<string name="menuitem_title_deselect_markers_on_tap">Deselect Markers On Tap</string>
@@ -112,6 +114,7 @@
<string name="category_offline">Offline</string>
<string name="category_userlocation">User Location</string>
<string name="category_navigation">Navigation</string>
+ <string name="category_style">Styling</string>
<string name="action_visible_bounds_explanation">Center map around 2 markers</string>
<string name="action_remove_polylines">Remove polylines</string>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
new file mode 100644
index 0000000000..3e312d3b0a
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/test/java/com/mapbox/mapboxsdk/style/layers/FilterTest.java
@@ -0,0 +1,90 @@
+package com.mapbox.mapboxsdk.style.layers;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import static com.mapbox.mapboxsdk.style.layers.Filter.*;
+
+/**
+ * Tests for Filter
+ */
+public class FilterTest {
+
+ @Test
+ public void testAll() {
+ assertArrayEquals(all().toArray(), new Object[]{"all"});
+ assertArrayEquals(
+ all(eq("key", 2), neq("key", 3)).toArray(),
+ new Object[]{"all", new Object[]{"==", "key", 2}, new Object[]{"!=", "key", 3}}
+ );
+ }
+
+ @Test
+ public void testAny() {
+ assertArrayEquals(any().toArray(), new Object[]{"any"});
+ assertArrayEquals(
+ any(eq("key", 2), neq("key", 3)).toArray(),
+ new Object[]{"any", new Object[]{"==", "key", 2}, new Object[]{"!=", "key", 3}}
+ );
+ }
+
+ @Test
+ public void testNone() {
+ assertArrayEquals(none().toArray(), new Object[]{"none"});
+ assertArrayEquals(
+ none(eq("key", 2), neq("key", 3)).toArray(),
+ new Object[]{"none", new Object[]{"==", "key", 2}, new Object[]{"!=", "key", 3}}
+ );
+ }
+
+ @Test
+ public void testHas() {
+ assertArrayEquals(has("key").toArray(), new Object[]{"has", "key"});
+ }
+
+ @Test
+ public void testHasNot() {
+ assertArrayEquals(notHas("key").toArray(), new Object[]{"!has", "key"});
+ }
+
+ @Test
+ public void testEq() {
+ assertArrayEquals(eq("key", 1).toArray(), new Object[]{"==", "key", 1});
+
+ }
+
+ @Test
+ public void testNeq() {
+ assertArrayEquals(neq("key", 1).toArray(), new Object[]{"!=", "key", 1});
+ }
+
+ @Test
+ public void testGt() {
+ assertArrayEquals(gt("key", 1).toArray(), new Object[]{">", "key", 1});
+ }
+
+ @Test
+ public void testGte() {
+ assertArrayEquals(gte("key", 1).toArray(), new Object[]{">=", "key", 1});
+ }
+
+ @Test
+ public void testLt() {
+ assertArrayEquals(lt("key", 1).toArray(), new Object[]{"<", "key", 1});
+ }
+
+ @Test
+ public void testLte() {
+ assertArrayEquals(lte("key", 1).toArray(), new Object[]{"<=", "key", 1});
+ }
+
+ @Test
+ public void testIn() {
+ assertArrayEquals(in("key", 1, 2, "Aap").toArray(), new Object[]{"in", "key", 1, 2, "Aap"});
+ }
+
+ @Test
+ public void testNotIn() {
+ assertArrayEquals(notIn("key", 1, 2, "Noot").toArray(), new Object[]{"!in", "key", 1, 2, "Noot"});
+ }
+}
diff --git a/platform/android/platform.gyp b/platform/android/platform.gyp
index 171fc0b51a..cd49dc25d8 100644
--- a/platform/android/platform.gyp
+++ b/platform/android/platform.gyp
@@ -27,11 +27,22 @@
'sources': [
'src/native_map_view.cpp',
'src/jni.cpp',
+ 'src/java_types.cpp',
'src/attach_env.cpp',
'src/log_android.cpp',
'src/http_file_source.cpp',
'src/asset_file_source.cpp',
'src/thread.cpp',
+ 'src/style/value.cpp',
+ 'src/style/sources/sources.cpp',
+ 'src/style/layers/layers.cpp',
+ 'src/style/layers/layer.cpp',
+ 'src/style/layers/background_layer.cpp',
+ 'src/style/layers/circle_layer.cpp',
+ 'src/style/layers/fill_layer.cpp',
+ 'src/style/layers/line_layer.cpp',
+ 'src/style/layers/raster_layer.cpp',
+ 'src/style/layers/symbol_layer.cpp',
'../default/string_stdlib.cpp',
'../default/image.cpp',
'../default/png_reader.cpp',
@@ -50,6 +61,7 @@
'cflags_cc': [
'<@(boost_cflags)',
+ '<@(geojson_cflags)',
'<@(rapidjson_cflags)',
'<@(nunicode_cflags)',
'<@(sqlite_cflags)',
@@ -79,6 +91,7 @@
'<@(libpng_ldflags)',
'<@(libjpeg-turbo_static_libs)',
'<@(libjpeg-turbo_ldflags)',
+ '<@(geojson_static_libs)'
],
},
},
diff --git a/platform/android/scripts/generate-style-code.js b/platform/android/scripts/generate-style-code.js
new file mode 100644
index 0000000000..03bfac8067
--- /dev/null
+++ b/platform/android/scripts/generate-style-code.js
@@ -0,0 +1,114 @@
+'use strict';
+
+const fs = require('fs');
+const ejs = require('ejs');
+const spec = require('mapbox-gl-style-spec').latest;
+const _ = require('lodash');
+
+global.iff = function (condition, val) {
+ return condition() ? val : "";
+}
+
+
+global.camelize = function (str) {
+ return str.replace(/(?:^|-)(.)/g, function (_, x) {
+ return x.toUpperCase();
+ });
+}
+
+global.camelizeWithLeadingLowercase = function (str) {
+ return str.replace(/-(.)/g, function (_, x) {
+ return x.toUpperCase();
+ });
+}
+
+global.snakeCaseUpper = function (str) {
+ return str.replace(/-/g, "_").toUpperCase();
+}
+
+global.propertyType = function propertyType(property) {
+//TODO: Doe we want these exceptions?
+// if (/-translate-anchor$/.test(property.name)) {
+ // return 'TranslateAnchorType';
+ // }
+ // if (/-(rotation|pitch)-alignment$/.test(property.name)) {
+ // return 'AlignmentType';
+ // }
+ switch (property.type) {
+ case 'boolean':
+ return 'Boolean';
+ case 'number':
+ return 'Float';
+ case 'string':
+ return 'String';
+ case 'enum':
+ return `String`;
+ case 'color':
+ return `String`;
+ case 'array':
+ return `${propertyType({type:property.value})}[]`;
+ default:
+ throw new Error(`unknown type for ${property.name}`);
+ }
+}
+
+global.propertyTypeAnnotation = function propertyTypeAnnotation(property) {
+ switch (property.type) {
+ case 'enum':
+ return `@Property.${snakeCaseUpper(property.name)}`;
+ default:
+ return "";
+ }
+};
+
+//Process Layers
+const layers = spec.layer.type.values.map((type) => {
+ const layoutProperties = Object.keys(spec[`layout_${type}`]).reduce((memo, name) => {
+ if (name !== 'visibility') {
+ spec[`layout_${type}`][name].name = name;
+ memo.push(spec[`layout_${type}`][name]);
+ }
+ return memo;
+ }, []);
+
+ const paintProperties = Object.keys(spec[`paint_${type}`]).reduce((memo, name) => {
+ spec[`paint_${type}`][name].name = name;
+ memo.push(spec[`paint_${type}`][name]);
+ return memo;
+ }, []);
+
+ return {
+ type: type,
+ layoutProperties: layoutProperties,
+ paintProperties: paintProperties,
+ };
+});
+
+const layerHpp = ejs.compile(fs.readFileSync('platform/android/scripts/layer.hpp.ejs', 'utf8'), {strict: true});
+const layerCpp = ejs.compile(fs.readFileSync('platform/android/scripts/layer.cpp.ejs', 'utf8'), {strict: true});
+const layerJava = ejs.compile(fs.readFileSync('platform/android/scripts/layer.java.ejs', 'utf8'), {strict: true});
+
+for (const layer of layers) {
+ fs.writeFileSync(`platform/android/src/style/layers/${layer.type}_layer.hpp`, layerHpp(layer));
+ fs.writeFileSync(`platform/android/src/style/layers/${layer.type}_layer.cpp`, layerCpp(layer));
+ fs.writeFileSync(`platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/${camelize(layer.type)}Layer.java`, layerJava(layer));
+}
+
+//Process all layer properties
+const layoutProperties = _(layers).map('layoutProperties').flatten().value();
+const paintProperties = _(layers).map('paintProperties').flatten().value();
+
+const propertiesTemplate = ejs.compile(fs.readFileSync('platform/android/scripts/layer_property_factory.java.ejs', 'utf8'), {strict: true});
+fs.writeFileSync(
+ `platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java`,
+ propertiesTemplate({layoutProperties: layoutProperties, paintProperties: paintProperties})
+);
+
+//Create types for the enum properties
+const enumProperties = _(layoutProperties).union(paintProperties).filter({'type': 'enum'}).value();
+const enumPropertyTemplate = ejs.compile(fs.readFileSync('platform/android/scripts/layer_property.java.ejs', 'utf8'), {strict: true});
+fs.writeFileSync(
+ `platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java`,
+ enumPropertyTemplate({properties: enumProperties})
+);
+
diff --git a/platform/android/scripts/layer.cpp.ejs b/platform/android/scripts/layer.cpp.ejs
new file mode 100644
index 0000000000..b53bc3c4d6
--- /dev/null
+++ b/platform/android/scripts/layer.cpp.ejs
@@ -0,0 +1,64 @@
+<%
+ const type = locals.type;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "<%- type %>_layer.hpp"
+
+#include <string>
+
+//XXX
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+<% if (type === 'background') { -%>
+ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String layerId)
+ : Layer(env, std::make_unique<mbgl::style::<%- camelize(type) %>Layer>(jni::Make<std::string>(env, layerId))) {
+<% } else { -%>
+ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::<%- camelize(type) %>Layer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+<% } -%>
+ mbgl::Log::Debug(mbgl::Event::JNI, "<%- camelize(type) %>Layer constructed, owning reference");
+ }
+
+ <%- camelize(type) %>Layer::<%- camelize(type) %>Layer(mbgl::Map& map, mbgl::style::<%- camelize(type) %>Layer& coreLayer)
+ : Layer(map, coreLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "<%- camelize(type) %>Layer Non-owning reference constructor");
+ }
+
+ <%- camelize(type) %>Layer::~<%- camelize(type) %>Layer() = default;
+
+ jni::Class<<%- camelize(type) %>Layer> <%- camelize(type) %>Layer::javaClass;
+
+ jni::jobject* <%- camelize(type) %>Layer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = <%- camelize(type) %>Layer::javaClass.template GetConstructor<jni::jlong>(env);
+ return <%- camelize(type) %>Layer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void <%- camelize(type) %>Layer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native background layer");
+
+ //Lookup the class
+ <%- camelize(type) %>Layer::javaClass = *jni::Class<<%- camelize(type) %>Layer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<<%- camelize(type) %>Layer>(
+ env, <%- camelize(type) %>Layer::javaClass, "nativePtr",
+<% if (type === 'background') { -%>
+ std::make_unique<<%- camelize(type) %>Layer, JNIEnv&, jni::String>,
+<% } else { -%>
+ std::make_unique<<%- camelize(type) %>Layer, JNIEnv&, jni::String, jni::String>,
+<% } -%>
+ "initialize",
+ "finalize"
+ );
+
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/scripts/layer.hpp.ejs b/platform/android/scripts/layer.hpp.ejs
new file mode 100644
index 0000000000..f735e3e35c
--- /dev/null
+++ b/platform/android/scripts/layer.hpp.ejs
@@ -0,0 +1,38 @@
+<%
+ const type = locals.type;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/<%- type %>_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class <%- camelize(type) %>Layer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/<%- camelize(type) %>Layer"; };
+
+ static jni::Class<<%- camelize(type) %>Layer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+<% if (type === 'background') { -%>
+ <%- camelize(type) %>Layer(jni::JNIEnv&, jni::String);
+<% } else { -%>
+ <%- camelize(type) %>Layer(jni::JNIEnv&, jni::String, jni::String);
+<% } -%>
+
+ <%- camelize(type) %>Layer(mbgl::Map&, mbgl::style::<%- camelize(type) %>Layer&);
+
+ ~<%- camelize(type) %>Layer();
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/scripts/layer.java.ejs b/platform/android/scripts/layer.java.ejs
new file mode 100644
index 0000000000..a5ca4c0521
--- /dev/null
+++ b/platform/android/scripts/layer.java.ejs
@@ -0,0 +1,44 @@
+<%
+ const type = locals.type;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+/**
+ * <%- camelize(type) %> Layer
+ */
+public class <%- camelize(type) %>Layer extends Layer {
+
+ public <%- camelize(type) %>Layer(long nativePtr) {
+ super(nativePtr);
+ }
+
+<% if (type === 'background') { -%>
+ public <%- camelize(type) %>Layer(String layerId) {
+ initialize(layerId);
+ }
+
+ protected native void initialize(String layerId);
+<% } else { -%>
+ public <%- camelize(type) %>Layer(String layerId, String sourceId) {
+ initialize(layerId, sourceId);
+ }
+
+ protected native void initialize(String layerId, String sourceId);
+
+ public void setSourceLayer(String sourceLayer) {
+ nativeSetSourceLayer(sourceLayer);
+ }
+<% } -%>
+
+<% if (type !== 'background' && type !== 'raster') { -%>
+ public void setFilter(Filter.Statement filter) {
+ this.setFilter(filter.toArray());
+ }
+
+ public void setFilter(Object[] filter) {
+ nativeSetFilter(filter);
+ }
+
+<% } -%>
+}
diff --git a/platform/android/scripts/layer_property.java.ejs b/platform/android/scripts/layer_property.java.ejs
new file mode 100644
index 0000000000..7119feeef2
--- /dev/null
+++ b/platform/android/scripts/layer_property.java.ejs
@@ -0,0 +1,42 @@
+<%
+ const properties = locals.properties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.support.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Paint/Layout properties for Layer
+ */
+public abstract class Property<T> {
+
+<% for (const property of properties) { -%>
+ //<%- property.name %>
+<% for (const value of property.values) { -%>
+ public static final String <%- snakeCaseUpper(property.name) %>_<%- snakeCaseUpper(value) %> = "<%- value %>";
+<% } -%>
+
+ @StringDef({
+ <% for (const value of property.values) { -%>
+ <%- snakeCaseUpper(property.name) %>_<%- snakeCaseUpper(value) %>,
+ <% } -%>
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface <%- snakeCaseUpper(property.name) %> {}
+
+<% } -%>
+
+ //Class definition
+ public final String name;
+ public final T value;
+
+ /* package */ Property(String name, T value) {
+ this.name = name;
+ this.value = value;
+ }
+
+}
diff --git a/platform/android/scripts/layer_property_factory.java.ejs b/platform/android/scripts/layer_property_factory.java.ejs
new file mode 100644
index 0000000000..045f206d3f
--- /dev/null
+++ b/platform/android/scripts/layer_property_factory.java.ejs
@@ -0,0 +1,45 @@
+<%
+ const paintProperties = locals.paintProperties;
+ const layoutProperties = locals.layoutProperties;
+-%>
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+package com.mapbox.mapboxsdk.style.layers;
+
+import android.annotation.SuppressLint;
+import android.support.annotation.ColorInt;
+
+/**
+ * Constructs paint/layout properties for Layers
+ * @see <a href="https://www.mapbox.com/mapbox-gl-style-spec/#layers>Layer style documentation</a>
+ *
+ */
+public class PropertyFactory {
+
+ public static Property<String> visibility(Boolean visible) {
+ return new LayoutProperty<>("visibility", visible? "visible": "none");
+ }
+
+<% for (const property of paintProperties) { -%>
+<% if (property.type == 'color') { -%>
+ public static Property<String> <%- camelizeWithLeadingLowercase(property.name) %>(@ColorInt int value) {
+ return new PaintProperty<>("<%- property.name %>", colorToRgbaString(value));
+ }
+
+<% } -%>
+ public static Property<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %>(<%- propertyTypeAnnotation(property) %><%- iff(() => propertyTypeAnnotation(property), " ") %><%- propertyType(property) %> value) {
+ return new PaintProperty<>("<%- property.name %>", value);
+ }
+
+<% } -%>
+<% for (const property of layoutProperties) { -%>
+ public static Property<<%- propertyType(property) %>> <%- camelizeWithLeadingLowercase(property.name) %>(<%- propertyTypeAnnotation(property) %><%- iff(() => propertyTypeAnnotation(property), " ") %><%- propertyType(property) %> value) {
+ return new LayoutProperty<>("<%- property.name %>", value);
+ }
+
+<% } -%>
+ @SuppressLint("DefaultLocale")
+ static String colorToRgbaString(@ColorInt int value) {
+ return String.format("rgba(%d, %d, %d, %d)", (value >> 16) & 0xFF, (value >> 8) & 0xFF, value & 0xFF, (value >> 24) & 0xFF);
+ }
+
+}
diff --git a/platform/android/src/java_types.cpp b/platform/android/src/java_types.cpp
new file mode 100644
index 0000000000..6383426387
--- /dev/null
+++ b/platform/android/src/java_types.cpp
@@ -0,0 +1,37 @@
+#include "java_types.hpp"
+
+namespace mbgl {
+namespace android {
+namespace java {
+
+ jni::jclass* ObjectArray::jclass;
+
+ jni::jclass* String::jclass;
+
+ jni::jclass* Boolean::jclass;
+ jni::jmethodID* Boolean::booleanValueMethodId;
+
+ jni::jclass* Number::jclass;
+ jni::jmethodID* Number::floatValueMethodId;
+
+ jni::jclass* Map::jclass;
+ jni::jmethodID* Map::getMethodId;
+
+ void registerNatives(JNIEnv& env) {
+ ObjectArray::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "[Ljava/lang/Object;")).release();
+
+ String::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/String")).release();
+
+ Boolean::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Boolean")).release();
+ Boolean::booleanValueMethodId = &jni::GetMethodID(env, *Boolean::jclass, "booleanValue", "()Z");
+
+ Number::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Number")).release();
+ Number::floatValueMethodId = &jni::GetMethodID(env, *Number::jclass, "floatValue", "()F");
+
+ Map::jclass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Map")).release();
+ Map::getMethodId = &jni::GetMethodID(env, *Map::jclass, "get", "(Ljava/lang/Object;)Ljava/lang/Object;");
+ }
+
+}
+}
+}
diff --git a/platform/android/src/java_types.hpp b/platform/android/src/java_types.hpp
new file mode 100644
index 0000000000..2e2c5fc2d6
--- /dev/null
+++ b/platform/android/src/java_types.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace java {
+
+ struct ObjectArray {
+ static jni::jclass* jclass;
+ };
+
+ struct String {
+ static jni::jclass* jclass;
+ };
+
+ struct Boolean {
+ static jni::jclass* jclass;
+ static jni::jmethodID* booleanValueMethodId;
+ };
+
+ struct Number {
+ static jni::jclass* jclass;
+ static jni::jmethodID* floatValueMethodId;
+ };
+
+ struct Map {
+ static jni::jclass* jclass;
+ static jni::jmethodID* getMethodId;
+ };
+
+ void registerNatives(JNIEnv&);
+}
+}
+} \ No newline at end of file
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index c9670bfcb9..18a22cb8b3 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -9,11 +9,15 @@
#include <sys/system_properties.h>
#include "jni.hpp"
+#include "java_types.hpp"
#include "native_map_view.hpp"
+#include "style/layers/layers.hpp"
+#include "style/sources/sources.hpp"
#include <mbgl/map/map.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/annotation/annotation.hpp>
+#include <mbgl/style/layer.hpp>
#include <mbgl/style/layers/custom_layer.hpp>
#include <mbgl/sprite/sprite_image.hpp>
#include <mbgl/platform/event.hpp>
@@ -1085,6 +1089,81 @@ void nativeRemoveCustomLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapView
nativeMapView->getMap().removeLayer(std_string_from_jstring(env, id));
}
+jni::jobject* nativeGetLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* layerId) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetLayer");
+
+ assert(env);
+ assert(nativeMapViewPtr != 0);
+
+ //Get the native map peer
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ //Find the layer
+ mbgl::style::Layer* coreLayer = nativeMapView->getMap().getLayer(std_string_from_jstring(env, layerId));
+ if (!coreLayer) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "No layer found");
+ return jni::Object<Layer>();
+ }
+
+ //Create and return the layer's native peer
+ return createJavaLayerPeer(*env, nativeMapView->getMap(), *coreLayer);
+}
+
+void nativeAddLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jlong nativeLayerPtr, jni::jstring* before) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddLayer");
+ assert(nativeMapViewPtr != 0);
+ assert(nativeLayerPtr != 0);
+
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ Layer *layer = reinterpret_cast<Layer *>(nativeLayerPtr);
+
+ nativeMapView->getMap().addLayer(
+ layer->releaseCoreLayer(),
+ before ? mbgl::optional<std::string>(std_string_from_jstring(env, before)) : mbgl::optional<std::string>()
+ );
+}
+
+void nativeRemoveLayer(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveLayer");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ try {
+ nativeMapView->getMap().removeLayer(std_string_from_jstring(env, id));
+ } catch (const std::runtime_error& error) {
+ jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/layers/NoSuchLayerException"), error.what());
+ }
+}
+
+void nativeAddSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id, jni::jobject* jsource) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeAddSource");
+ assert(nativeMapViewPtr != 0);
+ assert(id != nullptr);
+ assert(jsource != nullptr);
+
+ //Convert
+ mbgl::optional<std::unique_ptr<mbgl::style::Source>> source = convertToNativeSource(
+ *env,
+ jni::Object<jni::jobject>(jsource), jni::String(id)
+ );
+
+ //Add to map view
+ if (source) {
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ nativeMapView->getMap().addSource(std::move(*source));
+ }
+}
+
+void nativeRemoveSource(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jstring* id) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeRemoveSource");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+ try {
+ nativeMapView->getMap().removeSource(std_string_from_jstring(env, id));
+ } catch (const std::runtime_error& error) {
+ jni::ThrowNew(*env, jni::FindClass(*env, "com/mapbox/mapboxsdk/style/layers/NoSuchSourceException"), error.what());
+ }
+}
+
// Offline calls begin
jlong createDefaultFileSource(JNIEnv *env, jni::jobject* obj, jni::jstring* cachePath_, jni::jstring* assetRoot_, jlong maximumCacheSize) {
@@ -1545,6 +1624,9 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
mbgl::android::RegisterNativeHTTPRequest(env);
+ java::registerNatives(env);
+ registerNativeLayers(env);
+
latLngClass = &jni::FindClass(env, "com/mapbox/mapboxsdk/geometry/LatLng");
latLngClass = jni::NewGlobalRef(env, latLngClass).release();
latLngConstructorId = &jni::GetMethodID(env, *latLngClass, "<init>", "(DD)V");
@@ -1699,6 +1781,11 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeFlyTo, "(JDDDJDD)V"),
MAKE_NATIVE_METHOD(nativeAddCustomLayer, "(JLcom/mapbox/mapboxsdk/layers/CustomLayer;Ljava/lang/String;)V"),
MAKE_NATIVE_METHOD(nativeRemoveCustomLayer, "(JLjava/lang/String;)V"),
+ 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(nativeRemoveSource, "(JLjava/lang/String;)V"),
MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V")
);
diff --git a/platform/android/src/style/android_conversion.hpp b/platform/android/src/style/android_conversion.hpp
new file mode 100644
index 0000000000..5128cce51f
--- /dev/null
+++ b/platform/android/src/style/android_conversion.hpp
@@ -0,0 +1,95 @@
+#pragma once
+
+#include "value.hpp"
+
+#include <mbgl/platform/log.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+
+//XXX
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+
+inline bool isUndefined(const mbgl::android::Value& value) {
+ return value.isNull();
+}
+
+inline bool isArray(const mbgl::android::Value& value) {
+ return value.isArray();
+}
+
+inline bool isObject(const mbgl::android::Value& value) {
+ return value.isObject();
+}
+
+inline std::size_t arrayLength(const mbgl::android::Value& value) {
+ return value.getLength();;
+}
+
+inline mbgl::android::Value arrayMember(const mbgl::android::Value& value, std::size_t i) {
+ return value.get(i);
+}
+
+inline optional<mbgl::android::Value> objectMember(const mbgl::android::Value& value, const char* key) {
+ mbgl::android::Value member = value.get(key);
+
+ if (!member.isNull()) {
+ return member;
+ } else {
+ return {};
+ }
+}
+
+template <class Fn>
+optional<Error> eachMember(const mbgl::android::Value& value, Fn&& fn) {
+ //TODO
+ mbgl::Log::Warning(mbgl::Event::Android, "eachMember not implemented");
+ return {};
+}
+
+inline optional<bool> toBool(const mbgl::android::Value& value) {
+ if (value.isBool()) {
+ return value.toBool();
+ } else {
+ return {};
+ }
+}
+
+inline optional<float> toNumber(const mbgl::android::Value& value) {
+ if (value.isNumber()) {
+ return value.toNumber();
+ } else {
+ return {};
+ }
+}
+
+inline optional<std::string> toString(const mbgl::android::Value& value) {
+ if (value.isString()) {
+ return value.toString();
+ } else {
+ return {};
+ }
+}
+
+inline optional<Value> toValue(const mbgl::android::Value& value) {
+ if (value.isBool()) {
+ return { value.toBool() };
+ } else if (value.isString()) {
+ return { value.toString() };
+ } else if (value.isNumber()) {
+ return { value.toNumber() };
+ } else {
+ return {};
+ }
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/android_geojson.hpp b/platform/android/src/style/android_geojson.hpp
new file mode 100644
index 0000000000..b2e0ca0a4a
--- /dev/null
+++ b/platform/android/src/style/android_geojson.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "value.hpp"
+
+#include <mapbox/geojson.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/geojson.hpp>
+#include <mbgl/util/rapidjson.hpp>
+#include <mbgl/platform/log.hpp>
+#include <jni/jni.hpp>
+
+#include <sstream>
+#include <string>
+
+namespace mbgl {
+namespace style {
+namespace conversion {
+
+template <>
+Result<GeoJSON> convertGeoJSON(const mbgl::android::Value& value) {
+
+ //Value should be a string wrapped in an object
+ mbgl::android::Value jsonValue = value.get("data");
+ if(value.isNull()) {
+ return Error { "no json data found" };
+ }
+ std::string jsonString = value.get("data").toString();
+
+ rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> d;
+ d.Parse(jsonString.c_str());
+
+ if (d.HasParseError()) {
+ std::stringstream message;
+ message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError());
+ return Error { message.str() };
+ }
+
+ conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d);
+ if (!geoJSON) {
+ return Error { geoJSON.error().message };
+ }
+
+ return geoJSON;
+}
+
+} // namespace conversion
+} // namespace style
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/style/layers/background_layer.cpp b/platform/android/src/style/layers/background_layer.cpp
new file mode 100644
index 0000000000..4847d59420
--- /dev/null
+++ b/platform/android/src/style/layers/background_layer.cpp
@@ -0,0 +1,52 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "background_layer.hpp"
+
+#include <string>
+
+//XXX
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+ BackgroundLayer::BackgroundLayer(jni::JNIEnv& env, jni::String layerId)
+ : Layer(env, std::make_unique<mbgl::style::BackgroundLayer>(jni::Make<std::string>(env, layerId))) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "BackgroundLayer constructed, owning reference");
+ }
+
+ BackgroundLayer::BackgroundLayer(mbgl::Map& map, mbgl::style::BackgroundLayer& coreLayer)
+ : Layer(map, coreLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "BackgroundLayer Non-owning reference constructor");
+ }
+
+ BackgroundLayer::~BackgroundLayer() = default;
+
+ jni::Class<BackgroundLayer> BackgroundLayer::javaClass;
+
+ jni::jobject* BackgroundLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = BackgroundLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return BackgroundLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void BackgroundLayer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native background layer");
+
+ //Lookup the class
+ BackgroundLayer::javaClass = *jni::Class<BackgroundLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<BackgroundLayer>(
+ env, BackgroundLayer::javaClass, "nativePtr",
+ std::make_unique<BackgroundLayer, JNIEnv&, jni::String>,
+ "initialize",
+ "finalize"
+ );
+
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/background_layer.hpp b/platform/android/src/style/layers/background_layer.hpp
new file mode 100644
index 0000000000..b253b4861c
--- /dev/null
+++ b/platform/android/src/style/layers/background_layer.hpp
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/background_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class BackgroundLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/BackgroundLayer"; };
+
+ static jni::Class<BackgroundLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ BackgroundLayer(jni::JNIEnv&, jni::String);
+
+ BackgroundLayer(mbgl::Map&, mbgl::style::BackgroundLayer&);
+
+ ~BackgroundLayer();
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/circle_layer.cpp b/platform/android/src/style/layers/circle_layer.cpp
new file mode 100644
index 0000000000..ad0d0b34ce
--- /dev/null
+++ b/platform/android/src/style/layers/circle_layer.cpp
@@ -0,0 +1,52 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "circle_layer.hpp"
+
+#include <string>
+
+//XXX
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+ CircleLayer::CircleLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::CircleLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "CircleLayer constructed, owning reference");
+ }
+
+ CircleLayer::CircleLayer(mbgl::Map& map, mbgl::style::CircleLayer& coreLayer)
+ : Layer(map, coreLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "CircleLayer Non-owning reference constructor");
+ }
+
+ CircleLayer::~CircleLayer() = default;
+
+ jni::Class<CircleLayer> CircleLayer::javaClass;
+
+ jni::jobject* CircleLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = CircleLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return CircleLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void CircleLayer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native background layer");
+
+ //Lookup the class
+ CircleLayer::javaClass = *jni::Class<CircleLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<CircleLayer>(
+ env, CircleLayer::javaClass, "nativePtr",
+ std::make_unique<CircleLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize"
+ );
+
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/circle_layer.hpp b/platform/android/src/style/layers/circle_layer.hpp
new file mode 100644
index 0000000000..e0dc94eb00
--- /dev/null
+++ b/platform/android/src/style/layers/circle_layer.hpp
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class CircleLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/CircleLayer"; };
+
+ static jni::Class<CircleLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ CircleLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ CircleLayer(mbgl::Map&, mbgl::style::CircleLayer&);
+
+ ~CircleLayer();
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/fill_layer.cpp b/platform/android/src/style/layers/fill_layer.cpp
new file mode 100644
index 0000000000..1056c8353b
--- /dev/null
+++ b/platform/android/src/style/layers/fill_layer.cpp
@@ -0,0 +1,52 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "fill_layer.hpp"
+
+#include <string>
+
+//XXX
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+ FillLayer::FillLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::FillLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "FillLayer constructed, owning reference");
+ }
+
+ FillLayer::FillLayer(mbgl::Map& map, mbgl::style::FillLayer& coreLayer)
+ : Layer(map, coreLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "FillLayer Non-owning reference constructor");
+ }
+
+ FillLayer::~FillLayer() = default;
+
+ jni::Class<FillLayer> FillLayer::javaClass;
+
+ jni::jobject* FillLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = FillLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return FillLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void FillLayer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native background layer");
+
+ //Lookup the class
+ FillLayer::javaClass = *jni::Class<FillLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<FillLayer>(
+ env, FillLayer::javaClass, "nativePtr",
+ std::make_unique<FillLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize"
+ );
+
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/fill_layer.hpp b/platform/android/src/style/layers/fill_layer.hpp
new file mode 100644
index 0000000000..5ed80d401f
--- /dev/null
+++ b/platform/android/src/style/layers/fill_layer.hpp
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class FillLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/FillLayer"; };
+
+ static jni::Class<FillLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ FillLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ FillLayer(mbgl::Map&, mbgl::style::FillLayer&);
+
+ ~FillLayer();
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp
new file mode 100644
index 0000000000..aec7079698
--- /dev/null
+++ b/platform/android/src/style/layers/layer.cpp
@@ -0,0 +1,158 @@
+#include "layer.hpp"
+#include "../android_conversion.hpp"
+
+#include <jni/jni.hpp>
+
+#include <mbgl/platform/log.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/layer.hpp>
+#include <mbgl/style/conversion/source.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+ /**
+ * Invoked when the construction is initiated from the jvm through a subclass
+ */
+ Layer::Layer(jni::JNIEnv& env, std::unique_ptr<mbgl::style::Layer> coreLayer)
+ : ownedLayer(std::move(coreLayer))
+ , layer(*ownedLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "Layer constructed, owning reference");
+ }
+
+ Layer::Layer(mbgl::Map& coreMap, mbgl::style::Layer& coreLayer) : layer(coreLayer) , map(&coreMap) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Non-owning reference constructor");
+ }
+
+ Layer::~Layer() {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Layer destroyed");
+ }
+
+ jni::String Layer::getId(jni::JNIEnv& env) {
+ return jni::Make<jni::String>(env, layer.getID());
+ }
+
+ std::unique_ptr<mbgl::style::Layer> Layer::releaseCoreLayer() {
+ assert(ownedLayer != nullptr);
+ return std::move(ownedLayer);
+ }
+
+ void Layer::setLayoutProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Set layout property");
+
+ Value value(env, jvalue);
+
+ //Convert and set property
+ optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setLayoutProperty(layer, jni::Make<std::string>(env, jname), value);
+ if (error) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message);
+ return;
+ }
+
+ //Update the style if attached
+ if (ownedLayer == nullptr) {
+ map->update(mbgl::Update::RecalculateStyle);
+ }
+ }
+
+ void Layer::setPaintProperty(jni::JNIEnv& env, jni::String jname, jni::Object<> jvalue) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Set paint property");
+
+ Value value(env, jvalue);
+
+ //Convert and set property
+ optional<mbgl::style::conversion::Error> error = mbgl::style::conversion::setPaintProperty(layer, jni::Make<std::string>(env, jname), value, mbgl::optional<std::string>());
+ if (error) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Error setting property: " + jni::Make<std::string>(env, jname) + " " + error->message);
+ return;
+ }
+ }
+
+ void Layer::updateStyle(jni::JNIEnv&, jni::jboolean updateClasses) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Update style property. Update classes: " + std::to_string(updateClasses));
+
+ //Update the style only if attached
+ if (ownedLayer == 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 layer is not attached to map (yet)");
+ }
+ }
+
+ void Layer::setFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter) {
+ using namespace mbgl::style;
+ using namespace mbgl::style::conversion;
+ mbgl::Log::Debug(mbgl::Event::JNI, "Set filter");
+
+ Value wrapped(env, jfilter);
+ Filter filter;
+
+ Result<Filter> converted = convert<Filter>(wrapped);
+ if (!converted) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + converted.error().message);
+ return;
+ }
+ filter = std::move(*converted);
+
+ if (layer.is<FillLayer>()) {
+ layer.as<FillLayer>()->setFilter(filter);
+ } else if (layer.is<LineLayer>()) {
+ layer.as<LineLayer>()->setFilter(filter);
+ } else if (layer.is<SymbolLayer>()) {
+ layer.as<SymbolLayer>()->setFilter(filter);
+ } else if (layer.is<CircleLayer>()) {
+ layer.as<CircleLayer>()->setFilter(filter);
+ } else {
+ mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support filters");
+ }
+ }
+
+ void Layer::setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer) {
+ using namespace mbgl::style;
+
+ std::string layerId = jni::Make<std::string>(env, sourceLayer);
+ mbgl::Log::Debug(mbgl::Event::JNI, "Set source layer: " + layerId);
+
+ if (layer.is<FillLayer>()) {
+ layer.as<FillLayer>()->setSourceLayer(layerId);
+ } else if (layer.is<LineLayer>()) {
+ layer.as<LineLayer>()->setSourceLayer(layerId);
+ } else if (layer.is<SymbolLayer>()) {
+ layer.as<SymbolLayer>()->setSourceLayer(layerId);
+ } else if (layer.is<CircleLayer>()) {
+ layer.as<CircleLayer>()->setSourceLayer(layerId);
+ } else {
+ mbgl::Log::Warning(mbgl::Event::JNI, "Layer doesn't support source layer");
+ }
+ }
+
+ jni::Class<Layer> Layer::javaClass;
+
+ void Layer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native base layer");
+
+ //Lookup the class
+ Layer::javaClass = *jni::Class<Layer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<Layer>(env, Layer::javaClass, "nativePtr",
+ METHOD(&Layer::getId, "nativeGetId"),
+ METHOD(&Layer::setLayoutProperty, "nativeSetLayoutProperty"),
+ METHOD(&Layer::setPaintProperty, "nativeSetPaintProperty"),
+ METHOD(&Layer::updateStyle, "nativeUpdateStyle"),
+ METHOD(&Layer::setFilter, "nativeSetFilter"),
+ METHOD(&Layer::setSourceLayer, "nativeSetSourceLayer")
+ );
+
+ }
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/layers/layer.hpp b/platform/android/src/style/layers/layer.hpp
new file mode 100644
index 0000000000..8b3487551b
--- /dev/null
+++ b/platform/android/src/style/layers/layer.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/layer.hpp>
+
+#include "../value.hpp"
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class Layer : private mbgl::util::noncopyable {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/Layer"; };
+
+ static jni::Class<Layer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ /*
+ * Called when a Java object is created on the c++ side
+ */
+ Layer(mbgl::Map&, mbgl::style::Layer&);
+
+ /*
+ * Called when a Java object was created from the jvm side
+ */
+ Layer(jni::JNIEnv&, std::unique_ptr<mbgl::style::Layer>);
+
+ virtual ~Layer();
+
+ virtual jni::jobject* createJavaPeer(jni::JNIEnv&) = 0;
+
+ jni::String getId(jni::JNIEnv&);
+
+ //Release the owned view and return it
+ std::unique_ptr<mbgl::style::Layer> releaseCoreLayer();
+
+ void setLayoutProperty(jni::JNIEnv&, jni::String, jni::Object<> value);
+
+ void setPaintProperty(jni::JNIEnv&, jni::String, jni::Object<> value);
+
+ void updateStyle(jni::JNIEnv&, jni::jboolean updateClasses);
+
+ /* common properties, but not shared by all */
+
+ void setFilter(jni::JNIEnv& env, jni::Array<jni::Object<>> jfilter);
+
+ void setSourceLayer(jni::JNIEnv& env, jni::String sourceLayer);
+
+protected:
+ std::unique_ptr<mbgl::style::Layer> ownedLayer;
+ mbgl::style::Layer& layer;
+ mbgl::Map* map;
+
+};
+
+} //android
+} //mbgl
+
+
+
+
diff --git a/platform/android/src/style/layers/layers.cpp b/platform/android/src/style/layers/layers.cpp
new file mode 100644
index 0000000000..4ccdc829f2
--- /dev/null
+++ b/platform/android/src/style/layers/layers.cpp
@@ -0,0 +1,60 @@
+#include "layers.hpp"
+
+#include <mbgl/style/layers/background_layer.hpp>
+#include <mbgl/style/layers/circle_layer.hpp>
+#include <mbgl/style/layers/fill_layer.hpp>
+#include <mbgl/style/layers/line_layer.hpp>
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <mbgl/style/layers/symbol_layer.hpp>
+
+#include "background_layer.hpp"
+#include "circle_layer.hpp"
+#include "fill_layer.hpp"
+#include "line_layer.hpp"
+#include "raster_layer.hpp"
+#include "symbol_layer.hpp"
+
+namespace mbgl {
+namespace android {
+
+Layer* initializeLayerPeer(mbgl::Map& map, mbgl::style::Layer& coreLayer) {
+ Layer* layer;
+ if (coreLayer.is<mbgl::style::BackgroundLayer>()) {
+ layer = new BackgroundLayer(map, *coreLayer.as<mbgl::style::BackgroundLayer>());
+ } else if (coreLayer.is<mbgl::style::CircleLayer>()) {
+ layer = new CircleLayer(map, *coreLayer.as<mbgl::style::CircleLayer>());
+ } else if (coreLayer.is<mbgl::style::FillLayer>()) {
+ layer = new FillLayer(map, *coreLayer.as<mbgl::style::FillLayer>());
+ } else if (coreLayer.is<mbgl::style::LineLayer>()) {
+ layer = new LineLayer(map, *coreLayer.as<mbgl::style::LineLayer>());
+ } else if (coreLayer.is<mbgl::style::RasterLayer>()) {
+ layer = new RasterLayer(map, *coreLayer.as<mbgl::style::RasterLayer>());
+ } else if (coreLayer.is<mbgl::style::SymbolLayer>()) {
+ layer = new SymbolLayer(map, *coreLayer.as<mbgl::style::SymbolLayer>());
+ } else {
+ throw new std::runtime_error("Layer type not implemented");
+ }
+
+ return layer;
+}
+
+jni::jobject* createJavaLayerPeer(jni::JNIEnv& env, mbgl::Map& map, mbgl::style::Layer& coreLayer) {
+ std::unique_ptr<Layer> peerLayer = std::unique_ptr<Layer>(initializeLayerPeer(map, coreLayer));
+ jni::jobject* result = peerLayer->createJavaPeer(env);
+ peerLayer.release();
+
+ return result;
+}
+
+void registerNativeLayers(jni::JNIEnv& env) {
+ Layer::registerNative(env);
+ BackgroundLayer::registerNative(env);
+ CircleLayer::registerNative(env);
+ FillLayer::registerNative(env);
+ LineLayer::registerNative(env);
+ RasterLayer::registerNative(env);
+ SymbolLayer::registerNative(env);
+}
+
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/layers/layers.hpp b/platform/android/src/style/layers/layers.hpp
new file mode 100644
index 0000000000..0c979ec2cf
--- /dev/null
+++ b/platform/android/src/style/layers/layers.hpp
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <mbgl/map/map.hpp>
+#include <mbgl/style/layer.hpp>
+
+#include "layer.hpp"
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+mbgl::android::Layer* initializeLayerPeer(mbgl::Map&, mbgl::style::Layer&);
+
+jni::jobject* createJavaLayerPeer(jni::JNIEnv&, mbgl::Map&, mbgl::style::Layer&);
+
+void registerNativeLayers(jni::JNIEnv&);
+
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/layers/line_layer.cpp b/platform/android/src/style/layers/line_layer.cpp
new file mode 100644
index 0000000000..fdf2af7973
--- /dev/null
+++ b/platform/android/src/style/layers/line_layer.cpp
@@ -0,0 +1,52 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "line_layer.hpp"
+
+#include <string>
+
+//XXX
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+ LineLayer::LineLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::LineLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "LineLayer constructed, owning reference");
+ }
+
+ LineLayer::LineLayer(mbgl::Map& map, mbgl::style::LineLayer& coreLayer)
+ : Layer(map, coreLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "LineLayer Non-owning reference constructor");
+ }
+
+ LineLayer::~LineLayer() = default;
+
+ jni::Class<LineLayer> LineLayer::javaClass;
+
+ jni::jobject* LineLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = LineLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return LineLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void LineLayer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native background layer");
+
+ //Lookup the class
+ LineLayer::javaClass = *jni::Class<LineLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<LineLayer>(
+ env, LineLayer::javaClass, "nativePtr",
+ std::make_unique<LineLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize"
+ );
+
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/line_layer.hpp b/platform/android/src/style/layers/line_layer.hpp
new file mode 100644
index 0000000000..cb53d82940
--- /dev/null
+++ b/platform/android/src/style/layers/line_layer.hpp
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/line_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class LineLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/LineLayer"; };
+
+ static jni::Class<LineLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ LineLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ LineLayer(mbgl::Map&, mbgl::style::LineLayer&);
+
+ ~LineLayer();
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/raster_layer.cpp b/platform/android/src/style/layers/raster_layer.cpp
new file mode 100644
index 0000000000..3da246e128
--- /dev/null
+++ b/platform/android/src/style/layers/raster_layer.cpp
@@ -0,0 +1,52 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "raster_layer.hpp"
+
+#include <string>
+
+//XXX
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+ RasterLayer::RasterLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::RasterLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "RasterLayer constructed, owning reference");
+ }
+
+ RasterLayer::RasterLayer(mbgl::Map& map, mbgl::style::RasterLayer& coreLayer)
+ : Layer(map, coreLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "RasterLayer Non-owning reference constructor");
+ }
+
+ RasterLayer::~RasterLayer() = default;
+
+ jni::Class<RasterLayer> RasterLayer::javaClass;
+
+ jni::jobject* RasterLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = RasterLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return RasterLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void RasterLayer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native background layer");
+
+ //Lookup the class
+ RasterLayer::javaClass = *jni::Class<RasterLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<RasterLayer>(
+ env, RasterLayer::javaClass, "nativePtr",
+ std::make_unique<RasterLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize"
+ );
+
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/raster_layer.hpp b/platform/android/src/style/layers/raster_layer.hpp
new file mode 100644
index 0000000000..f77771631c
--- /dev/null
+++ b/platform/android/src/style/layers/raster_layer.hpp
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/raster_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class RasterLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/RasterLayer"; };
+
+ static jni::Class<RasterLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ RasterLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ RasterLayer(mbgl::Map&, mbgl::style::RasterLayer&);
+
+ ~RasterLayer();
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp
new file mode 100644
index 0000000000..5de08f6511
--- /dev/null
+++ b/platform/android/src/style/layers/symbol_layer.cpp
@@ -0,0 +1,52 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#include "symbol_layer.hpp"
+
+#include <string>
+
+//XXX
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+
+ SymbolLayer::SymbolLayer(jni::JNIEnv& env, jni::String layerId, jni::String sourceId)
+ : Layer(env, std::make_unique<mbgl::style::SymbolLayer>(jni::Make<std::string>(env, layerId), jni::Make<std::string>(env, sourceId))) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "SymbolLayer constructed, owning reference");
+ }
+
+ SymbolLayer::SymbolLayer(mbgl::Map& map, mbgl::style::SymbolLayer& coreLayer)
+ : Layer(map, coreLayer) {
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "SymbolLayer Non-owning reference constructor");
+ }
+
+ SymbolLayer::~SymbolLayer() = default;
+
+ jni::Class<SymbolLayer> SymbolLayer::javaClass;
+
+ jni::jobject* SymbolLayer::createJavaPeer(jni::JNIEnv& env) {
+ static auto constructor = SymbolLayer::javaClass.template GetConstructor<jni::jlong>(env);
+ return SymbolLayer::javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
+ }
+
+ void SymbolLayer::registerNative(jni::JNIEnv& env) {
+ mbgl::Log::Debug(mbgl::Event::JNI, "Registering native background layer");
+
+ //Lookup the class
+ SymbolLayer::javaClass = *jni::Class<SymbolLayer>::Find(env).NewGlobalRef(env).release();
+
+ #define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
+
+ //Register the peer
+ jni::RegisterNativePeer<SymbolLayer>(
+ env, SymbolLayer::javaClass, "nativePtr",
+ std::make_unique<SymbolLayer, JNIEnv&, jni::String, jni::String>,
+ "initialize",
+ "finalize"
+ );
+
+ }
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp
new file mode 100644
index 0000000000..3f3b0e1fe6
--- /dev/null
+++ b/platform/android/src/style/layers/symbol_layer.hpp
@@ -0,0 +1,31 @@
+// This file is generated. Edit android/platform/scripts/generate-style-code.js, then run `make style-code-android`.
+
+#pragma once
+
+#include "layer.hpp"
+#include <mbgl/style/layers/symbol_layer.hpp>
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+class SymbolLayer : public Layer {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/mapboxsdk/style/layers/SymbolLayer"; };
+
+ static jni::Class<SymbolLayer> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+ SymbolLayer(jni::JNIEnv&, jni::String, jni::String);
+
+ SymbolLayer(mbgl::Map&, mbgl::style::SymbolLayer&);
+
+ ~SymbolLayer();
+
+ jni::jobject* createJavaPeer(jni::JNIEnv&);
+};
+
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/style/sources/sources.cpp b/platform/android/src/style/sources/sources.cpp
new file mode 100644
index 0000000000..47c9757e9d
--- /dev/null
+++ b/platform/android/src/style/sources/sources.cpp
@@ -0,0 +1,28 @@
+#include "sources.hpp"
+
+#include "../value.hpp"
+#include "../android_conversion.hpp"
+#include "../android_geojson.hpp"
+
+#include <mbgl/util/constants.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/conversion/source.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+ mbgl::optional<std::unique_ptr<mbgl::style::Source>> convertToNativeSource(jni::JNIEnv& env, jni::Object<jni::jobject> jvalue, jni::String id) {
+ using namespace mbgl::style;
+
+ Value value(env, jvalue);
+ conversion::Result<std::unique_ptr<Source>> source = conversion::convert<std::unique_ptr<Source>>(value, jni::Make<std::string>(env, id));
+ if (!source) {
+ mbgl::Log::Error(mbgl::Event::JNI, "Unable to add source: " + source.error().message);
+ return {};
+ }
+ return std::move(*source);
+ }
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/sources/sources.hpp b/platform/android/src/style/sources/sources.hpp
new file mode 100644
index 0000000000..b967685dfb
--- /dev/null
+++ b/platform/android/src/style/sources/sources.hpp
@@ -0,0 +1,14 @@
+#pragma once
+
+#include <mbgl/style/source.hpp>
+#include <mbgl/util/optional.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+
+ mbgl::optional<std::unique_ptr<mbgl::style::Source>> convertToNativeSource(jni::JNIEnv& env, jni::Object<jni::jobject> jsource, jni::String id);
+
+}
+} \ No newline at end of file
diff --git a/platform/android/src/style/value.cpp b/platform/android/src/style/value.cpp
new file mode 100644
index 0000000000..daad3e998d
--- /dev/null
+++ b/platform/android/src/style/value.cpp
@@ -0,0 +1,67 @@
+#include "value.hpp"
+
+#include "../java_types.hpp"
+
+namespace mbgl {
+namespace android {
+
+ //Instance
+
+ Value::Value(jni::JNIEnv& env, jni::jobject* _value) : jenv(env), value(_value) {}
+
+ Value::~Value() {}
+
+ bool Value::isNull() const {
+ return value == nullptr;
+ }
+
+ bool Value::isArray() const {
+ return jni::IsInstanceOf(jenv, value, *java::ObjectArray::jclass);
+ }
+
+ bool Value::isObject() const {
+ return jni::IsInstanceOf(jenv, value, *java::Map::jclass);;
+ }
+
+ bool Value::isString() const {
+ return jni::IsInstanceOf(jenv, value, *java::String::jclass);
+ }
+
+ bool Value::isBool() const {
+ return jni::IsInstanceOf(jenv, value, *java::Boolean::jclass);
+ }
+
+ bool Value::isNumber() const {
+ return jni::IsInstanceOf(jenv, value, *java::Number::jclass);
+ }
+
+ std::string Value::toString() const {
+ jni::jstring* string = reinterpret_cast<jni::jstring*>(value);
+ return jni::Make<std::string>(jenv, jni::String(string));
+ }
+
+ float Value::toNumber() const {
+ return jni::CallMethod<jni::jfloat>(jenv, value, *java::Number::floatValueMethodId);
+ }
+
+ bool Value::toBool() const {
+ return jni::CallMethod<jni::jboolean>(jenv, value, *java::Boolean::booleanValueMethodId);
+ }
+
+ Value Value::get(const char* key) const {
+ jni::jobject* member = jni::CallMethod<jni::jobject*>(jenv, value, *java::Map::getMethodId, jni::Make<jni::String>(jenv, std::string(key)).Get());
+ return Value(jenv, member);
+ }
+
+ int Value::getLength() const {
+ auto array = (jni::jarray<jni::jobject>*) value;
+ return jni::GetArrayLength(jenv, *array);
+ }
+
+ Value Value::get(const int index ) const {
+ auto array = (jni::jarray<jni::jobject>*) value;
+ return Value(jenv, jni::GetObjectArrayElement(jenv, *array, index));
+ }
+}
+}
+
diff --git a/platform/android/src/style/value.hpp b/platform/android/src/style/value.hpp
new file mode 100644
index 0000000000..2f0b1d9706
--- /dev/null
+++ b/platform/android/src/style/value.hpp
@@ -0,0 +1,35 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+
+class Value {
+public:
+
+ Value(jni::JNIEnv&, jni::jobject*);
+ virtual ~Value();
+
+ bool isNull() const;
+ bool isArray() const;
+ bool isObject() const;
+ bool isString() const;
+ bool isBool() const;
+ bool isNumber() const;
+
+ std::string toString() const;
+ float toNumber() const;
+ bool toBool() const;
+ Value get(const char* key) const;
+ int getLength() const;
+ Value get(const int index ) const;
+
+ jni::JNIEnv& jenv;
+ jni::jobject* value;
+};
+
+}
+}