지도 레이어 토글과 현재 위치 주소 보강

This commit is contained in:
seo
2026-07-12 16:35:49 +09:00
parent fa8159db11
commit 2a8cbb96cd
2 changed files with 163 additions and 5 deletions
+161 -3
View File
@@ -178,6 +178,10 @@ const deviceLang = (navigator.language || navigator.userLanguage || "en").starts
mapFit: "전체",
mapPlay: "재생",
mapPause: "정지",
mapTraffic: "교통",
mapCadastral: "지적",
mapSatellite: "위성",
mapHistory: "기록",
historySummary: "기록 요약",
historyDistance: "이동 거리",
historyDuration: "기록 시간",
@@ -385,6 +389,10 @@ const deviceLang = (navigator.language || navigator.userLanguage || "en").starts
mapFit: "Fit",
mapPlay: "Play",
mapPause: "Pause",
mapTraffic: "Traffic",
mapCadastral: "Cadastral",
mapSatellite: "Satellite",
mapHistory: "History",
historySummary: "History Summary",
historyDistance: "Distance",
historyDuration: "Duration",
@@ -445,6 +453,13 @@ const deviceLang = (navigator.language || navigator.userLanguage || "en").starts
playbackSpeed: 1,
playbackSpeedOptions: [1],
rangeCircles: [],
layers: {},
mapLayerState: {
traffic: false,
cadastral: false,
satellite: false,
history: true,
},
homeCircle: null,
homeCircles: [],
homeCenter: null,
@@ -477,6 +492,7 @@ let langCode = localStorage.getItem("currentLang") || deviceLang,
radiusInterval,
_overrideRange,
wakeLock = null;
const reverseGeocodeCache = new Map();
const PLAYBACK_SPEEDS = [2, 5, 10, 20, 50, 100, 500];
const PLAYBACK_MIN_VISIBLE_STEPS = 80;
@@ -1207,12 +1223,71 @@ function setCookieHours(name, value, hours) {
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; expires=${d.toUTCString()}; path=/; SameSite=Lax`;
}
function isUsefulAddress(value) {
const text = String(value || '').trim();
return !!text && text !== '-' && text.toLowerCase() !== 'not_home' && text.toUpperCase() !== 'N/A';
}
function formatLatLngAddress(lat, lng) {
return `${Number(lat).toFixed(5)}, ${Number(lng).toFixed(5)}`;
}
function setCurrentPositionText(value) {
const el = document.getElementById('currentPosition');
if (!el) return;
el.textContent = value || '-';
el.title = value || '';
}
function updateCurrentAddress(data) {
if (!hasValidCoords(data.latitude, data.longitude)) {
setCurrentPositionText(isUsefulAddress(data.address) ? data.address : '-');
return;
}
if (isUsefulAddress(data.address)) {
setCurrentPositionText(data.address);
return;
}
const lat = Number(data.latitude);
const lng = Number(data.longitude);
const fallback = formatLatLngAddress(lat, lng);
setCurrentPositionText(fallback);
if (!naver.maps.Service?.reverseGeocode) return;
const key = `${lat.toFixed(5)},${lng.toFixed(5)}`;
if (reverseGeocodeCache.has(key)) {
setCurrentPositionText(reverseGeocodeCache.get(key));
return;
}
naver.maps.Service.reverseGeocode({
coords: new naver.maps.LatLng(lat, lng),
orders: ([
naver.maps.Service.OrderType?.ROAD_ADDR,
naver.maps.Service.OrderType?.ADDR,
].filter(Boolean).join(',') || 'roadaddr,addr')
}, (status, response) => {
const okStatus = naver.maps.Service.Status?.OK || 'OK';
if (status !== okStatus) return;
const address = response?.v2?.address;
const resolved = address?.roadAddress || address?.jibunAddress || '';
if (!resolved) return;
reverseGeocodeCache.set(key, resolved);
if (Math.abs(Number(lastLat) - lat) < 0.00001 && Math.abs(Number(lastLng) - lng) < 0.00001) {
setCurrentPositionText(resolved);
}
});
}
function updateDOM(data) {
highAccuracyModeActive = !!data.highAccuracyMode;
syncUpdateBtn();
const textMap = {
currentPosition: data.address,
currentPosition: isUsefulAddress(data.address)
? data.address
: (hasValidCoords(data.latitude, data.longitude) ? formatLatLngAddress(data.latitude, data.longitude) : '-'),
lastUpdateTime: data.updated ? getRelativeTime(data.updated) : "-",
nowActivity: data.activity,
connectionType: data.connection,
@@ -1230,7 +1305,10 @@ function updateDOM(data) {
for (const [id, value] of Object.entries(textMap)) {
const el = document.getElementById(id);
if (el) el.textContent = value || "-";
if (el) {
el.textContent = value || "-";
el.title = value || "";
}
}
toggleOptionalRow('WbatteryStatus', data.wstate);
@@ -1426,6 +1504,7 @@ function initMap(latlng, accuracy = 50) {
createMainMarker(latlng);
createAccuracyCircle(latlng, accuracy);
updateHomeCircles();
ensureMapLayers();
naver.maps.Event.addListener(mapState.map, 'zoom_changed', () => {
renderStayMarkers();
if (mapState.map.getZoom() < CURRENT_STAY_MIN_ZOOM) {
@@ -1445,6 +1524,12 @@ function initMap(latlng, accuracy = 50) {
ensureMapTools();
}
function ensureMapLayers() {
if (!mapState.map || Object.keys(mapState.layers).length) return;
if (naver.maps.TrafficLayer) mapState.layers.traffic = new naver.maps.TrafficLayer();
if (naver.maps.CadastralLayer) mapState.layers.cadastral = new naver.maps.CadastralLayer();
}
function ensureMapTools() {
const container = document.getElementById('mapContainer') || document.getElementById('map')?.parentElement;
if (!container || document.getElementById('mapTools')) return;
@@ -1461,6 +1546,10 @@ function ensureMapTools() {
tools.innerHTML = `
<button type="button" class="map-tool" data-map-action="fit"></button>
<button type="button" class="map-tool" data-map-action="play"></button>
<button type="button" class="map-tool" data-map-action="traffic"></button>
<button type="button" class="map-tool" data-map-action="cadastral"></button>
<button type="button" class="map-tool" data-map-action="satellite"></button>
<button type="button" class="map-tool active" data-map-action="history"></button>
<button type="button" class="map-tool active" data-map-action="filter"></button>
<button type="button" class="map-tool" data-map-action="naver"></button>
`;
@@ -1502,6 +1591,10 @@ function updateMapToolLabels() {
const labelMap = {
fit: t.mapFit,
play: mapState.playbackTimer ? `${t.mapPause} ${mapState.playbackSpeed}x` : t.mapPlay,
traffic: t.mapTraffic,
cadastral: t.mapCadastral,
satellite: t.mapSatellite,
history: t.mapHistory,
filter: t.historyFiltered,
naver: t.openNaverMap,
};
@@ -1511,10 +1604,67 @@ function updateMapToolLabels() {
button.title = labelMap[action] || action;
});
document.querySelector('[data-map-action="filter"]')?.classList.toggle('active', mapState.filterOutliers);
document.querySelector('[data-map-action="traffic"]')?.classList.toggle('active', mapState.mapLayerState.traffic);
document.querySelector('[data-map-action="cadastral"]')?.classList.toggle('active', mapState.mapLayerState.cadastral);
document.querySelector('[data-map-action="satellite"]')?.classList.toggle('active', mapState.mapLayerState.satellite);
document.querySelector('[data-map-action="history"]')?.classList.toggle('active', mapState.mapLayerState.history);
const timelineTitle = document.getElementById('historyTimelineTitle');
if (timelineTitle) timelineTitle.textContent = t.historyTimeline;
}
function setHistoryLayerVisible(visible) {
const map = visible ? mapState.map : null;
mapState.trackLine?.setMap?.(map);
mapState.trackLines?.forEach(line => line.setMap?.(map));
mapState.smallMarkers?.forEach(marker => marker.setMap?.(map));
mapState.historyStartMarker?.setMap?.(map);
mapState.historyEndMarker?.setMap?.(map);
if (visible) {
renderStayMarkers();
} else {
mapState.stayMarkers?.forEach(marker => marker.setMap?.(null));
stopCurrentStayBadge();
}
renderHistorySummary();
updateTimelinePanel();
}
function toggleMapLayer(action) {
if (action === 'traffic') {
if (!mapState.layers.traffic) {
mapState.mapLayerState.traffic = false;
return true;
}
mapState.mapLayerState.traffic = !mapState.mapLayerState.traffic;
mapState.layers.traffic?.setMap(mapState.mapLayerState.traffic ? mapState.map : null);
return true;
}
if (action === 'cadastral') {
if (!mapState.layers.cadastral) {
mapState.mapLayerState.cadastral = false;
return true;
}
mapState.mapLayerState.cadastral = !mapState.mapLayerState.cadastral;
mapState.layers.cadastral?.setMap(mapState.mapLayerState.cadastral ? mapState.map : null);
return true;
}
if (action === 'satellite') {
mapState.mapLayerState.satellite = !mapState.mapLayerState.satellite;
if (naver.maps.MapTypeId) {
const hybridType = naver.maps.MapTypeId.HYBRID || 'hybrid';
const normalType = naver.maps.MapTypeId.NORMAL || 'normal';
mapState.map.setMapTypeId(mapState.mapLayerState.satellite ? hybridType : normalType);
}
return true;
}
if (action === 'history') {
mapState.mapLayerState.history = !mapState.mapLayerState.history;
setHistoryLayerVisible(mapState.mapLayerState.history);
return true;
}
return false;
}
function handleMapTool(action) {
if (!mapState.map) return;
if (action === 'fit') {
@@ -1533,6 +1683,10 @@ function handleMapTool(action) {
updateMapToolLabels();
return;
}
if (toggleMapLayer(action)) {
updateMapToolLabels();
return;
}
if (action === 'naver') {
openSelectedPointInNaverMap();
}
@@ -2563,7 +2717,7 @@ function renderStayMarkers() {
mapState.stayMarkers.forEach(marker => marker.setMap(null));
mapState.stayMarkers = [];
}
if (!mapState.stayPoints?.length) return;
if (!mapState.mapLayerState.history || !mapState.stayPoints?.length) return;
buildStayMarkerGroups(mapState.stayPoints).forEach(group => {
const isCluster = group.stays.length > 1;
@@ -2635,6 +2789,9 @@ function renderHistoryPoints(rawPoints) {
}
renderStayMarkers();
if (!mapState.mapLayerState.history) {
setHistoryLayerVisible(false);
}
if (!mapState.historyBoundsFitted) {
fitHistoryOrCurrentBounds();
@@ -2715,6 +2872,7 @@ function updateDOMWithData(data) {
lastLat = data.latitude;
lastLng = data.longitude;
lastUpdateISOTime = data.updated;
updateCurrentAddress(data);
const currentPoint = {
lat: data.latitude,
lng: data.longitude,