diff --git a/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXSnapshotModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXSnapshotModule.kt
index 415763509..771884c6d 100644
--- a/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXSnapshotModule.kt
+++ b/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXSnapshotModule.kt
@@ -8,10 +8,13 @@ import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.module.annotations.ReactModule
import com.mapbox.geojson.Feature
+import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.Point
import com.mapbox.maps.CameraOptions
+import com.mapbox.maps.EdgeInsets
import com.mapbox.maps.MapSnapshotOptions
import com.mapbox.maps.Size
+import com.mapbox.maps.SnapshotOverlayOptions
import com.mapbox.maps.Snapshotter
import com.rnmapbox.rnmbx.modules.RNMBXModule.Companion.getAccessToken
import com.rnmapbox.rnmbx.modules.RNMBXSnapshotModule
@@ -43,30 +46,38 @@ class RNMBXSnapshotModule(private val mContext: ReactApplicationContext) :
// FileSource.getInstance(mContext).activate();
mContext.runOnUiQueueThread {
val snapshotterID = UUID.randomUUID().toString()
- val snapshotter = Snapshotter(mContext, getOptions(jsOptions))
+ val showLogo = if (jsOptions.hasKey("withLogo")) jsOptions.getBoolean("withLogo") else true
+ val overlayOptions = SnapshotOverlayOptions(showLogo = showLogo)
+ val snapshotter = Snapshotter(mContext, getOptions(jsOptions), overlayOptions)
snapshotter.setStyleUri(jsOptions.getString("styleURL")!!)
- snapshotter.setCamera(getCameraOptions(jsOptions))
+ try {
+ snapshotter.setCamera(getCameraOptions(jsOptions, snapshotter))
+ } catch (e: IllegalArgumentException) {
+ promise.reject(REACT_CLASS, e.message, e)
+ return@runOnUiQueueThread
+ }
mSnapshotterMap[snapshotterID] = snapshotter
- snapshotter.startV11 { image,error ->
+
+ snapshotter.start(null) { image, error ->
try {
if (image == null) {
Log.w(REACT_CLASS, "Snapshot failed: $error")
promise.reject(REACT_CLASS, "Snapshot failed: $error")
mSnapshotterMap.remove(snapshotterID)
} else {
- val image = image.toMapboxImage()
+ val mapboxImage = image.toMapboxImage()
var result: String? = null
result = if (jsOptions.getBoolean("writeToDisk")) {
- BitmapUtils.createImgTempFile(mContext, image)
+ BitmapUtils.createImgTempFile(mContext, mapboxImage)
} else {
- BitmapUtils.createImgBase64(image)
+ BitmapUtils.createImgBase64(mapboxImage)
}
if (result == null) {
promise.reject(
REACT_CLASS,
"Could not generate snapshot, please check Android logs for more info."
)
- return@startV11
+ return@start
}
promise.resolve(result)
mSnapshotterMap.remove(snapshotterID)
@@ -79,17 +90,44 @@ class RNMBXSnapshotModule(private val mContext: ReactApplicationContext) :
}
}
- private fun getCameraOptions(jsOptions: ReadableMap): CameraOptions {
- val centerPoint =
- Feature.fromJson(jsOptions.getString("centerCoordinate")!!)
- val point = centerPoint.geometry() as Point?
- val cameraOptionsBuilder = CameraOptions.Builder()
- return cameraOptionsBuilder
- .center(point)
- .pitch(jsOptions.getDouble("pitch"))
- .bearing(jsOptions.getDouble("heading"))
- .zoom(jsOptions.getDouble("zoomLevel"))
- .build()
+ private fun getCameraOptions(jsOptions: ReadableMap, snapshotter: Snapshotter): CameraOptions {
+ val pitch = jsOptions.getDouble("pitch")
+ val heading = jsOptions.getDouble("heading")
+ val zoomLevel = jsOptions.getDouble("zoomLevel")
+
+ // Check if centerCoordinate is provided
+ if (jsOptions.hasKey("centerCoordinate") && !jsOptions.isNull("centerCoordinate")) {
+ val centerPoint = Feature.fromJson(jsOptions.getString("centerCoordinate")!!)
+ val point = centerPoint.geometry() as Point?
+ return CameraOptions.Builder()
+ .center(point)
+ .pitch(pitch)
+ .bearing(heading)
+ .zoom(zoomLevel)
+ .build()
+ }
+
+ // Check if bounds is provided
+ if (jsOptions.hasKey("bounds") && !jsOptions.isNull("bounds")) {
+ val boundsJson = jsOptions.getString("bounds")!!
+ val featureCollection = FeatureCollection.fromJson(boundsJson)
+ val coords = featureCollection.features()?.mapNotNull { feature ->
+ feature.geometry() as? Point
+ } ?: emptyList()
+
+ if (coords.isEmpty()) {
+ throw IllegalArgumentException("bounds contains no valid coordinates")
+ }
+
+ return snapshotter.cameraForCoordinates(
+ coords,
+ EdgeInsets(0.0, 0.0, 0.0, 0.0),
+ heading,
+ pitch
+ )
+ }
+
+ throw IllegalArgumentException("neither centerCoordinate nor bounds provided")
}
private fun getOptions(jsOptions: ReadableMap): MapSnapshotOptions {
diff --git a/example/src/examples/Camera/TakeSnapshot.js b/example/src/examples/Camera/TakeSnapshot.js
index 22c2976d8..2208d55d6 100755
--- a/example/src/examples/Camera/TakeSnapshot.js
+++ b/example/src/examples/Camera/TakeSnapshot.js
@@ -7,6 +7,8 @@ import {
Dimensions,
Text,
ActivityIndicator,
+ TouchableOpacity,
+ ScrollView,
} from 'react-native';
import BaseExamplePropTypes from '../common/BaseExamplePropTypes';
@@ -17,9 +19,31 @@ const styles = StyleSheet.create({
padding: 16,
},
snapshot: {
- flex: 1,
+ width: '100%',
+ height: 200,
+ marginBottom: 16,
},
spinnerContainer: { alignItems: 'center', flex: 1, justifyContent: 'center' },
+ label: {
+ fontSize: 14,
+ fontWeight: 'bold',
+ marginBottom: 8,
+ color: '#333',
+ },
+ button: {
+ backgroundColor: '#4264fb',
+ padding: 12,
+ borderRadius: 8,
+ marginBottom: 16,
+ },
+ buttonText: {
+ color: 'white',
+ textAlign: 'center',
+ fontWeight: 'bold',
+ },
+ section: {
+ marginBottom: 24,
+ },
});
class TakeSnapshot extends React.Component {
@@ -31,54 +55,137 @@ class TakeSnapshot extends React.Component {
super(props);
this.state = {
- snapshotURI: null,
+ withLogoURI: null,
+ withoutLogoURI: null,
+ boundsURI: null,
+ loading: true,
};
}
componentDidMount() {
- this.takeSnapshot();
+ this.takeAllSnapshots();
}
- async takeSnapshot() {
- const { width, height } = Dimensions.get('window');
-
- const uri = await snapshotManager.takeSnap({
- centerCoordinate: [-74.12641, 40.797968],
- width,
- height,
- zoomLevel: 12,
- pitch: 30,
- heading: 20,
- styleURL: StyleURL.Dark,
- writeToDisk: true,
- });
-
- this.setState({ snapshotURI: uri });
+ async takeAllSnapshots() {
+ const { width } = Dimensions.get('window');
+ const snapshotWidth = width - 32;
+ const snapshotHeight = 200;
+
+ try {
+ // Snapshot with logo (default)
+ const withLogoURI = await snapshotManager.takeSnap({
+ centerCoordinate: [-74.12641, 40.797968],
+ width: snapshotWidth,
+ height: snapshotHeight,
+ zoomLevel: 12,
+ pitch: 30,
+ heading: 20,
+ styleURL: StyleURL.Dark,
+ writeToDisk: true,
+ withLogo: true,
+ });
+
+ // Snapshot without logo
+ const withoutLogoURI = await snapshotManager.takeSnap({
+ centerCoordinate: [-74.12641, 40.797968],
+ width: snapshotWidth,
+ height: snapshotHeight,
+ zoomLevel: 12,
+ pitch: 30,
+ heading: 20,
+ styleURL: StyleURL.Dark,
+ writeToDisk: true,
+ withLogo: false,
+ });
+
+ // Snapshot using bounds instead of centerCoordinate
+ const boundsURI = await snapshotManager.takeSnap({
+ bounds: [
+ [-74.2, 40.7],
+ [-74.0, 40.9],
+ ],
+ width: snapshotWidth,
+ height: snapshotHeight,
+ zoomLevel: 10,
+ pitch: 0,
+ heading: 0,
+ styleURL: StyleURL.Street,
+ writeToDisk: true,
+ withLogo: true,
+ });
+
+ this.setState({
+ withLogoURI,
+ withoutLogoURI,
+ boundsURI,
+ loading: false,
+ });
+ } catch (error) {
+ console.error('Snapshot error:', error);
+ this.setState({ loading: false });
+ }
}
render() {
- let childView = null;
+ const { loading, withLogoURI, withoutLogoURI, boundsURI } = this.state;
- if (!this.state.snapshotURI) {
- childView = (
+ if (loading) {
+ return (
-
- Generating Snapshot
-
- );
- } else {
- childView = (
-
-
+
+ Generating Snapshots...
);
}
- return childView;
+ return (
+
+
+ With Logo (withLogo: true)
+ {withLogoURI && (
+
+ )}
+
+
+
+ Without Logo (withLogo: false)
+ {withoutLogoURI && (
+
+ )}
+
+
+
+
+ Using Bounds (instead of centerCoordinate)
+
+ {boundsURI && (
+
+ )}
+
+
+ {
+ this.setState({ loading: true });
+ this.takeAllSnapshots();
+ }}
+ >
+ Retake Snapshots
+
+
+ );
}
}
diff --git a/ios/RNMBX/RNMBXSnapshotModule.swift b/ios/RNMBX/RNMBXSnapshotModule.swift
index 84c8e583e..1d1e2869b 100644
--- a/ios/RNMBX/RNMBXSnapshotModule.swift
+++ b/ios/RNMBX/RNMBXSnapshotModule.swift
@@ -93,10 +93,12 @@ class RNMBXSnapshotModule : NSObject {
let height = jsOptions["height"] as? NSNumber else {
throw RNMBXError.paramError("width, height: is not a number")
}
- let mapSnapshotOptions = MapSnapshotOptions(
+ let showsLogo = jsOptions["withLogo"] as? Bool ?? true
+ var mapSnapshotOptions = MapSnapshotOptions(
size: CGSize(width: width.doubleValue, height: height.doubleValue),
pixelRatio: 1.0
)
+ mapSnapshotOptions.showsLogo = showsLogo
return mapSnapshotOptions
}