diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt index ef529ef14..35dddca95 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt @@ -3,6 +3,7 @@ package com.rnmapbox.rnmbx.components.mapview import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableArray +import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.facebook.react.bridge.WritableNativeMap import com.rnmapbox.rnmbx.NativeMapViewModuleSpec @@ -12,6 +13,7 @@ import com.rnmapbox.rnmbx.utils.ViewRefTag import com.rnmapbox.rnmbx.utils.ViewTagResolver import com.rnmapbox.rnmbx.utils.extensions.toCoordinate import com.rnmapbox.rnmbx.utils.extensions.toScreenCoordinate +import com.mapbox.bindgen.Value class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver: ViewTagResolver) : NativeMapViewModuleSpec(context) { private fun withMapViewOnUIThread( @@ -184,6 +186,70 @@ class NativeMapViewModule(context: ReactApplicationContext, val viewTagResolver: ) } } + override fun queryRenderedLayersInRect( + viewRef: ViewRefTag?, + withBBox: ReadableArray, + withFilter: ReadableArray, + withLayerIDs: ReadableArray?, + promise: Promise + ) { + withMapViewOnUIThread(viewRef, promise) { + val layerIds = ConvertUtils.toStringList(withLayerIDs) + + it.queryRenderedLayersInRect( + ConvertUtils.toRectF(withBBox), + ExpressionParser.from(withFilter), + if (layerIds.size == 0) null else layerIds, + createCommandResponse(promise) + ) + } + } + + override fun getStyles( + viewRef: ViewRefTag?, + promise: Promise + ) { + withMapViewOnUIThread(viewRef, promise) { + + it.getStyles( + createCommandResponse(promise) + ) + } + } + + override fun setLayerProperties( + viewRef: ViewRefTag?, + layerId: String, + properties: ReadableMap, + promise: Promise + ) { + withMapViewOnUIThread(viewRef, promise) { + + it.setLayerProperties( + layerId, + properties, + createCommandResponse(promise) + ) + } + } + + override fun setLayerProperty( + viewRef: ViewRefTag?, + layerId: String, + property: String, + value: String, + promise: Promise + ) { + withMapViewOnUIThread(viewRef, promise) { + + it.setLayerProperty( + layerId, + property, + value, + createCommandResponse(promise) + ) + } + } companion object { const val NAME = "RNMBXMapViewModule" diff --git a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt index 95398fb6c..e14385b07 100644 --- a/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt +++ b/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt @@ -68,6 +68,7 @@ import java.util.* import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationCoordinator import com.rnmapbox.rnmbx.components.images.ImageManager +import com.google.gson.Gson; import com.rnmapbox.rnmbx.v11compat.event.* import com.rnmapbox.rnmbx.v11compat.feature.* @@ -75,6 +76,7 @@ import com.rnmapbox.rnmbx.v11compat.mapboxmap.* import com.rnmapbox.rnmbx.v11compat.ornamentsettings.* import org.json.JSONException import org.json.JSONObject +import com.facebook.react.bridge.ReadableMapKeySetIterator; fun MutableList.removeIf21(predicate: (T) -> Boolean): Boolean { var removed = false @@ -88,6 +90,40 @@ fun MutableList.removeIf21(predicate: (T) -> Boolean): Boolean { } return removed } +fun convertReadableArrayToList(readableArray: ReadableArray): List { + val list = mutableListOf() + for (i in 0 until readableArray.size()) { + val value = when (readableArray.getType(i)) { + ReadableType.Boolean -> Value.valueOf(readableArray.getBoolean(i)) + ReadableType.Number -> Value.valueOf(readableArray.getDouble(i)) + ReadableType.String -> Value.valueOf(readableArray.getString(i) ?: "") + ReadableType.Map -> Value.valueOf(convertReadableMapToHashMap(readableArray.getMap(i)!!)) + ReadableType.Array -> Value.valueOf(convertReadableArrayToList(readableArray.getArray(i)!!)) + else -> throw IllegalArgumentException("Unsupported type in array") + } + list.add(value) + } + return list +} +fun convertReadableMapToHashMap(readableMap: ReadableMap): HashMap { + val hashMap = HashMap() + val iterator: ReadableMapKeySetIterator = readableMap.keySetIterator() + + while (iterator.hasNextKey()) { + val key = iterator.nextKey() + val value = when (readableMap.getType(key)) { + ReadableType.Boolean -> Value.valueOf(readableMap.getBoolean(key)) + ReadableType.Number -> Value.valueOf(readableMap.getDouble(key)) + ReadableType.String -> Value.valueOf(readableMap.getString(key)!!) + ReadableType.Map -> Value.valueOf(convertReadableMapToHashMap(readableMap.getMap(key)!!)) + ReadableType.Array -> Value.valueOf(convertReadableArrayToList(readableMap.getArray(key)!!)) + else -> throw IllegalArgumentException("Unsupported type") + } + hashMap[key] = value + } + + return hashMap +} data class OrnamentSettings( var enabled : Boolean? = false, @@ -95,6 +131,11 @@ data class OrnamentSettings( var position: Int = -1 ) +data class FeatureObject( + val id: String, + val feature: String +) + enum class MapGestureType { Move,Scale,Rotate,Fling,Shove } @@ -1502,7 +1543,77 @@ open class RNMBXMapView(private val mContext: Context, var mManager: RNMBXMapVie } } + fun queryRenderedLayersInRect(rect: RectF?, filter: Expression?, layerIDs: List?, response: CommandResponse) { + val size = mMap!!.getMapOptions().size + val screenBox = if (rect == null) ScreenBox(ScreenCoordinate(0.0, 0.0), ScreenCoordinate(size?.width!!.toDouble(), size?.height!!.toDouble())) else ScreenBox( + ScreenCoordinate(rect.right.toDouble(), rect.bottom.toDouble() ), + ScreenCoordinate(rect.left.toDouble(), rect.top.toDouble()), + ) + mMap.queryRenderedFeatures( + RenderedQueryGeometry(screenBox), + RenderedQueryOptions(layerIDs, filter) + ) { features -> + if (features.isValue) { + val featuresList = ArrayList() + for (i in features.value!!) { + val featureJson = try { + i.queriedFeature.feature.toJson() + } catch (e: Exception) { + Logger.e("queryRenderedFeaturesAtPoint", "Error converting feature to JSON", e) + continue + } + featuresList.add(FeatureObject(i.queriedFeature.sourceLayer ?: "unknown", featureJson)) + } + + response.success { + val gson = Gson(); + val json = gson.toJson(featuresList); + it.putString("data", json) + } + } else { + response.error(features.error ?: "n/a") + } + } + } + + fun getStyles(response: CommandResponse) { + val styleJSON = try { + val style = mMap!!.getStyle() + val json = style?.styleJSON ?: throw Exception("Style JSON is null") + json + } catch (e: Exception) { + response.error("Error converting styles to JSON: ${e.message}") + return + } + response.success { + it.putString("data", styleJSON) + } + } + fun setLayerProperties(layerId: String, properties: ReadableMap, response: CommandResponse) { + val style = mMap!!.getStyle() + val propertiesValue = convertReadableMapToHashMap(properties) + try { + style?.setStyleLayerProperties(layerId, Value.valueOf(propertiesValue)) + response.success { + it.putString("data", "Successfully set layer properties") + } + } catch (e: Exception) { + response.error("Error setting layer properties: ${e.message}") + } + } + + fun setLayerProperty(layerId: String, property: String, value: String, response: CommandResponse) { + val style = mMap!!.getStyle() + try { + style?.setStyleLayerProperty(layerId,property,value as Value) + response.success { + it.putString("data", "Successfully set layer property") + } + } catch (e: Exception) { + response.error("Error setting layer property") + } + } // endregion } diff --git a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeMapViewModuleSpec.java b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeMapViewModuleSpec.java index 3081071d0..7a60eabf1 100644 --- a/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeMapViewModuleSpec.java +++ b/android/src/main/old-arch/com/rnmapbox/rnmbx/NativeMapViewModuleSpec.java @@ -1,4 +1,3 @@ - /** * This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen). * @@ -9,7 +8,6 @@ * * @nolint */ - package com.rnmapbox.rnmbx; import com.facebook.proguard.annotations.DoNotStrip; @@ -19,71 +17,91 @@ import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactModuleWithSpec; import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.turbomodule.core.interfaces.TurboModule; import javax.annotation.Nonnull; import javax.annotation.Nullable; public abstract class NativeMapViewModuleSpec extends ReactContextBaseJavaModule implements ReactModuleWithSpec, TurboModule { - public static final String NAME = "RNMBXMapViewModule"; - public NativeMapViewModuleSpec(ReactApplicationContext reactContext) { - super(reactContext); - } + public static final String NAME = "RNMBXMapViewModule"; + + public NativeMapViewModuleSpec(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public @Nonnull + String getName() { + return NAME; + } + + @ReactMethod + @DoNotStrip + public abstract void takeSnap(@Nullable Integer viewRef, boolean writeToDisk, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void queryTerrainElevation(@Nullable Integer viewRef, ReadableArray coordinates, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void setSourceVisibility(@Nullable Integer viewRef, boolean visible, String sourceId, String sourceLayerId, Promise promise); + + @ReactMethod + @DoNotStrip + public abstract void getCenter(@Nullable Integer viewRef, Promise promise); - @Override - public @Nonnull String getName() { - return NAME; - } + @ReactMethod + @DoNotStrip + public abstract void getCoordinateFromView(@Nullable Integer viewRef, ReadableArray atPoint, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void takeSnap(@Nullable Integer viewRef, boolean writeToDisk, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void getPointInView(@Nullable Integer viewRef, ReadableArray atCoordinate, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void queryTerrainElevation(@Nullable Integer viewRef, ReadableArray coordinates, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void getZoom(@Nullable Integer viewRef, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void setSourceVisibility(@Nullable Integer viewRef, boolean visible, String sourceId, String sourceLayerId, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void getVisibleBounds(@Nullable Integer viewRef, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void getCenter(@Nullable Integer viewRef, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void queryRenderedFeaturesAtPoint(@Nullable Integer viewRef, ReadableArray atPoint, ReadableArray withFilter, ReadableArray withLayerIDs, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void getCoordinateFromView(@Nullable Integer viewRef, ReadableArray atPoint, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void queryRenderedFeaturesInRect(@Nullable Integer viewRef, ReadableArray withBBox, ReadableArray withFilter, ReadableArray withLayerIDs, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void getPointInView(@Nullable Integer viewRef, ReadableArray atCoordinate, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void setHandledMapChangedEvents(@Nullable Integer viewRef, ReadableArray events, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void getZoom(@Nullable Integer viewRef, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void clearData(@Nullable Integer viewRef, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void getVisibleBounds(@Nullable Integer viewRef, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void querySourceFeatures(@Nullable Integer viewRef, String sourceId, ReadableArray withFilter, ReadableArray withSourceLayerIDs, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void queryRenderedFeaturesAtPoint(@Nullable Integer viewRef, ReadableArray atPoint, ReadableArray withFilter, ReadableArray withLayerIDs, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void queryRenderedLayersInRect(@Nullable Integer viewRef, ReadableArray withBBox, ReadableArray withFilter, ReadableArray withLayerIDs, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void queryRenderedFeaturesInRect(@Nullable Integer viewRef, ReadableArray withBBox, ReadableArray withFilter, ReadableArray withLayerIDs, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void getStyles(@Nullable Integer viewRef, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void setHandledMapChangedEvents(@Nullable Integer viewRef, ReadableArray events, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void setLayerProperties(@Nullable Integer viewRef, String layerId, ReadableMap properties, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void clearData(@Nullable Integer viewRef, Promise promise); + @ReactMethod + @DoNotStrip + public abstract void setLayerProperty(@Nullable Integer viewRef, String layerId, String property, String value, Promise promise); - @ReactMethod - @DoNotStrip - public abstract void querySourceFeatures(@Nullable Integer viewRef, String sourceId, ReadableArray withFilter, ReadableArray withSourceLayerIDs, Promise promise); } diff --git a/example/rnmapbox.web 2.symlink b/example/rnmapbox.web 2.symlink new file mode 120000 index 000000000..8fb16c871 --- /dev/null +++ b/example/rnmapbox.web 2.symlink @@ -0,0 +1 @@ +../javascript/web \ No newline at end of file diff --git a/ios/RNMBX/RNMBXMapViewManager.swift b/ios/RNMBX/RNMBXMapViewManager.swift index 53834dfdb..5c49bd709 100644 --- a/ios/RNMBX/RNMBXMapViewManager.swift +++ b/ios/RNMBX/RNMBXMapViewManager.swift @@ -224,4 +224,101 @@ extension RNMBXMapViewManager { } } } + + @objc public static func queryRenderedLayersInRect( + _ map: RNMBXMapView, + withBBox bbox: [NSNumber], + withFilter filter: [Any]?, + withLayerIDs layerIDs: [String]?, + resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock) -> Void { + let top = bbox.isEmpty ? 0.0 : CGFloat(bbox[0].floatValue) + let right = bbox.isEmpty ? 0.0 : CGFloat(bbox[1].floatValue) + let bottom = bbox.isEmpty ? 0.0 : CGFloat(bbox[2].floatValue) + let left = bbox.isEmpty ? 0.0 : CGFloat(bbox[3].floatValue) + let rect = bbox.isEmpty ? CGRect(x: 0.0, y: 0.0, width: map.bounds.size.width, height: map.bounds.size.height) : CGRect(x: [left,right].min()!, y: [top,bottom].min()!, width: abs(right-left), height: abs(bottom-top)) + logged("queryRenderedLayersInRect.option", rejecter: rejecter) { + let options = try RenderedQueryOptions(layerIds: layerIDs?.isEmpty ?? true ? nil : layerIDs, filter: filter?.asExpression()) + map.mapboxMap.queryRenderedFeatures(with: rect, options: options) { result in + switch result { + case .success(let features): + resolver(["data": features.compactMap { feature in + logged("queryRenderedLayersInRect.queriedfeature.map") { + return ["id": feature.queriedFeature.sourceLayer, "feature": try feature.queriedFeature.feature.toJSON()] } + }]) + case .failure(let error): + rejecter("queryRenderedLayersInRect","failed to query features", error) + } + } + } + } + @objc public static func getStyles( + _ map: RNMBXMapView, + resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock) -> Void { + logged("getStyles.option", rejecter: rejecter) { + resolver(["data": map.mapboxMap.style.JSON]) + } + } + + @objc public static func setLayerProperties( + _ map: RNMBXMapView, + withLayerID layerID: String, + withProperties properties: [String: Any], + resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock) -> Void { + do{ + try map.mapboxMap.style.setLayerProperties(for: layerID, properties: properties) + resolver(nil) + } catch let error { + rejecter("failed_to_set_properties", "An error occurred while setting layer properties: \(error.localizedDescription)", error) + } + } + + @objc public static func setLayerProperty( + _ map: RNMBXMapView, + withLayerID layerID: String, + withProperty property: String, + withValue value: String, + resolver: @escaping RCTPromiseResolveBlock, + rejecter: @escaping RCTPromiseRejectBlock) -> Void { + do{ + try map.mapboxMap.style.setLayerProperty(for: layerID, property: property, value: value) + resolver(nil) + } catch let error { + rejecter("failed_to_set_property", "An error occurred while setting layer property: \(error.localizedDescription)", error) + } + } + + // @objc public static func setLayerFilter( + // _ map: RNMBXMapView, + // withLayerID layerID: String, + // withFilter filter: [Any], + // resolver: @escaping RCTPromiseResolveBlock, + // rejecter: @escaping RCTPromiseRejectBlock) -> Void { + + // do { + // var layer:Layer = try map.mapboxMap.style.layer(withId: layerID) + // if let specificLayer = layer as? FillLayer { + // try map.mapboxMap.style.updateLayer(withId: layerID, type: FillLayer.self) { (updatedLayer: inout FillLayer) in + // updatedLayer.filter = try filter.asExpression() + // } + // } else if let specificLayer = layer as? LineLayer { + // try map.mapboxMap.style.updateLayer(withId: layerID, type: LineLayer.self) { (updatedLayer: inout LineLayer) in + // updatedLayer.filter = try filter.asExpression() + // } + // } else if let specificLayer = layer as? CircleLayer { + // try map.mapboxMap.style.updateLayer(withId: layerID, type: CircleLayer.self) { (updatedLayer: inout CircleLayer) in + // updatedLayer.filter = try filter.asExpression() + // } + // } else { + // // Handle other layer types as needed + // throw NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unsupported layer type"]) + // } + + // resolver(nil) + // } catch let error { + // rejecter("failed_to_set_filter", "Set Layer Filter: \(error.localizedDescription)", error) + // } + // } } diff --git a/ios/RNMBX/RNMBXMapViewModule.mm b/ios/RNMBX/RNMBXMapViewModule.mm index 21cb8f1ad..9ad171370 100644 --- a/ios/RNMBX/RNMBXMapViewModule.mm +++ b/ios/RNMBX/RNMBXMapViewModule.mm @@ -139,6 +139,30 @@ - (void)withMapView:(nonnull NSNumber*)viewRef block:(void (^)(RNMBXMapView *))b } reject:reject methodName:@"querySourceFeatures"]; } +RCT_EXPORT_METHOD(queryRenderedLayersInRect:(nonnull NSNumber*)viewRef withBBox:(NSArray *)withBBox withFilter:(NSArray *)withFilter withLayerIDs:(NSArray *)withLayerIDs resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [self withMapView:viewRef block:^(RNMBXMapView *view) { + [RNMBXMapViewManager queryRenderedLayersInRect:view withBBox:withBBox withFilter:withFilter withLayerIDs:withLayerIDs resolver:resolve rejecter:reject]; + } reject:reject methodName:@"queryRenderedLayersInRect"]; +} + +RCT_EXPORT_METHOD(getStyles:(nonnull NSNumber*)viewRef resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) { + [self withMapView:viewRef block:^(RNMBXMapView *view) { + [RNMBXMapViewManager getStyles:view resolver:resolve rejecter:reject]; + } reject:reject methodName:@"getStyles"]; +} + +RCT_EXPORT_METHOD(setLayerProperties: (nonnull NSNumber *)viewRef withLayerID:(NSString *)withLayerID withProperties: (NSDictionary *)withProperties resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject){ + [self withMapView:viewRef block:^(RNMBXMapView *view) { + [RNMBXMapViewManager setLayerProperties:view withLayerID:withLayerID withProperties:withProperties resolver:resolve rejecter:reject]; + } reject:reject methodName:@"setLayerProperties"]; +} + +RCT_EXPORT_METHOD(setLayerProperty : (nonnull NSNumber *)viewRef withLayerID: (nonnull NSString *)withLayerID withProperty: (nonnull NSString *)withProperty withValue: (nonnull NSString *)withValue resolve: (RCTPromiseResolveBlock)resolve reject: (RCTPromiseRejectBlock)reject){ + [self withMapView:viewRef block:^(RNMBXMapView *view) { + [RNMBXMapViewManager setLayerProperty:view withLayerID:withLayerID withProperty:withProperty withValue:withValue resolver:resolve rejecter:reject]; + } reject:reject methodName:@"setLayerProperty"]; +} + // Thanks to this guard, we won't compile this code when we build for the old architecture. #ifdef RCT_NEW_ARCH_ENABLED - (std::shared_ptr)getTurboModule: diff --git a/package.json b/package.json index 996deed17..c10bd6a54 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "@rnmapbox/maps", - "description": "A Mapbox react native module for creating custom maps", - "version": "10.1.27", + "name": "rnmapbox-ditch", + "description": "A customized Mapbox React Native module designed to create tailored maps, specifically enhanced for the unique requirements of Ditch Navigation.", + "version": "1.2.0", "publishConfig": { "access": "public" }, @@ -13,10 +13,9 @@ "mapbox", "react-native" ], - "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/rnmapbox/maps" + "url": "https://github.com/extentdevices/ditch-rnmapbox" }, "main": "lib/commonjs/index", "module": "lib/module/index", @@ -57,10 +56,10 @@ }, "peerDependencies": { "expo": ">=47.0.0", - "mapbox-gl": "^2.9.0", + "mapbox-gl": "^3.1.2", "react": ">=16.6.1", - "react-native": ">=0.59.9", - "react-dom": ">= 17.0.0" + "react-dom": ">= 17.0.0", + "react-native": ">=0.59.9" }, "peerDependenciesMeta": { "expo": { @@ -87,6 +86,7 @@ "@babel/eslint-parser": "^7.19.1", "@babel/plugin-proposal-class-properties": "7.18.6", "@babel/runtime": "7.19.0", + "@mdx-js/mdx": "^3.0.0", "@react-native/eslint-config": "^0.72.2", "@sinonjs/fake-timers": "^8.0.1", "@testing-library/react-native": "^12.4.0", @@ -109,18 +109,17 @@ "jest": "29.7.0", "jest-cli": "29.7.0", "lint-staged": "^12.1.2", - "mapbox-gl": "^2.9.0", + "mapbox-gl": "^3.1.2", "metro-react-native-babel-preset": "0.71.1", "node-dir": "0.1.17", "prettier": "2.7.1", "react": "18.2.0", "react-docgen": "rnmapbox/react-docgen#rnmapbox-dist-react-docgen-v6", - "react-native": "0.73.0-rc.4", + "react-native": "^0.75.2", "react-native-builder-bob": "^0.23.1", "react-test-renderer": "18.2.0", "ts-node": "10.9.1", - "typescript": "5.1.3", - "@mdx-js/mdx": "^3.0.0" + "typescript": "5.1.3" }, "codegenConfig": { "name": "rnmapbox_maps_specs", diff --git a/src/components/Annotation.tsx b/src/components/Annotation.tsx index b0925228a..a4d56b275 100644 --- a/src/components/Annotation.tsx +++ b/src/components/Annotation.tsx @@ -129,7 +129,7 @@ class Annotation extends React.Component { return ( } > {children} diff --git a/src/components/MapView.tsx b/src/components/MapView.tsx index 13042336f..24cf27787 100644 --- a/src/components/MapView.tsx +++ b/src/components/MapView.tsx @@ -470,6 +470,16 @@ type CallbablePropKeysWithoutOn = CallbablePropKeys extends `on${infer C}` type Debounced = F & { clear(): void; flush(): void }; +type LayerProperty = Array | string | number | boolean; + +type LayerProperties = { + [key: string]: LayerProperty; +}; +type FeatureLayer = { + id: string; + feature: GeoJSON.Feature | string; +}; + /** * MapView backed by Mapbox Native GL */ @@ -775,6 +785,51 @@ class MapView extends NativeBridgeComponent( } } + async queryRenderedLayersInRect( + bbox: BBox | [], + filter: FilterExpression | [] = [], + layerIDs: string[] | null = null, + ) { + if ( + bbox != null && + (bbox.length === 4 || (RNMBXModule.MapboxV10 && bbox.length === 0)) + ) { + const res = await this._runNative<{ + data: FeatureLayer[] | string; + }>('queryRenderedLayersInRect', [bbox, getFilter(filter), layerIDs]); + + if (isAndroid()) { + const data = JSON.parse(res.data as unknown as string); + + return data.map((layer: FeatureLayer) => { + return { ...layer, feature: JSON.parse(layer.feature as string) }; + }); + } + + return res.data; + } else { + throw new Error( + 'Must pass in a valid bounding box: [top, right, bottom, left]. An empty array [] is also acceptable in v10.', + ); + } + } + async getStyles() { + const res = await this._runNative<{ data: string }>('getStyles'); + return JSON.parse(res.data); + } + + async setLayerProperties(layerID: string, properities: LayerProperties) { + return await this._runNative('setLayerProperties', [layerID, properities]); + } + + async setLayerProperty(layerID: string, property: string, value: string) { + return await this._runNative('setLayerProperty', [ + layerID, + property, + value, + ]); + } + /** * Returns an array of GeoJSON Feature objects representing features within the specified vector tile or GeoJSON source that satisfy the query parameters. * diff --git a/src/components/UserLocation.tsx b/src/components/UserLocation.tsx index d45fb5778..a655a1a61 100644 --- a/src/components/UserLocation.tsx +++ b/src/components/UserLocation.tsx @@ -3,6 +3,7 @@ import React, { ReactElement } from 'react'; import locationManager from '../modules/location/locationManager'; import { type Location } from '../modules/location/locationManager'; import { CircleLayerStyle } from '../Mapbox'; +import { OnPressEvent } from '../types/OnPressEvent'; import Annotation from './Annotation'; import CircleLayer from './CircleLayer'; @@ -92,7 +93,7 @@ type Props = { /** * Callback that is triggered on location icon press */ - onPress?: () => void; + onPress?: (event: OnPressEvent) => void; /** * Callback that is triggered on location update @@ -237,7 +238,7 @@ class UserLocation extends React.Component { if (location && location.coords) { const { longitude, latitude } = location.coords; - ({ heading } = location.coords); + heading = location.coords.course ?? location.coords.heading; coordinates = [longitude, latitude]; }