겹치는 마커 처리(클러스터)에 사용법에 대하여 질문
ParkJongJoon7128 opened this issue · 4 comments
크롤링한 데이터를 기반으로 지도맵에 마커를 뿌려 보여줄려고 하는데, 적은양의 데이터가 아니라서 마커가 많아 겹치는 부분이 많습니다.
때문에 이를 방지하고자 라이브러리에서 클러스터(cluster) 기능을 제공해주고 있어서 사용하고 있는데,
구현을 하고 앱을 실행해보니 클러스터 기능이 적용이 안되고, 마커만 적용되고 있어 질문합니다.
공식문서에서 사용된 예제에서는 클러스터가 사용되어 정상적으로 동작하는것으로 보이는데, 혹시 제 코드에서 미흡하게 구현한 부분이 있으면 조언을 받고 싶어 코드와 시연 영상과 함께 이슈 글을 남깁니다.
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2024-09-23.at.14.08.00.mp4
제가 구현중인 코드입니다.
import {
Camera,
ClusterMarkerProp,
MapType,
NaverMapMarkerOverlay,
NaverMapView,
NaverMapViewRef,
Region,
} from '@mj-studio/react-native-naver-map';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { View } from 'react-native';
import { getRestaurants } from '../utils/API/LocationAPI';
import { Restaurant } from '../utils/data/type';
const Cameras = {
KNU: {
latitude: 37.271855,
longitude: 127.127626,
zoom: 14,
},
} satisfies Record<string, Camera>;
const Regions = {
KNU_Region: {
latitude: 37.271855,
longitude: 127.127626,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
},
} satisfies Record<string, Region>;
const MapTypes = [
'Basic',
'Navi',
'Satellite',
'Hybrid',
'Terrain',
'NaviHybrid',
'None',
] satisfies MapType[];
const LocationMapScreen = () => {
// Logic
const ref = useRef<NaverMapViewRef>(null);
const [restaurants, setRestaurants] = useState<Restaurant[]>([]);
const [indoor, setIndoor] = useState(false);
const [mapType, setMapType] = useState<MapType>(MapTypes[0]!);
const [lightness, setLightness] = useState(0);
const [compass, setCompass] = useState(true);
const [scaleBar, setScaleBar] = useState(true);
const [zoomControls, setZoomControls] = useState(true);
const [indoorLevelPicker, setIndoorLevelPicker] = useState(true);
const [myLocation, setMyLocation] = useState(true);
const [hash, setHash] = useState(0);
const [camera, setCamera] = useState(Cameras.KNU);
const clusters = useMemo<
{
markers: ClusterMarkerProp[];
screenDistance?: number;
minZoom?: number;
maxZoom?: number;
animate?: boolean;
width?: number;
height?: number;
}[]
>(() => {
return restaurants.map(i => {
return {
width: 200,
height: 200,
markers: restaurants.map<ClusterMarkerProp>(
j =>
({
image: {
httpUri: `https://picsum.photos/seed/${hash}-${i}-${j}/32/32`,
},
width: 100,
height: 100,
latitude: Cameras.KNU.latitude + Math.random() + 1.5,
longitude: Cameras.KNU.longitude + Math.random() + 1.5,
identifier: `${hash}-${i}-${j}`,
} satisfies ClusterMarkerProp),
),
};
});
}, [hash]);
useEffect(() => {
getRestaurants(setRestaurants);
}, []);
// View
return (
<View
style={{
flex: 1,
backgroundColor: 'white',
}}>
<NaverMapView
style={{flex: 1}}
ref={ref}
initialCamera={{
latitude: 37.271855,
longitude: 127.127626,
zoom: 15,
}}
locale="ko"
layerGroups={{
BUILDING: true,
BICYCLE: false,
CADASTRAL: false,
MOUNTAIN: false,
TRAFFIC: false,
TRANSIT: false,
}}
mapType={mapType}
initialRegion={Regions.KNU_Region}
camera={camera}
isIndoorEnabled={indoor}
isShowCompass={compass}
isShowIndoorLevelPicker={indoorLevelPicker}
isShowScaleBar={scaleBar}
isShowZoomControls={zoomControls}
isShowLocationButton={myLocation}
lightness={lightness}
clusters={clusters}
onInitialized={() => console.log('initialized!')}
onTapClusterLeaf={({markerIdentifier}) => {
console.log('onTapClusterLeaf', markerIdentifier);
}}>
{restaurants.map((restaurant, index) => (
<NaverMapMarkerOverlay
key={restaurant.id}
latitude={parseFloat(restaurant.y)}
longitude={parseFloat(restaurant.x)}
onTap={() => console.log(`Tapped on: ${restaurant.name}`)}
anchor={{x: 0.5, y: 1}}
width={20}
height={20}>
<View style={{width: 50, height: 50, backgroundColor: 'red'}} />
</NaverMapMarkerOverlay>
))}
</NaverMapView>
</View>
);
};
export default LocationMapScreen;
예제의 App.tsx
의 코드를 그대로 쓰신 것으로 보입니다. clusters
변수가 hash
가 변할 때만 변경되도록 설정되어 있으니 useMemo
를 지워보시면 될것같습니다.
현재 저는 서버로 요청을 보내 받아온 결과값을 restaurants
라는 state 변수에 저장하여 marker로 뿌려주고 있습니다(빨간 사각형이 응답값으로 내려온 데이터 좌표값들).
이 state 변수와 연관지어 clusters
를 보여줄순 없나요? 지도를 축소했을때는 겹치는 마커만큼 갯수를 clusters로 표현하고, 확대하면 clusters가 벗겨져 마커를 띄우게 하고 싶습니다.
현재 구현한 코드입니다.
import { formatJson, generateArray } from '@mj-studio/js-util';
import {
Camera,
ClusterMarkerProp,
MapType,
NaverMapMarkerOverlay,
NaverMapView,
NaverMapViewRef,
Region,
} from '@mj-studio/react-native-naver-map';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { View } from 'react-native';
import { getRestaurants } from '../utils/API/LocationAPI';
import { Restaurant } from '../utils/data/type';
const Cameras = {
KNU: {
latitude: 37.271855,
longitude: 127.127626,
zoom: 14,
},
} satisfies Record<string, Camera>;
const Regions = {
KNU_Region: {
latitude: 37.271855,
longitude: 127.127626,
latitudeDelta: 0.01,
longitudeDelta: 0.01,
},
} satisfies Record<string, Region>;
const MapTypes = [
'Basic',
'Navi',
'Satellite',
'Hybrid',
'Terrain',
'NaviHybrid',
'None',
] satisfies MapType[];
const LocationMapScreen = () => {
// Logic
const ref = useRef<NaverMapViewRef>(null);
const [restaurants, setRestaurants] = useState<Restaurant[]>([]);
const [indoor, setIndoor] = useState(false);
const [mapType, setMapType] = useState<MapType>(MapTypes[0]!);
const [lightness, setLightness] = useState(0);
const [compass, setCompass] = useState(true);
const [scaleBar, setScaleBar] = useState(true);
const [zoomControls, setZoomControls] = useState(true);
const [indoorLevelPicker, setIndoorLevelPicker] = useState(true);
const [myLocation, setMyLocation] = useState(true);
const [hash, setHash] = useState(0);
const [camera, setCamera] = useState(Cameras.KNU);
const clusters = useMemo<
{
markers: ClusterMarkerProp[];
screenDistance?: number;
minZoom?: number;
maxZoom?: number;
animate?: boolean;
width?: number;
height?: number;
}[]
>(() => {
return generateArray(5).map(i => {
return {
width: 200,
height: 200,
markers: generateArray(restaurants.length).map<ClusterMarkerProp>(
j =>
({
image: {
httpUri: `https://picsum.photos/seed/${hash}-${i}-${j}/32/32`,
},
width: 100,
height: 100,
latitude: Cameras.KNU.latitude,
longitude: Cameras.KNU.longitude,
identifier: `${hash}-${i}-${j}`,
} satisfies ClusterMarkerProp),
),
};
});
}, [hash]);
useEffect(() => {
getRestaurants(setRestaurants);
}, []);
// View
return (
<View
style={{
flex: 1,
backgroundColor: 'white',
}}>
<NaverMapView
style={{flex: 1}}
ref={ref}
initialCamera={{
latitude: 37.271855,
longitude: 127.127626,
zoom: 15,
}}
locale="ko"
layerGroups={{
BUILDING: true,
BICYCLE: false,
CADASTRAL: false,
MOUNTAIN: false,
TRAFFIC: false,
TRANSIT: false,
}}
mapType={mapType}
initialRegion={Regions.KNU_Region}
camera={camera}
isIndoorEnabled={indoor}
isShowCompass={compass}
isShowIndoorLevelPicker={indoorLevelPicker}
isShowScaleBar={scaleBar}
isShowZoomControls={zoomControls}
isShowLocationButton={myLocation}
lightness={lightness}
clusters={clusters}
onInitialized={() => console.log('initialized!')}
onTapClusterLeaf={({markerIdentifier}) => {
console.log('onTapClusterLeaf', markerIdentifier);
}}
onTapMap={(args) => console.log(`Map Tapped: ${formatJson(args)}`)}
>
{restaurants.map((restaurant, index) => (
<NaverMapMarkerOverlay
key={restaurant.id}
latitude={parseFloat(restaurant.y)}
longitude={parseFloat(restaurant.x)}
onTap={() => console.log(`Tapped on: ${restaurant.name}`)}
anchor={{x: 0.5, y: 1}}
width={20}
height={20}>
<View style={{width: 50, height: 50, backgroundColor: 'red'}} />
</NaverMapMarkerOverlay>
))}
</NaverMapView>
</View>
);
};
export default LocationMapScreen;
@mym0404 클러스터링 사용법에 대해서 궁금합니다. 예제 코드를 확인하고 구현해보려 하나 이해가 되지 않아 질문합니다.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 2 days.