rnmapbox / maps

A Mapbox react native module for creating custom maps
MIT License
2.02k stars 813 forks source link

[Bug]: App crash on calling ShapeSource getClusterLeaves #3468

Closed g-sharat closed 2 weeks ago

g-sharat commented 2 weeks ago

Mapbox Implementation

Mapbox

Mapbox Version

default

React Native Version

0.73.4

Platform

iOS, Android

@rnmapbox/maps version

10.1.10

Standalone component to reproduce

/* eslint-disable react/prop-types */
import {View, Text} from 'react-native';
import React, {useEffect, useRef, useState} from 'react';
import Mapbox, {
  Camera,
  CircleLayer,
  Image,
  Images,
  ShapeSource,
  SymbolLayer,
} from '@rnmapbox/maps';

export default function ProfilePage() {
  const SF_OFFICE_COORDINATE = [-122.400021, 37.789085];

  const layerStyles = {
    icon: {
      iconImage: ['get', 'icon'],

      iconSize: [
        'match',
        ['get', 'icon'],
        'example',
        1.2,
        'airport-15',
        1.2,
        /* default */ 1,
      ],
      iconAllowOverlap: true,
    },
    singlePoint: {
      circleColor: 'green',
      circleOpacity: 0.84,
      circleStrokeWidth: 2,
      circleStrokeColor: 'white',
      circleRadius: 5,
      circlePitchAlignment: 'map',
    },

    clusteredPoints: {
      circlePitchAlignment: 'map',
      circleColor: 'yellow',
      circleRadius: ['step', ['get', 'point_count'], 20, 100, 30, 750, 40],
      circleOpacity: 0.84,
      circleStrokeWidth: 2,
      circleStrokeColor: 'white',
    },

    clusterCount: {
      textField: ['get', 'point_count'],
      textSize: 12,
      textPitchAlignment: 'map',
    },
  };
  const [layerPoints, setLayerPoints] = useState();
  var shapeSourceRef = useRef();

  useEffect(() => {
    let temp = {
      type: 'FeatureCollection',
      features: [],
    };
    for (let index = 0; index < 10; index++) {
      temp.features.push({
        type: 'Feature',
        id: index,
        properties: {
          icon: index == 0 ? 'pin-rn' : 'test',
        },
        geometry: {
          type: 'Point',
          coordinates: [
            SF_OFFICE_COORDINATE[0] + index,
            SF_OFFICE_COORDINATE[1] + index,
          ],
        },
      });
    }
    setLayerPoints(temp);
  }, []);

  const handleClusterTap = async l => {
    if (l.features.length == 1) {
      //do something with single point
    } else {
      console.log(l.features);
      const collection = await shapeSourceRef.current.getClusterLeaves(
        5,
        999,
        0,
      );
      console.log(collection);
    }
  };

  return (
    <View style={{flex: 1}}>
      <Mapbox.MapView style={{flex: 1}}>
        <Camera
          defaultSettings={{
            centerCoordinate: SF_OFFICE_COORDINATE,
            zoomLevel: 3,
          }}
        />
        <Images>
          <Image name="pin-rn">
            <View>
              <View
                style={{
                  borderRadius: 10,
                  backgroundColor: 'gray',
                  padding: 8,
                  margin: 16,
                  width: 100,
                  shadowOffset: {width: 0, height: 8},
                  shadowOpacity: 0.2,
                }}>
                <Text style={{fontWeight: 'bold', color: 'white'}}>
                  RN Pin 2
                </Text>
              </View>
            </View>
          </Image>
          <Image name="test">
            <View>
              <View
                style={{
                  borderRadius: 10,
                  backgroundColor: 'gray',
                  padding: 8,
                  margin: 16,
                  width: 100,
                  shadowOffset: {width: 0, height: 8},
                  shadowOpacity: 0.2,
                }}>
                <Text style={{fontWeight: 'bold', color: 'white'}}>Test</Text>
              </View>
            </View>
          </Image>
        </Images>
        <ShapeSource
          id="earthquakes"
          cluster
          ref={shapeSourceRef}
          onPress={i => handleClusterTap(i)}
          clusterRadius={50}
          clusterMaxZoom={14}
          shape={layerPoints}>
          <SymbolLayer id="pointCount" style={layerStyles.clusterCount} />

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

          <SymbolLayer id="exampleIconName" style={layerStyles.icon} />
        </ShapeSource>
      </Mapbox.MapView>
    </View>
  );
}

Observed behavior and steps to reproduce

Exception in native call com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was NUMBER at line 1 column 2 path $ at com.google.gson.Gson.fromJson(Gson.java:975) at com.google.gson.Gson.fromJson(Gson.java:928) at com.google.gson.Gson.fromJson(Gson.java:877) at com.google.gson.Gson.fromJson(Gson.java:848) at com.mapbox.geojson.Feature.fromJson(Feature.java:82) at com.rnmapbox.rnmbx.components.styles.sources.RNMBXShapeSource.getClusterLeaves(RNMBXShapeSource.kt:280) at com.rnmapbox.rnmbx.components.styles.sources.RNMBXShapeSourceModule$getClusterLeaves$1.invoke(RNMBXShapeSourceModule.kt:51) at com.rnmapbox.rnmbx.components.styles.sources.RNMBXShapeSourceModule$getClusterLeaves$1.invoke(RNMBXShapeSourceModule.kt:50) at com.rnmapbox.rnmbx.utils.ViewTagResolver.withViewResolved$lambda$4(ViewTagResolver.kt:63) at com.rnmapbox.rnmbx.utils.ViewTagResolver.$r8$lambda$ZFXWzquiK28lSR5tbH0BihabahM(Unknown Source:0) at com.rnmapbox.rnmbx.utils.ViewTagResolver$$ExternalSyntheticLambda0.run(Unknown Source:8) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:29) at android.os.Looper.loopOnce(Looper.java:201) at android.os.Looper.loop(Looper.java:288) at android.app.ActivityThread.main(ActivityThread.java:7872) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was NUMBER at line 1 column 2 path $ at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:384) at com.mapbox.geojson.Feature$GsonTypeAdapter.read(Feature.java:579) at com.mapbox.geojson.Feature$GsonTypeAdapter.read(Feature.java:497) at com.google.gson.Gson.fromJson(Gson.java:963) ... 19 more Screenshot_1714376311

Expected behavior

App shouldn't crash and should return collection

Notes / preliminary analysis

No response

Additional links and references

No response

g-sharat commented 2 weeks ago

Sorry for not checking more thoroughly. I need to pass the argument as const [cluster] = l.features;. Closing the issue as redundant