From 08d1398b70769268a809a767c1bd22e37d164c6f Mon Sep 17 00:00:00 2001 From: Kevin Li Date: Wed, 25 Mar 2020 09:59:38 +0800 Subject: [PATCH 1/5] Add runtime styling APIs on MapSnapshotter (#268) * Add styleable snapshotter. * Add style API for snapshotter * Add observer interface to snapshotter * WIP Add styling to snapshotter activity * Update interface and implement addlayer functions. * Deprecate methods instead of removing * Implement addSource functions. * Implement addImage function * Still use the previous callback interface. * Expose onStyleDidloaded interface * Add setStyle method * Start native snapshotter from start method * Fix snapshotter test. * Fix check style * Keep the original interface * Update gl-native * Add demo for snapshotter heatmap layer * bump gl-native Co-authored-by: Alexander Shalamov (cherry picked from commit da6d8a2b06e61bc9a5f72774a6ca5eeddd717329) --- .../java/com/mapbox/mapboxsdk/maps/Image.java | 2 +- .../com/mapbox/mapboxsdk/maps/MapboxMap.java | 20 +- .../com/mapbox/mapboxsdk/maps/NativeMap.java | 2 + .../mapbox/mapboxsdk/maps/NativeMapView.java | 8 + .../java/com/mapbox/mapboxsdk/maps/Style.java | 56 +++-- .../mapboxsdk/snapshotter/MapSnapshotter.java | 204 +++++++++++++++--- .../mapboxsdk/style/layers/CustomLayer.java | 17 +- .../snapshotter/MapSnapshotterTest.kt | 14 +- .../src/main/AndroidManifest.xml | 18 +- .../customlayer/CustomLayerActivity.java | 4 +- .../activity/render/RenderTestDefinition.java | 3 +- .../snapshot/MapSnapshotterActivity.java | 96 ++++++++- ... MapSnapshotterBitMapOverlayActivity.java} | 17 +- .../MapSnapshotterHeatMapActivity.java | 174 +++++++++++++++ .../MapSnapshotterLocalStyleActivity.java | 3 +- .../snapshot/MapSnapshotterReuseActivity.java | 7 +- .../src/main/res/values/descriptions.xml | 3 +- .../src/main/res/values/titles.xml | 3 +- vendor/mapbox-gl-native | 2 +- 19 files changed, 565 insertions(+), 88 deletions(-) rename MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/{MapSnapshotterMarkerActivity.java => MapSnapshotterBitMapOverlayActivity.java} (92%) create mode 100644 MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.java diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java index 73ead3bc0..380f78b07 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Image.java @@ -3,7 +3,7 @@ import androidx.annotation.Keep; @Keep -class Image { +public class Image { private final byte[] buffer; private final float pixelRatio; private final String name; diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java index 286e48fe8..f50b1a394 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java @@ -5,12 +5,6 @@ import android.graphics.PointF; import android.graphics.RectF; import android.os.Bundle; -import androidx.annotation.FloatRange; -import androidx.annotation.IntRange; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.Size; -import androidx.annotation.UiThread; import android.text.TextUtils; import android.view.View; @@ -44,6 +38,13 @@ import java.util.ArrayList; import java.util.List; +import androidx.annotation.FloatRange; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.Size; +import androidx.annotation.UiThread; + /** * The general class to interact with in the Android Mapbox SDK. It exposes the entry point for all * methods related to the MapView. You cannot instantiate {@link MapboxMap} object directly, rather, @@ -93,6 +94,13 @@ public final class MapboxMap { this.developerAnimationStartedListeners = developerAnimationStartedListeners; } + /** + * Trigger the mapview to repaint. + */ + public void triggerRepaint() { + nativeMapView.triggerRepaint(); + } + void initialise(@NonNull Context context, @NonNull MapboxMapOptions options) { transform.initialise(this, options); uiSettings.initialise(context, options); diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java index 81ff438c6..2b93fa8c6 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java @@ -237,6 +237,8 @@ List queryRenderedFeatures(@NonNull RectF coordinates, float getPixelRatio(); + void triggerRepaint(); + // // Deprecated Annotations API // diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 05ebf36d6..8ff484f10 100755 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -1003,6 +1003,11 @@ public float getPixelRatio() { return pixelRatio; } + @Override + public void triggerRepaint() { + nativeTriggerRepaint(); + } + @NonNull @Override public RectF getDensityDependantRectangle(final RectF rectangle) { @@ -1476,6 +1481,9 @@ public long getNativePtr() { return nativePtr; } + @Keep + private native void nativeTriggerRepaint(); + // // Snapshot // diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java index 31c1ae982..fe88e081a 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Style.java @@ -340,7 +340,7 @@ public void addImage(@NonNull String name, @NonNull Drawable drawable) { */ public void addImage(@NonNull final String name, @NonNull Bitmap bitmap, boolean sdf) { validateState("addImage"); - nativeMap.addImages(new Image[]{toImage(new Builder.ImageWrapper(name, bitmap, sdf))}); + nativeMap.addImages(new Image[] {toImage(new Builder.ImageWrapper(name, bitmap, sdf))}); } /** @@ -932,23 +932,23 @@ public Builder withBitmapImages(boolean sdf, @NonNull Pair... va return this; } - String getUri() { + public String getUri() { return styleUri; } - String getJson() { + public String getJson() { return styleJson; } - List getSources() { + public List getSources() { return sources; } - List getLayers() { + public List getLayers() { return layers; } - List getImages() { + public List getImages() { return images; } @@ -963,18 +963,30 @@ Style build(@NonNull NativeMap nativeMap) { return new Style(this, nativeMap); } - static class ImageWrapper { + public static class ImageWrapper { Bitmap bitmap; String id; boolean sdf; - ImageWrapper(String id, Bitmap bitmap, boolean sdf) { + public ImageWrapper(String id, Bitmap bitmap, boolean sdf) { this.id = id; this.bitmap = bitmap; this.sdf = sdf; } - static ImageWrapper[] convertToImageArray(HashMap bitmapHashMap, boolean sdf) { + public Bitmap getBitmap() { + return bitmap; + } + + public String getId() { + return id; + } + + public boolean isSdf() { + return sdf; + } + + public static ImageWrapper[] convertToImageArray(HashMap bitmapHashMap, boolean sdf) { ImageWrapper[] images = new ImageWrapper[bitmapHashMap.size()]; List keyList = new ArrayList<>(bitmapHashMap.keySet()); for (int i = 0; i < bitmapHashMap.size(); i++) { @@ -985,43 +997,59 @@ static ImageWrapper[] convertToImageArray(HashMap bitmapHashMap, } } - class LayerWrapper { + public class LayerWrapper { Layer layer; LayerWrapper(Layer layer) { this.layer = layer; } + + public Layer getLayer() { + return layer; + } } - class LayerAboveWrapper extends LayerWrapper { + public class LayerAboveWrapper extends LayerWrapper { String aboveLayer; LayerAboveWrapper(Layer layer, String aboveLayer) { super(layer); this.aboveLayer = aboveLayer; } + + public String getAboveLayer() { + return aboveLayer; + } } - class LayerBelowWrapper extends LayerWrapper { + public class LayerBelowWrapper extends LayerWrapper { String belowLayer; LayerBelowWrapper(Layer layer, String belowLayer) { super(layer); this.belowLayer = belowLayer; } + + public String getBelowLayer() { + return belowLayer; + } } - class LayerAtWrapper extends LayerWrapper { + public class LayerAtWrapper extends LayerWrapper { int index; LayerAtWrapper(Layer layer, int index) { super(layer); this.index = index; } + + public int getIndex() { + return index; + } } } - private static Image toImage(Builder.ImageWrapper imageWrapper) { + public static Image toImage(Builder.ImageWrapper imageWrapper) { Bitmap bitmap = imageWrapper.bitmap; if (bitmap.getConfig() != Bitmap.Config.ARGB_8888) { bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false); diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java index c5e2b0164..26af5157a 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java @@ -7,11 +7,6 @@ import android.graphics.Matrix; import android.graphics.PointF; import android.os.Handler; -import androidx.annotation.Keep; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; -import androidx.core.content.res.ResourcesCompat; import android.text.Html; import android.text.TextUtils; import android.util.DisplayMetrics; @@ -28,12 +23,23 @@ import com.mapbox.mapboxsdk.constants.MapboxConstants; import com.mapbox.mapboxsdk.geometry.LatLngBounds; import com.mapbox.mapboxsdk.log.Logger; +import com.mapbox.mapboxsdk.maps.Image; import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.maps.TelemetryDefinition; import com.mapbox.mapboxsdk.storage.FileSource; +import com.mapbox.mapboxsdk.style.layers.Layer; +import com.mapbox.mapboxsdk.style.sources.Source; import com.mapbox.mapboxsdk.utils.FontUtils; import com.mapbox.mapboxsdk.utils.ThreadUtils; +import androidx.annotation.Keep; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.UiThread; +import androidx.core.content.res.ResourcesCompat; + +import static com.mapbox.mapboxsdk.maps.Style.toImage; + /** * The map snapshotter creates a large of the map, rendered * off the UI thread. The snapshotter itself must be used on @@ -44,6 +50,21 @@ public class MapSnapshotter { private static final String TAG = "Mbgl-MapSnapshotter"; + private static final int LOGO_MARGIN_DP = 4; + + // Holds the pointer to JNI NativeMapView + @Keep + private long nativePtr = 0; + + private final Context context; + private boolean fullyLoaded = false; + private Options options; + + @Nullable + private SnapshotReadyCallback callback; + @Nullable + private ErrorHandler errorHandler; + /** * Get notified on snapshot completion. * @@ -57,7 +78,6 @@ public interface SnapshotReadyCallback { * @param snapshot the snapshot */ void onSnapshotReady(MapSnapshot snapshot); - } /** @@ -77,18 +97,6 @@ public interface ErrorHandler { void onError(String error); } - private static final int LOGO_MARGIN_DP = 4; - - // Holds the pointer to JNI NativeMapView - @Keep - private long nativePtr = 0; - - private final Context context; - @Nullable - private SnapshotReadyCallback callback; - @Nullable - private ErrorHandler errorHandler; - /** * MapSnapshotter options */ @@ -96,13 +104,12 @@ public static class Options { private float pixelRatio = 1; private int width; private int height; - private String styleUri = Style.MAPBOX_STREETS; - private String styleJson; private LatLngBounds region; private CameraPosition cameraPosition; private boolean showLogo = true; private String localIdeographFontFamily = MapboxConstants.DEFAULT_FONT; private String apiBaseUrl; + private Style.Builder builder; /** * @param width the width of the image @@ -116,23 +123,42 @@ public Options(int width, int height) { this.height = height; } + /** + * Set a style builder to snapshotter, the contents in builder like layers/sources/images will be applied + * to snapshotter. + * + * @param builder The builder will applied to snapshotter + * @return the mutated {@link Options} + */ + @NonNull + public Options withStyleBuilder(Style.Builder builder) { + this.builder = builder; + return this; + } + + public Style.Builder getBuilder() { + return builder; + } + /** * @param uri The style URI to use * @return the mutated {@link Options} + * @deprecated use {@link #withStyleBuilder(Style.Builder)} instead */ @NonNull public Options withStyle(String uri) { - this.styleUri = uri; + withStyleBuilder(new Style.Builder().fromUri(uri)); return this; } /** * @param styleJson The style json to use * @return the mutated {@link Options} + * @deprecated use {@link #withStyleBuilder(Style.Builder)} instead */ @NonNull public Options withStyleJson(String styleJson) { - this.styleJson = styleJson; + withStyleBuilder(new Style.Builder().fromJson(styleJson)); return this; } @@ -187,6 +213,7 @@ public Options withLogo(boolean showLogo) { * Default system fonts are defined in '/system/etc/fonts.xml' * Default font for local ideograph font family is {@link MapboxConstants#DEFAULT_FONT}. *

+ * * @param fontFamily font family for local ideograph generation. * @return the mutated {@link Options} */ @@ -204,6 +231,7 @@ public Options withLocalIdeographFontFamily(String fontFamily) { * '/system/etc/fonts.xml'. Default font for local ideograph font family is * {@link MapboxConstants#DEFAULT_FONT}. *

+ * * @param fontFamilies font families for local ideograph generation. * @return the mutated {@link Options} */ @@ -274,14 +302,22 @@ public LatLngBounds getRegion() { */ @Deprecated public String getStyleUrl() { - return styleUri; + return builder == null ? Style.MAPBOX_STREETS : builder.getUri(); } /** * @return the style uri */ public String getStyleUri() { - return styleUri; + return builder == null ? Style.MAPBOX_STREETS : builder.getUri(); + } + + /** + * @return the style json + */ + @Nullable + public String getStyleJson() { + return builder == null ? null : builder.getJson(); } /** @@ -329,6 +365,7 @@ public String getApiBaseUri() { public MapSnapshotter(@NonNull Context context, @NonNull Options options) { checkThread(); this.context = context.getApplicationContext(); + this.options = options; TelemetryDefinition telemetry = Mapbox.getTelemetry(); if (telemetry != null) { telemetry.onAppUserTurnstileEvent(); @@ -340,10 +377,11 @@ public MapSnapshotter(@NonNull Context context, @NonNull Options options) { } nativeInitialize(this, fileSource, options.pixelRatio, options.width, - options.height, options.styleUri, options.styleJson, options.region, options.cameraPosition, + options.height, options.getStyleUri(), options.getStyleJson(), options.region, options.cameraPosition, options.showLogo, options.localIdeographFontFamily); } + /** * Starts loading and rendering the snapshot. The callback will be fired * on the calling thread. @@ -412,6 +450,57 @@ public void start(@NonNull SnapshotReadyCallback callback, ErrorHandler errorHan @Keep public native void setStyleJson(String styleJson); + /** + * Adds the layer to the map. The layer must be newly created and not added to the snapshotter before + * + * @param layer the layer to add + * @param below the layer id to add this layer before + */ + private void addLayerBelow(Layer layer, String below) { + nativeAddLayerBelow(layer.getNativePtr(), below); + } + + /** + * Adds the layer to the map. The layer must be newly created and not added to the snapshotter before + * + * @param layer the layer to add + * @param above the layer id to add this layer above + */ + private void addLayerAbove(@NonNull Layer layer, @NonNull String above) { + nativeAddLayerAbove(layer.getNativePtr(), above); + } + + /** + * Adds the layer to the snapshotter at the specified index. The layer must be newly + * created and not added to the snapshotter before + * + * @param layer the layer to add + * @param index the index to insert the layer at + */ + private void addLayerAt(Layer layer, int index) { + nativeAddLayerAt(layer.getNativePtr(), index); + } + + /** + * Adds the source to the map. The source must be newly created and not added to the map before + * + * @param source the source to add + */ + private void addSource(Source source) { + nativeAddSource(source, source.getNativePtr()); + } + + /** + * Adds an image to be used in the snapshotter's style + * + * @param name the name of the image + * @param bitmap the pre-multiplied Bitmap + * @param sdf the flag indicating image is an SDF or template image + */ + private void addImage(@NonNull final String name, @NonNull Bitmap bitmap, boolean sdf) { + nativeAddImages(new Image[] {toImage(new Style.Builder.ImageWrapper(name, bitmap, sdf))}); + } + /** * Must be called in on the thread * the object was created on. @@ -609,6 +698,54 @@ protected void onSnapshotFailed(String reason) { } } + /** + * Called by JNI peer when snapshot style is ready. + */ + @Keep + protected void onDidFailLoadingStyle(String reason) { + onSnapshotFailed(reason); + } + + /** + * Called by JNI peer when snapshot style is loaded. + */ + @Keep + protected void onDidFinishLoadingStyle() { + if (!fullyLoaded) { + fullyLoaded = true; + Style.Builder builder = options.getBuilder(); + if (builder != null) { + for (Source source : builder.getSources()) { + nativeAddSource(source, source.getNativePtr()); + } + + for (Style.Builder.LayerWrapper layerWrapper : builder.getLayers()) { + if (layerWrapper instanceof Style.Builder.LayerAtWrapper) { + addLayerAt(layerWrapper.getLayer(), ((Style.Builder.LayerAtWrapper) layerWrapper).getIndex()); + } else if (layerWrapper instanceof Style.Builder.LayerAboveWrapper) { + addLayerAbove(layerWrapper.getLayer(), ((Style.Builder.LayerAboveWrapper) layerWrapper).getAboveLayer()); + } else if (layerWrapper instanceof Style.Builder.LayerBelowWrapper) { + addLayerBelow(layerWrapper.getLayer(), ((Style.Builder.LayerBelowWrapper) layerWrapper).getBelowLayer()); + } else { + addLayerBelow(layerWrapper.getLayer(), MapboxConstants.LAYER_ID_ANNOTATIONS); + } + } + + for (Style.Builder.ImageWrapper image : builder.getImages()) { + addImage(image.getId(), image.getBitmap(), image.isSdf()); + } + } + } + } + + /** + * Called by JNI peer when snapshot style image is missing. + */ + @Keep + protected void onStyleImageMissing(String imageName) { + onSnapshotFailed("style image is missing: " + imageName); + } + private void checkThread() { ThreadUtils.checkThread(TAG); } @@ -631,6 +768,21 @@ protected native void nativeInitialize(MapSnapshotter mapSnapshotter, @Keep protected native void nativeCancel(); + @Keep + private native void nativeAddLayerBelow(long layerPtr, String below); + + @Keep + private native void nativeAddLayerAbove(long layerPtr, String above); + + @Keep + private native void nativeAddLayerAt(long layerPtr, int index); + + @Keep + private native void nativeAddSource(Source source, long sourcePtr); + + @Keep + private native void nativeAddImages(Image[] images); + @Override @Keep protected native void finalize() throws Throwable; @@ -640,7 +792,7 @@ private class Logo { private Bitmap small; private float scale; - public Logo(Bitmap large, Bitmap small, float scale) { + Logo(Bitmap large, Bitmap small, float scale) { this.large = large; this.small = small; this.scale = scale; diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java index 0dd0df0b2..1e5669f59 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/CustomLayer.java @@ -15,20 +15,23 @@ public CustomLayer(String id, initialize(id, host); } + /** + * Triggers map re-paint. + * + * @deprecated Use {@link MapboxMap#triggerRepaint()} instead. + */ + @Deprecated @Keep - CustomLayer(long nativePtr) { - super(nativePtr); - } - public void update() { - nativeUpdate(); } @Keep - protected native void initialize(String id, long host); + CustomLayer(long nativePtr) { + super(nativePtr); + } @Keep - protected native void nativeUpdate(); + protected native void initialize(String id, long host); @Override @Keep diff --git a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotterTest.kt b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotterTest.kt index 167656610..b87cc4b6f 100644 --- a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotterTest.kt +++ b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotterTest.kt @@ -5,13 +5,14 @@ import androidx.test.rule.ActivityTestRule import com.mapbox.mapboxsdk.camera.CameraPosition import com.mapbox.mapboxsdk.geometry.LatLng import com.mapbox.mapboxsdk.maps.Style +import com.mapbox.mapboxsdk.style.layers.BackgroundLayer +import com.mapbox.mapboxsdk.style.layers.PropertyFactory import com.mapbox.mapboxsdk.testapp.activity.FeatureOverviewActivity import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import java.util.concurrent.TimeoutException import junit.framework.Assert.assertNotNull import org.junit.Assert -import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -20,7 +21,6 @@ import org.junit.runner.RunWith * Integration test that validates if a snapshotter creation */ @RunWith(AndroidJUnit4::class) -@Ignore("Ignored until https://github.com/mapbox/mapbox-gl-native/issues/11669 is resolved.") class MapSnapshotterTest { @Rule @@ -31,18 +31,22 @@ class MapSnapshotterTest { @Test fun mapSnapshotter() { + var mapSnapshotter: MapSnapshotter? rule.activity.runOnUiThread { + val bg = BackgroundLayer("rand_tint") + bg.setProperties(PropertyFactory.backgroundColor("rgba(255,128,0,0.7)")) val options = MapSnapshotter.Options(512, 512) .withPixelRatio(1.0f) - .withStyle(Style.SATELLITE_STREETS) + .withStyleBuilder(Style.Builder().fromUri(Style.SATELLITE_STREETS) + .withLayerAbove(bg, "country-label")) .withCameraPosition( CameraPosition.Builder() .zoom(12.0) .target(LatLng(51.145495, 5.742234)) .build() ) - val mapSnapshotter = MapSnapshotter(rule.activity, options) - mapSnapshotter.start({ + mapSnapshotter = MapSnapshotter(rule.activity, options) + mapSnapshotter!!.start({ assertNotNull(it) assertNotNull(it.bitmap) countDownLatch.countDown() diff --git a/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index ee239a6d5..7e2f59d3e 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -363,9 +363,21 @@ android:value=".activity.FeatureOverviewActivity" /> + android:name=".activity.snapshot.MapSnapshotterBitMapOverlayActivity" + android:description="@string/description_map_snapshotter_bitmap_overlay" + android:label="@string/activity_map_snapshotter_bitmap_overlay"> + + + + diff --git a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java index 514d362c2..92ee75c20 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java +++ b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/customlayer/CustomLayerActivity.java @@ -68,8 +68,8 @@ private void swapCustomLayer() { } private void updateLayer() { - if (customLayer != null) { - customLayer.update(); + if (mapboxMap != null) { + mapboxMap.triggerRepaint(); } } diff --git a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java index 3cff4cad5..fbd8251be 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java +++ b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/render/RenderTestDefinition.java @@ -1,5 +1,6 @@ package com.mapbox.mapboxsdk.testapp.activity.render; +import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; public class RenderTestDefinition { @@ -75,7 +76,7 @@ public RenderTestStyleDefinition.Test getTest() { public MapSnapshotter.Options toOptions() { return new MapSnapshotter .Options(getWidth(), getHeight()) - .withStyleJson(styleJson) + .withStyleBuilder(new Style.Builder().fromJson(styleJson)) .withPixelRatio(getPixelRatio()) .withLogo(false); } diff --git a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java index c15cf4f25..c01254862 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java +++ b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterActivity.java @@ -1,31 +1,64 @@ package com.mapbox.mapboxsdk.testapp.activity.snapshot; +import android.graphics.Bitmap; +import android.graphics.Color; import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; import android.view.ViewTreeObserver; import android.widget.GridLayout; import android.widget.ImageView; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.mapbox.geojson.Feature; +import com.mapbox.geojson.FeatureCollection; +import com.mapbox.geojson.Point; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.constants.MapboxConstants; -import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import com.mapbox.mapboxsdk.style.layers.Property; +import com.mapbox.mapboxsdk.style.layers.RasterLayer; +import com.mapbox.mapboxsdk.style.layers.SymbolLayer; +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.testapp.R; +import com.mapbox.mapboxsdk.utils.BitmapUtils; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Random; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; import timber.log.Timber; +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.switchCase; +import static com.mapbox.mapboxsdk.style.expressions.Expression.toBool; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAllowOverlap; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconAnchor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconIgnorePlacement; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconImage; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.iconSize; + /** * Test activity showing how to use a the {@link com.mapbox.mapboxsdk.snapshotter.MapSnapshotter} */ public class MapSnapshotterActivity extends AppCompatActivity { - - private GridLayout grid; + private static final String ID_FEATURE_PROPERTY = "id"; + private static final String SELECTED_FEATURE_PROPERTY = "selected"; + private static final String TITLE_FEATURE_PROPERTY = "title"; + // layer & source constants + private static final String MARKER_SOURCE = "marker-source"; + private static final String MARKER_LAYER = "marker-layer"; + + public GridLayout grid; private List snapshotters = new ArrayList<>(); @Override @@ -58,6 +91,9 @@ private void addSnapshots() { } private void startSnapShot(final int row, final int column) { + // Optionally the style + Style.Builder builder = new Style.Builder() + .fromUri((column + row) % 2 == 0 ? Style.MAPBOX_STREETS : Style.DARK); // Define the dimensions MapSnapshotter.Options options = new MapSnapshotter.Options( @@ -66,9 +102,6 @@ private void startSnapShot(final int row, final int column) { ) // Optionally the pixel ratio .withPixelRatio(1) - - // Optionally the style - .withStyle((column + row) % 2 == 0 ? Style.MAPBOX_STREETS : Style.DARK) .withLocalIdeographFontFamily(MapboxConstants.DEFAULT_FONT); // Optionally the visible region @@ -88,14 +121,52 @@ private void startSnapShot(final int row, final int column) { : new LatLng(randomInRange(-80, 80), randomInRange(-160, 160))) .bearing(randomInRange(0, 360)) .tilt(randomInRange(0, 60)) - .zoom(randomInRange(0, 20)) + .zoom(randomInRange(0, 10)) + .padding(1, 1, 1, 1) + .build() + ); + } + if (row == 0 && column == 0) { + // Add a source + Source source = new RasterSource("my-raster-source", "mapbox://mapbox.satellite", 512); + builder.withLayerAbove(new RasterLayer("satellite-layer", "my-raster-source"), "country-label"); + builder.withSource(source); + } else if (row == 0 && column == 2) { + + Bitmap carBitmap = BitmapUtils.getBitmapFromDrawable( + getResources().getDrawable(R.drawable.ic_directions_car_black)); + + // marker source + FeatureCollection markerCollection = FeatureCollection.fromFeatures(new Feature[] { + Feature.fromGeometry(Point.fromLngLat(4.91638, 52.34673), featureProperties("2", "Car")) + }); + Source markerSource = new GeoJsonSource(MARKER_SOURCE, markerCollection); + + // marker layer + SymbolLayer markerSymbolLayer = new SymbolLayer(MARKER_LAYER, MARKER_SOURCE) + .withProperties( + iconImage(get(TITLE_FEATURE_PROPERTY)), + iconIgnorePlacement(true), + iconAllowOverlap(true), + iconSize(switchCase(toBool(get(SELECTED_FEATURE_PROPERTY)), literal(1.5f), literal(1.0f))), + iconAnchor(Property.ICON_ANCHOR_BOTTOM), + iconColor(Color.BLUE) + ); + + builder.withImage("Car", Objects.requireNonNull(carBitmap), false) + .withSources(markerSource) + .withLayers(markerSymbolLayer); + options.withCameraPosition(new CameraPosition.Builder() + .target(new LatLng(5.537109374999999, + 52.07950600379697)) + .zoom(1) .padding(1, 1, 1, 1) .build() ); } + options.withStyleBuilder(builder); MapSnapshotter snapshotter = new MapSnapshotter(MapSnapshotterActivity.this, options); - snapshotter.start(snapshot -> { Timber.i("Got the snapshot"); ImageView imageView = new ImageView(MapSnapshotterActivity.this); @@ -125,4 +196,11 @@ public static float randomInRange(float min, float max) { return (random.nextFloat() * (max - min)) + min; } + private JsonObject featureProperties(@NonNull String id, @NonNull String title) { + JsonObject object = new JsonObject(); + object.add(ID_FEATURE_PROPERTY, new JsonPrimitive(id)); + object.add(TITLE_FEATURE_PROPERTY, new JsonPrimitive(title)); + object.add(SELECTED_FEATURE_PROPERTY, new JsonPrimitive(false)); + return object; + } } diff --git a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterBitMapOverlayActivity.java similarity index 92% rename from MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java rename to MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterBitMapOverlayActivity.java index 5190bce90..d2ab581a9 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterMarkerActivity.java +++ b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterBitMapOverlayActivity.java @@ -6,26 +6,29 @@ import android.graphics.Canvas; import android.graphics.PointF; import android.os.Bundle; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.AppCompatActivity; import android.view.MotionEvent; import android.view.View; import android.view.ViewTreeObserver; import android.widget.ImageView; + import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; import com.mapbox.mapboxsdk.testapp.R; + +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AppCompatActivity; import timber.log.Timber; /** * Test activity showing how to use a the {@link MapSnapshotter} and overlay * {@link android.graphics.Bitmap}s on top. */ -public class MapSnapshotterMarkerActivity extends AppCompatActivity implements MapSnapshotter.SnapshotReadyCallback { +public class MapSnapshotterBitMapOverlayActivity extends AppCompatActivity + implements MapSnapshotter.SnapshotReadyCallback { private MapSnapshotter mapSnapshotter; private MapSnapshot mapSnapshot; @@ -49,10 +52,10 @@ public void onGlobalLayout() { getApplicationContext(), new MapSnapshotter .Options(Math.min(container.getMeasuredWidth(), 1024), Math.min(container.getMeasuredHeight(), 1024)) - .withStyle(Style.OUTDOORS) + .withStyleBuilder(new Style.Builder().fromUri(Style.OUTDOORS)) .withCameraPosition(new CameraPosition.Builder().target(new LatLng(52.090737, 5.121420)).zoom(15).build()) ); - mapSnapshotter.start(MapSnapshotterMarkerActivity.this); + mapSnapshotter.start(MapSnapshotterBitMapOverlayActivity.this); } }); } diff --git a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.java b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.java new file mode 100644 index 000000000..77e698b02 --- /dev/null +++ b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.java @@ -0,0 +1,174 @@ +package com.mapbox.mapboxsdk.testapp.activity.snapshot; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.ImageView; + +import com.mapbox.mapboxsdk.camera.CameraPosition; +import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.Style; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; +import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; +import com.mapbox.mapboxsdk.style.layers.HeatmapLayer; +import com.mapbox.mapboxsdk.style.sources.GeoJsonSource; +import com.mapbox.mapboxsdk.style.sources.Source; +import com.mapbox.mapboxsdk.testapp.R; + +import org.jetbrains.annotations.NotNull; + +import java.net.URI; +import java.net.URISyntaxException; + +import androidx.appcompat.app.AppCompatActivity; +import timber.log.Timber; + +import static com.mapbox.mapboxsdk.style.expressions.Expression.get; +import static com.mapbox.mapboxsdk.style.expressions.Expression.heatmapDensity; +import static com.mapbox.mapboxsdk.style.expressions.Expression.interpolate; +import static com.mapbox.mapboxsdk.style.expressions.Expression.linear; +import static com.mapbox.mapboxsdk.style.expressions.Expression.literal; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgb; +import static com.mapbox.mapboxsdk.style.expressions.Expression.rgba; +import static com.mapbox.mapboxsdk.style.expressions.Expression.stop; +import static com.mapbox.mapboxsdk.style.expressions.Expression.zoom; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapColor; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapIntensity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapOpacity; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapRadius; +import static com.mapbox.mapboxsdk.style.layers.PropertyFactory.heatmapWeight; + +/** + * Test activity showing how to use a the {@link MapSnapshotter} and heatmap layer on it. + */ +public class MapSnapshotterHeatMapActivity extends AppCompatActivity implements MapSnapshotter.SnapshotReadyCallback { + private static final String EARTHQUAKE_SOURCE_URL = "https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"; + private static final String EARTHQUAKE_SOURCE_ID = "earthquakes"; + private static final String HEATMAP_LAYER_ID = "earthquakes-heat"; + private static final String HEATMAP_LAYER_SOURCE = "earthquakes"; + private MapSnapshotter mapSnapshotter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_map_snapshotter_marker); + + final View container = findViewById(R.id.container); + container.getViewTreeObserver() + .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + //noinspection deprecation + container.getViewTreeObserver().removeGlobalOnLayoutListener(this); + + Timber.i("Starting snapshot"); + + Style.Builder builder = new Style.Builder().fromUri(Style.OUTDOORS) + .withSource(getEarthquakeSource()) + .withLayerAbove(getHeatmapLayer(), "waterway-label"); + + mapSnapshotter = new MapSnapshotter( + getApplicationContext(), + new MapSnapshotter + .Options(container.getMeasuredWidth(), container.getMeasuredHeight()) + .withStyleBuilder(builder) + .withCameraPosition(new CameraPosition.Builder() + .target(new LatLng(15, -94)) + .zoom(5) + .padding(1, 1, 1, 1) + .build() + ) + ); + mapSnapshotter.start(MapSnapshotterHeatMapActivity.this); + } + }); + } + + @NotNull + private HeatmapLayer getHeatmapLayer() { + HeatmapLayer layer = new HeatmapLayer(HEATMAP_LAYER_ID, EARTHQUAKE_SOURCE_ID); + layer.setMaxZoom(9); + layer.setSourceLayer(HEATMAP_LAYER_SOURCE); + layer.setProperties( + + // Color ramp for heatmap. Domain is 0 (low) to 1 (high). + // Begin color ramp at 0-stop with a 0-transparency color + // to create a blur-like effect. + heatmapColor( + interpolate( + linear(), heatmapDensity(), + literal(0), rgba(33, 102, 172, 0), + literal(0.2), rgb(103, 169, 207), + literal(0.4), rgb(209, 229, 240), + literal(0.6), rgb(253, 219, 199), + literal(0.8), rgb(239, 138, 98), + literal(1), rgb(178, 24, 43) + ) + ), + + // Increase the heatmap weight based on frequency and property magnitude + heatmapWeight( + interpolate( + linear(), get("mag"), + stop(0, 0), + stop(6, 1) + ) + ), + + // Increase the heatmap color weight weight by zoom level + // heatmap-intensity is a multiplier on top of heatmap-weight + heatmapIntensity( + interpolate( + linear(), zoom(), + stop(0, 1), + stop(9, 3) + ) + ), + + // Adjust the heatmap radius by zoom level + heatmapRadius( + interpolate( + linear(), zoom(), + stop(0, 2), + stop(9, 20) + ) + ), + + // Transition from heatmap to circle layer by zoom level + heatmapOpacity( + interpolate( + linear(), zoom(), + stop(7, 1), + stop(9, 0) + ) + ) + ); + return layer; + } + + @Override + protected void onStop() { + super.onStop(); + mapSnapshotter.cancel(); + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public void onSnapshotReady(MapSnapshot snapshot) { + Timber.i("Snapshot ready"); + ImageView imageView = findViewById(R.id.snapshot_image); + imageView.setImageBitmap(snapshot.getBitmap()); + } + + private Source getEarthquakeSource() { + Source source = null; + try { + source = new GeoJsonSource(EARTHQUAKE_SOURCE_ID, new URI(EARTHQUAKE_SOURCE_URL)); + } catch (URISyntaxException uriSyntaxException) { + Timber.e(uriSyntaxException, "That's not an url... "); + } + return source; + } + +} diff --git a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.java b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.java index 1dda9f655..3b8c9ae72 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.java +++ b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.java @@ -7,6 +7,7 @@ import android.widget.ImageView; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.geometry.LatLng; +import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; import com.mapbox.mapboxsdk.testapp.R; @@ -48,7 +49,7 @@ public void onGlobalLayout() { getApplicationContext(), new MapSnapshotter .Options(Math.min(container.getMeasuredWidth(), 1024), Math.min(container.getMeasuredHeight(), 1024)) - .withStyleJson(styleJson) + .withStyleBuilder(new Style.Builder().fromJson(styleJson)) .withCameraPosition(new CameraPosition.Builder().target(new LatLng(52.090737, 5.121420)).zoom(18).build()) ); mapSnapshotter.start(MapSnapshotterLocalStyleActivity.this, error -> Timber.e(error)); diff --git a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java index 6b54ff9d5..198727277 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java +++ b/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/snapshot/MapSnapshotterReuseActivity.java @@ -1,20 +1,21 @@ package com.mapbox.mapboxsdk.testapp.activity.snapshot; import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; import com.mapbox.mapboxsdk.camera.CameraPosition; -import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.geometry.LatLngBounds; +import com.mapbox.mapboxsdk.maps.Style; import com.mapbox.mapboxsdk.snapshotter.MapSnapshot; import com.mapbox.mapboxsdk.snapshotter.MapSnapshotter; import com.mapbox.mapboxsdk.testapp.R; import java.util.Random; +import androidx.appcompat.app.AppCompatActivity; + /** * Test activity showing how to use a the {@link MapSnapshotter} */ @@ -49,7 +50,7 @@ protected void onCreate(Bundle savedInstanceState) { mapSnapshotter = new MapSnapshotter( getApplicationContext(), - new MapSnapshotter.Options(512, 512) + new MapSnapshotter.Options(512, 512).withStyleBuilder(new Style.Builder().fromUri(getRandomStyle())) ); mapSnapshotter.start(MapSnapshotterReuseActivity.this); diff --git a/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index 075949be5..cc6a5ab80 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -60,7 +60,8 @@ Show 2 MapView on screen with a bottom sheet Show a static bitmap taken with the MapSnapshotter Show how to reuse a MapSnapshotter instance - Show how to add a marker to a Snapshot + Show how to add a bitmap overlay to a Snapshot + Show how to add a heatmap layer to a Snapshot Show how to load a local style with a Snapshot Use Android SDK Animators to animate camera position changes Use Android SDK Views as symbols diff --git a/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 0a0166945..75647878f 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -59,7 +59,8 @@ Bottom sheet Map Snapshotter Map Snapshotter Reuse - Map Snapshot with marker + Map Snapshot with bitmap overlay + Map Snapshot with heatmap layer Map Snapshot with local style Animator animation SymbolGenerator diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native index a601f9474..3f45b0a24 160000 --- a/vendor/mapbox-gl-native +++ b/vendor/mapbox-gl-native @@ -1 +1 @@ -Subproject commit a601f94743eb812847fbb58944df7240b088f7e2 +Subproject commit 3f45b0a24b5b608dfa75c84308d1ab0deb6159ee From 0ad363c834165808ef5e4dfe05c8d37f55e7a1b0 Mon Sep 17 00:00:00 2001 From: Alexander Shalamov Date: Wed, 25 Mar 2020 07:42:23 -0400 Subject: [PATCH 2/5] Add getLayer and getSource snapshotter methods (#292) * Expose snapshotter layer and source objects * Add example snapshotter + within activity * Bump vendor/mapbox-gl-native (cherry picked from commit 6984dbf155c6a1f26388481a2d8ab0979978dfa6) --- .../mapboxsdk/snapshotter/MapSnapshotter.java | 62 ++++ .../src/main/AndroidManifest.xml | 11 + .../turf/MapSnapshotterWithinExpression.kt | 274 ++++++++++++++++++ ...ivity_mapsnapshotter_within_expression.xml | 30 ++ .../src/main/res/values/descriptions.xml | 1 + .../src/main/res/values/titles.xml | 1 + vendor/mapbox-gl-native | 2 +- 7 files changed, 380 insertions(+), 1 deletion(-) create mode 100644 MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/turf/MapSnapshotterWithinExpression.kt create mode 100644 MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapsnapshotter_within_expression.xml diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java index 26af5157a..dcbcb22b9 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java @@ -64,6 +64,8 @@ public class MapSnapshotter { private SnapshotReadyCallback callback; @Nullable private ErrorHandler errorHandler; + @Nullable + private Observer observer; /** * Get notified on snapshot completion. @@ -97,6 +99,20 @@ public interface ErrorHandler { void onError(String error); } + /** + * Can be used to get notified on snapshotter style loading + * completion. + * + * @see MapSnapshotter#setObserver(Observer) + */ + public interface Observer { + + /** + * Called when snapshotter finishes loading it's style. + */ + void onDidFinishLoadingStyle(); + } + /** * MapSnapshotter options */ @@ -511,6 +527,16 @@ public void cancel() { nativeCancel(); } + /** + * Sets observer for a snapshotter + * + * @param observer an Observer object + */ + public void setObserver(@Nullable Observer observer) { + checkThread(); + this.observer = observer; + } + /** * Draw an overlay on the map snapshot. * @@ -736,6 +762,34 @@ protected void onDidFinishLoadingStyle() { } } } + + if (observer != null) { + observer.onDidFinishLoadingStyle(); + } + } + + /** + * Returns Layer of a style that is used by a snapshotter + * + * @param layerId the id of a Layer + * @return the Layer object if Layer with layerId exists, null otherwise + */ + @Nullable + public Layer getLayer(@NonNull String layerId) { + checkThread(); + return fullyLoaded ? nativeGetLayer(layerId) : null; + } + + /** + * Returns Source of a style that is used by a snapshotter + * + * @param sourceId the id of a Source + * @return the Source object if a Source with sourceId exists, null otherwise + */ + @Nullable + public Source getSource(@NonNull String sourceId) { + checkThread(); + return fullyLoaded ? nativeGetSource(sourceId) : null; } /** @@ -783,6 +837,14 @@ protected native void nativeInitialize(MapSnapshotter mapSnapshotter, @Keep private native void nativeAddImages(Image[] images); + @NonNull + @Keep + private native Layer nativeGetLayer(String layerId); + + @NonNull + @Keep + private native Source nativeGetSource(String sourceId); + @Override @Keep protected native void finalize() throws Throwable; diff --git a/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml index 7e2f59d3e..97f93f9bb 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml +++ b/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml @@ -396,6 +396,17 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".activity.FeatureOverviewActivity" /> + + + + + mapboxMap = map + + // Setup camera position above Georgetown + mapboxMap.cameraPosition = CameraPosition.Builder() + .target(LatLng(38.90628988399711, -77.06574689337494)) + .zoom(15.5) + .build() + + // Wait for the map to become idle before manipulating the style and camera of the map + mapView.addOnDidBecomeIdleListener(object : MapView.OnDidBecomeIdleListener { + override fun onDidBecomeIdle() { + mapboxMap.easeCamera( + CameraUpdateFactory.newCameraPosition( + CameraPosition.Builder() + .zoom(16.0) + .target(LatLng(38.905156245642814, -77.06535338052844)) + .bearing(80.68015859462369) + .tilt(55.0) + .build() + ), 1000 + ) + mapView.removeOnDidBecomeIdleListener(this) + } + }) + // Load mapbox streets and add lines and circles + setupStyle() + } + } + + private fun setupStyle() { + // Assume the route is represented by an array of coordinates. + val coordinates = listOf( + Point.fromLngLat( + -77.06866264343262, + 38.90506061276737 + ), + Point.fromLngLat( + -77.06283688545227, + 38.905194197410545 + ), + Point.fromLngLat( + -77.06285834312439, + 38.906429843444094 + ), + Point.fromLngLat( + -77.0630407333374, + 38.90680554236621 + ) + ) + + // Setup style with additional layers, + // using Style.MAPBOX_STREETS as a base style + mapboxMap.setStyle( + Style.Builder() + .fromUri(Style.MAPBOX_STREETS) + ) { + mapView.addOnCameraDidChangeListener(cameraListener) + } + + val options = MapSnapshotter.Options(imageView.measuredWidth / 2, imageView.measuredHeight / 2) + .withCameraPosition(mapboxMap.cameraPosition) + .withPixelRatio(2.0f) + .withStyleBuilder(Style.Builder() + .fromUri(Style.MAPBOX_STREETS) + .withSources( + GeoJsonSource( + POINT_ID, LineString.fromLngLats(coordinates) + ), + GeoJsonSource( + FILL_ID, + FeatureCollection.fromFeature(Feature.fromGeometry(bufferLineStringGeometry())), + GeoJsonOptions().withBuffer(0).withTolerance(0.0f) + ) + ) + .withLayerBelow( + LineLayer(LINE_ID, POINT_ID) + .withProperties(lineWidth(7.5f), lineColor(Color.LTGRAY)), + "poi-label" + ) + .withLayerBelow( + CircleLayer(POINT_ID, POINT_ID) + .withProperties( + circleRadius(7.5f), + circleColor(Color.DKGRAY), + circleOpacity(0.75f) + ), "poi-label" + ).withLayerBelow( + FillLayer(FILL_ID, FILL_ID) + .withProperties( + fillOpacity(0.12f), + fillColor(Color.YELLOW) + ), LINE_ID + )) + snapshotter = MapSnapshotter(this, options) + snapshotter.setObserver(snapshotterObserver) + } + + override fun onStart() { + super.onStart() + mapView.onStart() + } + + override fun onResume() { + super.onResume() + mapView.onResume() + } + + override fun onPause() { + super.onPause() + mapView.onPause() + } + + override fun onStop() { + super.onStop() + mapView.onStop() + } + + override fun onLowMemory() { + super.onLowMemory() + mapView.onLowMemory() + } + + override fun onDestroy() { + super.onDestroy() + mapView.onDestroy() + } + + override fun onSaveInstanceState(outState: Bundle?, outPersistentState: PersistableBundle?) { + super.onSaveInstanceState(outState, outPersistentState) + outState?.let { + mapView.onSaveInstanceState(it) + } + } + + private fun bufferLineStringGeometry(): Polygon { + // TODO replace static data by Turf#Buffer: mapbox-java/issues/987 + return FeatureCollection.fromJson( + """ + { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -77.06867337226866, + 38.90467655551809 + ], + [ + -77.06233263015747, + 38.90479344272695 + ], + [ + -77.06234335899353, + 38.906463238984344 + ], + [ + -77.06290125846863, + 38.907206285691615 + ], + [ + -77.06364154815674, + 38.90684728656818 + ], + [ + -77.06326603889465, + 38.90637140121084 + ], + [ + -77.06321239471436, + 38.905561553883246 + ], + [ + -77.0691454410553, + 38.905436318935635 + ], + [ + -77.06912398338318, + 38.90466820642439 + ], + [ + -77.06867337226866, + 38.90467655551809 + ] + ] + ] + } + } + ] + } + """.trimIndent() + ).features()!![0].geometry() as Polygon + } + + companion object { + const val POINT_ID = "point" + const val FILL_ID = "fill" + const val LINE_ID = "line" + } +} \ No newline at end of file diff --git a/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapsnapshotter_within_expression.xml b/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapsnapshotter_within_expression.xml new file mode 100644 index 000000000..121af0e14 --- /dev/null +++ b/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_mapsnapshotter_within_expression.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml b/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml index cc6a5ab80..2ff1bf157 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml +++ b/MapboxGLAndroidSDKTestApp/src/main/res/values/descriptions.xml @@ -27,6 +27,7 @@ Add a polygon to a map Scroll with pixels in x,y direction Example to make a snapshot of the map + Example of snapshotter with within expression 2 maps in a view hierarchy Learn how to create a dynamic custom InfoWindow Use SupportMapFragments in a ViewPager diff --git a/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml b/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml index 75647878f..00a416b04 100644 --- a/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml +++ b/MapboxGLAndroidSDKTestApp/src/main/res/values/titles.xml @@ -21,6 +21,7 @@ Scroll By Method Double Map Activity Snapshot Activity + Snapshot with within expression Custom Layer Map Padding Debug Mode diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native index 3f45b0a24..fd1f8703c 160000 --- a/vendor/mapbox-gl-native +++ b/vendor/mapbox-gl-native @@ -1 +1 @@ -Subproject commit 3f45b0a24b5b608dfa75c84308d1ab0deb6159ee +Subproject commit fd1f8703cdd73420f23cb8617ffb6f5c42f8a32d From adceb7351921cf67d045e36ec09b55f71566fe0e Mon Sep 17 00:00:00 2001 From: Tobrun Date: Wed, 25 Mar 2020 14:58:59 +0100 Subject: [PATCH 3/5] [glyph] rework local glyph generation to use RGB values vs alpha (#289) (cherry picked from commit 4b0cd4025f88423ddc615314ab947b29b6db5dea) --- .../java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java | 3 +-- vendor/mapbox-gl-native | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java index d698fb181..7d83884cc 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/text/LocalGlyphRasterizer.java @@ -4,7 +4,6 @@ import android.graphics.Color; import android.graphics.Paint; import android.graphics.Bitmap; -import android.graphics.PorterDuff; import android.graphics.Typeface; import androidx.annotation.Keep; import androidx.annotation.NonNull; @@ -52,7 +51,7 @@ of the bitmap (y: 20) with some buffer around the edge @WorkerThread protected Bitmap drawGlyphBitmap(String fontFamily, boolean bold, char glyphID) { paint.setTypeface(Typeface.create(fontFamily, bold ? Typeface.BOLD : Typeface.NORMAL)); - canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); + canvas.drawColor(Color.WHITE); canvas.drawText(String.valueOf(glyphID), 0, 20, paint); return bitmap; } diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native index fd1f8703c..d4db3b112 160000 --- a/vendor/mapbox-gl-native +++ b/vendor/mapbox-gl-native @@ -1 +1 @@ -Subproject commit fd1f8703cdd73420f23cb8617ffb6f5c42f8a32d +Subproject commit d4db3b11267d9ac22fc53991a383f95f95e607ea From a05b44a62dc9372221d7372d1e7f35b1c9ad77f4 Mon Sep 17 00:00:00 2001 From: Kevin Li Date: Thu, 26 Mar 2020 16:24:55 +0800 Subject: [PATCH 4/5] bump telemetry to 5.0.0, core to 2.0.0 (#293) (cherry picked from commit fbea14a8bc7253f448c3346e7019d4a9f0e54779) --- gradle/dependencies.gradle | 4 ++-- vendor/mapbox-events-android | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index daca6ecbc..f82887222 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -8,8 +8,8 @@ ext { versions = [ mapboxServices : '5.0.0', - mapboxTelemetry : '4.7.3', - mapboxCore : '1.4.1', + mapboxTelemetry : '5.0.0', + mapboxCore : '2.0.0', mapboxGestures : '0.6.0', mapboxAccounts : '0.7.0', appCompat : '1.0.0', diff --git a/vendor/mapbox-events-android b/vendor/mapbox-events-android index 7945e61bc..0b46d29ef 160000 --- a/vendor/mapbox-events-android +++ b/vendor/mapbox-events-android @@ -1 +1 @@ -Subproject commit 7945e61bccc1df3b04ca2e80cb25459dfeea01dd +Subproject commit 0b46d29efd23d870e1f084f0591091a16169c800 From fff28b0a2ac9c09325cce58a2356c43c301d9d3d Mon Sep 17 00:00:00 2001 From: Kevin Li Date: Fri, 27 Mar 2020 12:54:53 +0800 Subject: [PATCH 5/5] Bump gl-native to 1.5.0 (#296) (cherry picked from commit 585253ed0dbd321e02dda7d27c91c2036c17f9a8) --- vendor/mapbox-gl-native | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native index d4db3b112..77466d23d 160000 --- a/vendor/mapbox-gl-native +++ b/vendor/mapbox-gl-native @@ -1 +1 @@ -Subproject commit d4db3b11267d9ac22fc53991a383f95f95e607ea +Subproject commit 77466d23d6b2f524c104373d546ac458035b0ec0