네이티브 맵 최적화 반응



Video Url



지도에 사용되는 라이브러리:
https://www.npmjs.com/package/react-native-maps

이 게시물은 막대한 양의 마커/폴리곤이 지도에 표시될 때 앱 충돌을 방지하고 최적화하는 방법에 관한 것입니다. 여러 데이터가 있는 지도를 사용할 때 많은 성능 문제가 있으며 확대/축소 수준에 따라 사용자 지정 마커를 업데이트해야 합니다. 따라서 두 플랫폼 모두에서 앱을 쉽게 실행하는 데 필요한 각 단계를 안내해 드리겠습니다.

사용 중인 샘플 데이터:

export const kmlData = {
  status: true,
  data: [
    {
      id: 3,
      polygons: [
        {latitude: 24.79672061085053, longitude: 46.69632228737188},
        {latitude: 24.79730088563427, longitude: 46.6960244924467},
        {latitude: 24.7969867813524, longitude: 46.69528762847696},
        {latitude: 24.796409228271, longitude: 46.69558319234557},
        {latitude: 24.79672061085053, longitude: 46.69632228737188},
      ],
      center: {latitude: 24.796853402304418, longitude: 46.69580273788693},
    },
    {
      id: 5,
      polygons: [
        {latitude: 24.79478092109267, longitude: 46.69287860779153},
        {latitude: 24.79491840277788, longitude: 46.69320194403617},
        {latitude: 24.79511421069013, longitude: 46.6931029170428},
        {latitude: 24.79497672880431, longitude: 46.69277958136081},
        {latitude: 24.79478092109267, longitude: 46.69287860779153},
      ],
      center: {latitude: 24.794971095661616, longitude: 46.693035073221495},
    },
*
*
*
*
]}



