maps icon indicating copy to clipboard operation
maps copied to clipboard

(iOS) v10 - ShapeSource.getClusterExpansionZoom throws error

Open everettblakley opened this issue 2 years ago • 2 comments

Describe the bug
The ShapeSource.getClusterExpansionZoom method throws an error when provided with a Feature argument:

`JSON value '{...JSON object}' of type NSString cannot be converted to NSNumber

To Reproduce

  • Clone the minimal reproduction repo
  • Add your Download token to the app.config.js file
  • Add your Access token to the App.tsx file
  • run expo run:ios to spin up the application (may need to install Expo if your don't already have it)
  • Tap on a cluster

Example:

export default function App() {
  const sourceRef = useRef<MapboxGL.ShapeSource>(null);
  const mapRef = useRef<MapboxGL.MapView>(null);
  const cameraRef = useRef<MapboxGL.Camera>(null);

  const onPress = async (event: OnPressEvent) => {
    const [cluster] = event.features as Feature<Point>[];
    if (cluster.properties?.cluster === true && sourceRef.current) {
      // Error here. Works fine on Android
      const expansionZoom = await sourceRef.current.getClusterExpansionZoom(
        cluster
      );

      const centerCoordinate = cluster.geometry.coordinates;
      const zoomLevel = expansionZoom * 1.15;
      cameraRef.current?.setCamera({
        centerCoordinate,
        zoomLevel,
        animationMode: "easeTo",
        animationDuration: 1500
      });
    }
  };

  return (
    <View style={styles.container}>
      <MapboxGL.MapView
        ref={mapRef}
        style={StyleSheet.absoluteFillObject}
        styleURL={MapboxGL.StyleURL.Dark}
        scaleBarEnabled={false}
        pitchEnabled={false}
      >
        <MapboxGL.Camera
          ref={cameraRef}
          centerCoordinate={[0, 0]}
          zoomLevel={0}
        />

        <MapboxGL.ShapeSource
          ref={sourceRef}
          id="earthquakes"
          onPress={onPress}
          cluster
          clusterRadius={50}
          clusterMaxZoom={14}
          url="https://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_week.geojson"
        >
          <MapboxGL.SymbolLayer
            id="pointCount"
            style={layerStyles.clusterCount}
          />

          <MapboxGL.CircleLayer
            id="clusteredPoints"
            belowLayerID="pointCount"
            filter={["has", "point_count"]}
            style={layerStyles.clusteredPoints}
          />

          <MapboxGL.CircleLayer
            id="singlePoint"
            filter={["!", ["has", "point_count"]]}
            style={layerStyles.singlePoint}
          />
        </MapboxGL.ShapeSource>
      </MapboxGL.MapView>
    </View>
  );
}

Expected behavior
The method returns the zoom required to expand the cluster

Actual behavior
The method throws and error (as seen in the provided screenshot)

Screenshots
Screenshot 2022-06-26 at 5 50 05 PM

Versions (please complete the following information):

  • Platform: iOS
  • Platform OS: iOS 15
  • Device: Phone6
  • Emulator/ Simulator: mo
  • Dev OS: macOS 12.12.1
  • @rnmapbox/maps Version 10.0.0-beta.15
  • Mapbox GL version 10
  • React Native Version 0.68.2
  • Expo Version 45

everettblakley avatar Jun 27 '22 00:06 everettblakley

I've got same issue

image

vadim312 avatar Sep 14 '22 21:09 vadim312

Please help me Mapbox developers

vadim312 avatar Sep 14 '22 22:09 vadim312

I can provide info into why this is happening, but I'm not sure I'll be able to submit a PR...

tldr: using clusterId for cluster-related functions is being deprecated, but the iOS v10 code hasn't been updated for the getClusterExpansionZoom native func (in switft).

details:

javascript/components/ShapeSource.js provides a means to overload the getClusterExpansionZoom function to allow either a

  • clusterId: number or
  • feature: Feature<Geometry, Properties>

and calls the appropriate native func, either getClusterExpansionZoom or getClusterExpansionZoomById

ios/RCTMGL-v10/RCTMGLShapeSource.swift implements getClusterExpansionZoom, but expecting a clusterId arg. There's no getClusterExpansionZoomById

Looks like just an oversight, since getClusterLeaves and getClusterChildren take the cluster feature as an arg.

MillerGregor avatar Sep 26 '22 18:09 MillerGregor

Hello,

I am not an iOS developer but I was able to solve this problem with this patch

diff --git a/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSource.swift b/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSource.swift
index 3542821..09a09ee 100644
--- a/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSource.swift
+++ b/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSource.swift
@@ -191,24 +191,25 @@ extension RCTMGLShapeSource
 
 extension RCTMGLShapeSource
 {
-  func getClusterExpansionZoom(
-    _ clusterId: NSNumber,
-    completion: @escaping (Result<Int, Error>) -> Void)
-  {
-    guard let mapView = map?.mapView else {
-      completion(.failure(RCTMGLError.failed("getClusterExpansionZoom: no mapView")))
-      return
-    }
+    func getClusterExpansionZoom(
+      _ featureJSON: String,
+      completion: @escaping (Result<Int, Error>) -> Void
+    ) {
+      guard let mapView = map?.mapView else {
+        completion(.failure(RCTMGLError.failed("getClusterExpansionZoom: no mapView")))
+        return
+      }
 
-    let options = SourceQueryOptions(sourceLayerIds: nil, filter: Exp(.eq) {
-      Exp(.get) { "cluster_id" }
-      clusterId.uintValue
-    })
-    mapView.mapboxMap.querySourceFeatures(for: id, options: options) { result in
-      switch result {
-      case .success(let features):
-        let cluster = features[0]
-        mapView.mapboxMap.queryFeatureExtension(for: self.id, feature: cluster.feature, extension: "supercluster", extensionField: "expansion-zoom") { result in
+      logged(
+        "RCTMGLShapeSource.getClusterExpansionZoom",
+        rejecter: { (_, _, error) in
+          completion(.failure(error!))
+        }
+      ) {
+        let cluster: Feature = try parse(featureJSON)
+        mapView.mapboxMap.queryFeatureExtension(
+          for: self.id, feature: cluster, extension: "supercluster", extensionField: "expansion-zoom"
+        ) { result in
           switch result {
           case .success(let features):
             guard let value = features.value as? NSNumber else {
@@ -221,11 +222,8 @@ extension RCTMGLShapeSource
             completion(.failure(error))
           }
         }
-      case .failure(let error):
-        completion(.failure(error))
       }
     }
-  }
 
   func getClusterLeaves(_ featureJSON: String,
                               number: uint,
diff --git a/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.m b/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.m
index 9449b79..124bc36 100644
--- a/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.m
+++ b/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.m
@@ -25,7 +25,7 @@
 
 
 RCT_EXTERN_METHOD(getClusterExpansionZoom:(nonnull NSNumber*)reactTag
-                                clusterId:(nonnull NSNumber*)clusterId
+                                 featureJSON:(nonnull NSString*)featureJSON
                                  resolver:(RCTPromiseResolveBlock)resolve
                                  rejecter:(RCTPromiseRejectBlock)reject)
 
diff --git a/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.swift b/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.swift
index 7cb642a..65e152e 100644
--- a/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.swift
+++ b/node_modules/@rnmapbox/maps/ios/RCTMGL-v10/RCTMGLShapeSourceManager.swift
@@ -38,12 +38,12 @@ extension RCTMGLShapeSourceManager {
 extension RCTMGLShapeSourceManager {
   @objc func getClusterExpansionZoom(
     _ reactTag: NSNumber,
-    clusterId: NSNumber,
+    featureJSON: String,
     resolver: @escaping RCTPromiseResolveBlock,
     rejecter: @escaping RCTPromiseRejectBlock) -> Void
   {
     self.withShapeSource(reactTag, name:"getClusterExpansionZoom", rejecter: rejecter) { shapeSource in
-      shapeSource.getClusterExpansionZoom(clusterId) { result in
+      shapeSource.getClusterExpansionZoom(featureJSON) { result in
         switch result {
         case .success(let zoom):
           resolver([

I don't know if it's the right thing to do 😄

xD3CODER avatar Oct 10 '22 11:10 xD3CODER