체류 마커 줌별 묶음과 위치 갱신 순서 정리
This commit is contained in:
+87
-39
@@ -488,6 +488,7 @@ const CURRENT_STAY_BADGE_WIDTH = 140;
|
||||
const CURRENT_STAY_BADGE_HEIGHT = 28;
|
||||
const CURRENT_STAY_BADGE_GAP = 8;
|
||||
const CURRENT_STAY_MIN_ZOOM = 13;
|
||||
const STAY_CLUSTER_MIN_ZOOM = 13;
|
||||
|
||||
const FALLBACK_BACK_TARGET = 'https://chaegeon.com/';
|
||||
const BACK_HISTORY_KEY = 'chaegeon.backHistory.v1';
|
||||
@@ -1426,6 +1427,7 @@ function initMap(latlng, accuracy = 50) {
|
||||
createAccuracyCircle(latlng, accuracy);
|
||||
updateHomeCircles();
|
||||
naver.maps.Event.addListener(mapState.map, 'zoom_changed', () => {
|
||||
renderStayMarkers();
|
||||
if (mapState.map.getZoom() < CURRENT_STAY_MIN_ZOOM) {
|
||||
stopCurrentStayBadge();
|
||||
setStayMarkersVisibleForCurrentPoint(
|
||||
@@ -2285,8 +2287,8 @@ function setStayMarkersVisibleForCurrentPoint(point) {
|
||||
if (!mapState.stayMarkers?.length) return;
|
||||
const shouldHideCurrent = point && !_overrideRange && !mapState.playbackTimer && hasValidCoords(point.lat, point.lng);
|
||||
mapState.stayMarkers.forEach(marker => {
|
||||
const stay = marker.stayPoint;
|
||||
const hide = shouldHideCurrent && stay && distanceMeters(stay, point) <= STAY_GROUP_RADIUS_M;
|
||||
const stays = marker.stayPoints || (marker.stayPoint ? [marker.stayPoint] : []);
|
||||
const hide = shouldHideCurrent && stays.some(stay => distanceMeters(stay, point) <= STAY_GROUP_RADIUS_M);
|
||||
marker.setMap(hide ? null : mapState.map);
|
||||
});
|
||||
}
|
||||
@@ -2527,6 +2529,82 @@ function applyHistoryCounts(data) {
|
||||
mapState.historyRenderCount = Number.isFinite(Number(data?.historyRenderCount)) ? Number(data.historyRenderCount) : historyLength;
|
||||
}
|
||||
|
||||
function stayClusterRadiusForZoom(zoom) {
|
||||
if (zoom >= STAY_CLUSTER_MIN_ZOOM) return 0;
|
||||
if (zoom <= 8) return 70000;
|
||||
if (zoom <= 10) return 25000;
|
||||
return 6000;
|
||||
}
|
||||
|
||||
function buildStayMarkerGroups(stays) {
|
||||
const zoom = mapState.map?.getZoom?.() || 12;
|
||||
const radius = stayClusterRadiusForZoom(zoom);
|
||||
if (!radius) return stays.map(stay => ({ lat: stay.lat, lng: stay.lng, stays: [stay] }));
|
||||
|
||||
const groups = [];
|
||||
stays.forEach(stay => {
|
||||
const group = groups.find(item => distanceMeters(item, stay) <= radius);
|
||||
if (group) {
|
||||
const oldCount = group.stays.length;
|
||||
const nextCount = oldCount + 1;
|
||||
group.lat = ((group.lat * oldCount) + Number(stay.lat)) / nextCount;
|
||||
group.lng = ((group.lng * oldCount) + Number(stay.lng)) / nextCount;
|
||||
group.stays.push(stay);
|
||||
} else {
|
||||
groups.push({ lat: Number(stay.lat), lng: Number(stay.lng), stays: [stay] });
|
||||
}
|
||||
});
|
||||
return groups;
|
||||
}
|
||||
|
||||
function renderStayMarkers() {
|
||||
if (!mapState.map) return;
|
||||
if (mapState.stayMarkers?.length) {
|
||||
mapState.stayMarkers.forEach(marker => marker.setMap(null));
|
||||
mapState.stayMarkers = [];
|
||||
}
|
||||
if (!mapState.stayPoints?.length) return;
|
||||
|
||||
buildStayMarkerGroups(mapState.stayPoints).forEach(group => {
|
||||
const isCluster = group.stays.length > 1;
|
||||
const duration = group.stays.reduce((sum, stay) => sum + (Number(stay.duration) || 0), 0);
|
||||
const label = isCluster ? `${formatNumber(group.stays.length)}개` : formatDuration(Math.round(duration));
|
||||
const marker = new naver.maps.Marker({
|
||||
position: new naver.maps.LatLng(group.lat, group.lng),
|
||||
map: mapState.map,
|
||||
zIndex: isCluster ? 75 : 80,
|
||||
icon: {
|
||||
content: `<div class="history-marker history-marker-stay">${label}</div>`,
|
||||
anchor: new naver.maps.Point(26, 14)
|
||||
}
|
||||
});
|
||||
marker.stayPoints = group.stays;
|
||||
if (isCluster) {
|
||||
naver.maps.Event.addListener(marker, "click", () => {
|
||||
mapState.map.panTo(marker.getPosition());
|
||||
mapState.map.setZoom(Math.max(mapState.map.getZoom() + 2, STAY_CLUSTER_MIN_ZOOM));
|
||||
});
|
||||
} else {
|
||||
const stay = group.stays[0];
|
||||
marker.infoWindow = new naver.maps.InfoWindow({
|
||||
content: buildStayInfoContent(stay)
|
||||
});
|
||||
marker.stayPoint = stay;
|
||||
naver.maps.Event.addListener(marker, "click", () => {
|
||||
if (mapState.openInfoWindow && mapState.openInfoWindow !== marker.infoWindow) {
|
||||
mapState.openInfoWindow.close();
|
||||
}
|
||||
marker.infoWindow.open(mapState.map, marker);
|
||||
mapState.openInfoWindow = marker.infoWindow;
|
||||
});
|
||||
}
|
||||
mapState.stayMarkers.push(marker);
|
||||
});
|
||||
setStayMarkersVisibleForCurrentPoint(
|
||||
hasValidCoords(lastLat, lastLng) ? { lat: lastLat, lng: lastLng } : null
|
||||
);
|
||||
}
|
||||
|
||||
function renderHistoryPoints(rawPoints) {
|
||||
if (!mapState.map || !rawPoints?.length) return;
|
||||
const keepPlaybackIndex = !!mapState.playbackTimer;
|
||||
@@ -2547,10 +2625,6 @@ function renderHistoryPoints(rawPoints) {
|
||||
mapState.smallMarkers.forEach(marker => marker.setMap(null));
|
||||
mapState.smallMarkers = [];
|
||||
}
|
||||
if (mapState.stayMarkers?.length) {
|
||||
mapState.stayMarkers.forEach(marker => marker.setMap(null));
|
||||
mapState.stayMarkers = [];
|
||||
}
|
||||
if (mapState.historyStartMarker) {
|
||||
mapState.historyStartMarker.setMap(null);
|
||||
mapState.historyStartMarker = null;
|
||||
@@ -2560,32 +2634,7 @@ function renderHistoryPoints(rawPoints) {
|
||||
mapState.historyEndMarker = null;
|
||||
}
|
||||
|
||||
mapState.stayPoints.forEach(stay => {
|
||||
const marker = new naver.maps.Marker({
|
||||
position: stay.latlng,
|
||||
map: mapState.map,
|
||||
zIndex: 80,
|
||||
icon: {
|
||||
content: `<div class="history-marker history-marker-stay">${formatDuration(Math.round(stay.duration))}</div>`,
|
||||
anchor: new naver.maps.Point(26, 14)
|
||||
}
|
||||
});
|
||||
marker.infoWindow = new naver.maps.InfoWindow({
|
||||
content: buildStayInfoContent(stay)
|
||||
});
|
||||
marker.stayPoint = stay;
|
||||
naver.maps.Event.addListener(marker, "click", () => {
|
||||
if (mapState.openInfoWindow && mapState.openInfoWindow !== marker.infoWindow) {
|
||||
mapState.openInfoWindow.close();
|
||||
}
|
||||
marker.infoWindow.open(mapState.map, marker);
|
||||
mapState.openInfoWindow = marker.infoWindow;
|
||||
});
|
||||
mapState.stayMarkers.push(marker);
|
||||
});
|
||||
setStayMarkersVisibleForCurrentPoint(
|
||||
hasValidCoords(lastLat, lastLng) ? { lat: lastLat, lng: lastLng } : null
|
||||
);
|
||||
renderStayMarkers();
|
||||
|
||||
if (!mapState.historyBoundsFitted) {
|
||||
fitHistoryOrCurrentBounds();
|
||||
@@ -2650,13 +2699,6 @@ function updateDOMWithData(data) {
|
||||
initMap(latlng, data.accuracy);
|
||||
}
|
||||
|
||||
// 라이브 모드에서도 최신 24시간 기록을 계속 반영한다.
|
||||
// 현재 위치 자동 이동은 지도 중심만 바꾸므로, 경로와 종료 마커는 여기서 별도로 갱신한다.
|
||||
if (data.history?.length > 0) {
|
||||
applyHistoryCounts(data);
|
||||
updateHistoryMarkers(data.history);
|
||||
}
|
||||
|
||||
// 메인 마커 / 정확도 원 업데이트
|
||||
const isPlaybackActive = !!mapState.playbackTimer;
|
||||
setCurrentLocationVisible(!isPlaybackActive);
|
||||
@@ -2679,6 +2721,12 @@ function updateDOMWithData(data) {
|
||||
latlng,
|
||||
time: data.updated || new Date().toISOString(),
|
||||
};
|
||||
|
||||
// 라이브 위치 마커를 먼저 최신화한 뒤 같은 기준으로 경로와 체류 마커를 다시 그린다.
|
||||
if (data.history?.length > 0) {
|
||||
applyHistoryCounts(data);
|
||||
updateHistoryMarkers(data.history);
|
||||
}
|
||||
setStayMarkersVisibleForCurrentPoint(currentPoint);
|
||||
updateCurrentStayBadge(currentPoint);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user