설명:
  • trackViewChangesMap prop은 주로 Android에서 충돌/지연 없이 맵을 실행하려는 경우 항상 false로 유지되어야 합니다. 활성화하면 마커가 첫 번째 렌더링 통과 후 정보 창의 내용을 변경할 수 있지만 성능이 저하되므로 필요하지 않을 때마다 비활성화하는 것이 좋습니다.
  • iOS의 경우 앱 영역의 확대/축소 수준(Latitudedelta)이 변경된 경우에만 trackViewChanges를 true로 설정하고 약간의 지연 후 다시 false로 설정합니다. 하지만 이 수정으로 Android가 제대로 작동하지 않습니다.
  • Android의 경우 영역이 변경될 때마다 마커를 다시 렌더링하는 특정key 속성을 제공하고 항상 trackViewChanges를 false로 설정합니다.

  • 추가 사항:
  • getCenterOfAllPolygon()는 지도의 폴리곤 내부에 어떤 뷰도 넣을 수 없고 그 위에 마커를 사용해야 하는 폴리곤 위에 뷰를 표시할 수 없기 때문에 폴리곤 중앙에 마커를 배치할 수 있도록 폴리곤의 중심을 제공합니다.
  • animateToRegion(coordinate,delay)는 특정 지역으로 이동하는 데 사용됩니다.
  • fitToCoordinates(polygonsArray, {edgePadding,animated})는 다각형을 클릭하여 측면을 자르지 않고 다각형 수준으로 확대하려는 경우에 사용됩니다.

  • 파일Map.js
    export default function MapPlots({}) {
      const mapRef = useRef(null);
      const markerRef = useRef(null);
      const [markerType, setMarkerType] = useState(0);
      const [clickedPolygon, setClickedPolygon] = useState();
      const [mapType, setmapType] = useState(SATELLITE_MAP_MODE);
      const [trackViewChanges, settrackViewChanges] = useState(false);
      const [regionChanged, setregionChanged] = useState(null);
      const {data} = kmlData;
      const isIOS = Platform.OS === 'ios' ? true : false;
      const coordinatesData = data.map((v, i) => ({
        ...v,
        color:
          i % 2 === 0
            ? [COLOR_GREEN_OPACITY, COLOR_GREEN]
            : [COLOR_RED_OPACITY, COLOR_RED],
      }));
      const navigation = useNavigation();
      const initialRegion = {
        ...coordinatesData[0]?.polygons[0],
        latitudeDelta: 1,
        longitudeDelta: 50,
      };
    
      console.log('coordinatesDatacoordinatesData', coordinatesData);
    
      const onMapReady = () => {
        InteractionManager.runAfterInteractions(() => goToLocation());
        // getCenterOfAllPolygon();
      };
    
      const goToLocation = () => {
        let initCoordinates = coordinatesData[0]?.polygons[0] || [];
        let c2 = {
          ...initCoordinates,
          latitudeDelta: 0.005,
          longitudeDelta: 0.005,
        };
        mapRef?.current?.animateToRegion(c2, 5 * 1000);
      };
    
      // const getCenterOfAllPolygon = () => {
      //   let centers = [];
      //   polygonsCoordinates.map((v, i) => {
      //     let c = getCenterPolygon(v);
      //     centers.push(c);
      //   });
      //   setPolygonCenters(centers);
      //   console.log('centers', centers);
      // };
    
      // let getCenterPolygon = coordinates => {
      //   let x = coordinates.map(c => c.latitude);
      //   let y = coordinates.map(c => c.longitude);
    
      //   let minX = Math.min.apply(null, x);
      //   let maxX = Math.max.apply(null, x);
    
      //   let minY = Math.min.apply(null, y);
      //   let maxY = Math.max.apply(null, y);
    
      //   return {
      //     latitude: (minX + maxX) / 2,
      //     longitude: (minY + maxY) / 2,
      //   };
      // };
    
      const onPressPolygon = (polygons, index = 0) => {
        console.log('onPressPolygononPressPolygon', polygons, index);
        setClickedPolygon(index);
        mapRef?.current?.fitToCoordinates(polygons, {
          edgePadding: {top: 2, right: 2, bottom: 2, left: 2},
          animated: true,
        });
      };
    
        const onRegionChangeComplete = param => {
        console.log('onRegionChangeComplete0', param);
        if (parseInt(param?.latitudeDelta.toFixed(2)) < 0.03) {
          //50//1
          if (param?.latitudeDelta.toFixed(5) !== regionChanged?.latitudeDelta) {
            setregionChanged({
              ...param,
              latitudeDelta: param?.latitudeDelta.toFixed(5),
              longitudeDelta: param?.longitudeDelta.toFixed(5),
            });
            let toFixedLat = param?.latitudeDelta;
            console.log('onRegionChangeComplete1', param);
            if (toFixedLat > 0.005) {
              setMarkerType(1);
            } else if (toFixedLat < 0.005) {
              setMarkerType(2);
            }
            //region change 0=nothing show
            //region change 1=dot show
            //region change 2=marker show
            settrackViewChanges(true);
            setTimeout(() => {
              markerRef?.current?.redraw();
              settrackViewChanges(false);
            }, 100);
          }
        }
      };
    
      const setMapTypeFunc = () => {
        setmapType(v =>
          v === STANDARD_MAP_MODE ? SATELLITE_MAP_MODE : STANDARD_MAP_MODE,
        );
      };
    
      return (
        <View style={styles.container}>
          <TouchableOpacity
            style={styles.mapType(mapType)}
            onPress={setMapTypeFunc}>
            <CustomIcon
              color={
                mapType === STANDARD_MAP_MODE ? COLOR_GRAY_70 : COLOR_SECONDARY
              }
              name={ICONOGRAPHY.FLOORS}
              size={20}
            />
          </TouchableOpacity>
    
          {markerType ? null : (
            <View
              style={{
                ...styles.map,
                ...styles.loader,
              }}>
              <ActivityIndicator color={COLOR_PRIMARY} size="large" />
            </View>
          )}
    
          <MapView
            provider={MapView.PROVIDER_GOOGLE}
            ref={mapRef}
            style={styles.map}
            onMapReady={onMapReady}
            initialRegion={initialRegion}
            mapType={mapType}
            loadingEnabled={true}
            showsCompass={true}
            onRegionChangeComplete={onRegionChangeComplete}>
            {!markerType
              ? null
              : coordinatesData.map((item, index) => (
                  <View key={index}>
                    <Polygon
                      coordinates={item?.polygons}
                      strokeColor={
                        index !== clickedPolygon ? COLOR_PLOT_BORDER : COLOR_BLACK
                      } // fallback for when `strokeColors` is not supported by the map-provider
                      fillColor={item?.color[index !== clickedPolygon ? 0 : 1]}
                      strokeWidth={index !== clickedPolygon ? 0.3 : 2}
                      tappable
                      geodesic
                      onPress={() => onPressPolygon(item?.polygons, index)}
                    />
                    <Marker
                      ref={markerRef}
                      key={
                        isIOS() ? index : `${index}${trackViewChanges}`
                        // isIOS() ? index : `${index}${trackViewChanges}${Date.now()}`
                      }
                      opacity={markerType ? 1 : 0}
                      anchor={{x: 0.5, y: 0.5}}
                      centerOffset={{x: 0.5, y: 0.5}}
                      onPress={() => onPressPolygon(item?.polygons, index)}
                      coordinate={item?.center}
                      tracksViewChanges={isIOS() ? trackViewChanges : false}>
                      {!markerType ? (
                        <View />
                      ) : markerType === 1 ? (
                        <View style={styles.dot} />
                      ) : (
                        <Text style={styles.markerText}>1001</Text>
                      )}
                    </Marker>
                  </View>
                ))}
          </MapView>
          {clickedPolygon === null || clickedPolygon === undefined ? null : (
            <PopupBottom
              navigation={navigation}
              clickedPolygon={clickedPolygon}
              onPressCloseButton={() => setClickedPolygon(null)}
            />
          )}
        </View>
      );
    }
    const styles = StyleSheet.create({
      container: {
        ...StyleSheet.absoluteFillObject,
        flex: 1,
        justifyContent: 'flex-end',
        alignItems: 'center',
      },
      back: {
        height: 20,
        width: 20,
        backgroundColor: 'red',
        position: 'absolute',
        zIndex: 1,
        left: 20,
        top: 50,
      },
      map: {
        ...StyleSheet.absoluteFillObject,
      },
      markerText: {color: '#000', fontSize: 12},
      mapType: mapType => ({
        position: 'absolute',
        zIndex: 1,
        top: 160,
        left: 16,
        height: 30,
        width: 30,
        borderRadius: 4,
        backgroundColor: COLOR_WHITE,
        borderWidth: 1,
        borderColor: mapType === STANDARD_MAP_MODE ? COLOR_WHITE : COLOR_SECONDARY,
        justifyContent: 'center',
        alignItems: 'center',
        ...shadow.shadow,
      }),
      loader: {
        justifyContent: 'center',
        alignItems: 'center',
        zIndex: 100,
      },
      dot: {
        height: 10,
        width: 10,
        borderRadius: 10,
        backgroundColor: COLOR_WHITE,
      },
    });
    
    

    좋은 웹페이지 즐겨찾기