체류 지점 방문 내역 합산 표시
This commit is contained in:
@@ -72,7 +72,7 @@ Naver Map에는 단일 Polyline 중심의 부드러운 경로선을 먼저 그
|
|||||||
|
|
||||||
집 판정은 설정된 서울 집과 목포 집 좌표를 중심점으로 한 150m 반경을 기준으로 합니다. GPS 튐 보정이 켜져 있으면 두 집 주변 1.2km 안의 지점은 가까운 집 위치로 흡수하고, 외출은 연속된 외부 지점이 5분 이상 지속될 때만 세션으로 확정합니다. 외부 지점 사이의 공백이 2시간을 넘으면 해당 구간은 알 수 없는 구간으로 보고 외출 지속시간에 합산하지 않습니다. 여러 번 나갔다 돌아온 경우 집 체류 시간은 합산하고, 외출은 출발/복귀 단위의 세션으로 따로 보여줍니다.
|
집 판정은 설정된 서울 집과 목포 집 좌표를 중심점으로 한 150m 반경을 기준으로 합니다. GPS 튐 보정이 켜져 있으면 두 집 주변 1.2km 안의 지점은 가까운 집 위치로 흡수하고, 외출은 연속된 외부 지점이 5분 이상 지속될 때만 세션으로 확정합니다. 외부 지점 사이의 공백이 2시간을 넘으면 해당 구간은 알 수 없는 구간으로 보고 외출 지속시간에 합산하지 않습니다. 여러 번 나갔다 돌아온 경우 집 체류 시간은 합산하고, 외출은 출발/복귀 단위의 세션으로 따로 보여줍니다.
|
||||||
|
|
||||||
기록 시간 슬라이더를 움직이면 지도 아래에는 `월. 일. 시간` 형식의 선택 시각만 간결하게 표시합니다. 경로 재생은 얼굴 아이콘 마커만 사용하며, 재생 중에는 현재 위치 얼굴과 정확도 원을 숨깁니다. 재생 버튼은 지점 수에 따라 의미 있는 배속만 남겨 `2x`, `5x`, `10x`, `20x`, `50x`, `100x`, `500x` 순서로 돌고, 각 배속에서 최소 80단계 이상 보여줄 수 없으면 해당 배속은 건너뜁니다. 100m 반경 안에서 10분 이상 머문 구간은 체류 지점 마커로 묶어 표시하되, 서울 집과 목포 집 안의 체류는 계산하거나 표시하지 않습니다. 클릭 시 체류 시간과 시작/종료 시각을 확인할 수 있습니다.
|
기록 시간 슬라이더를 움직이면 지도 아래에는 `월. 일. 시간` 형식의 선택 시각만 간결하게 표시합니다. 경로 재생은 얼굴 아이콘 마커만 사용하며, 재생 중에는 현재 위치 얼굴과 정확도 원을 숨깁니다. 재생 버튼은 지점 수에 따라 의미 있는 배속만 남겨 `2x`, `5x`, `10x`, `20x`, `50x`, `100x`, `500x` 순서로 돌고, 각 배속에서 최소 80단계 이상 보여줄 수 없으면 해당 배속은 건너뜁니다. 100m 반경 안에서 10분 이상 머문 구간은 체류 지점 마커로 묶어 표시하되, 서울 집과 목포 집 안의 체류는 계산하거나 표시하지 않습니다. 같은 위치의 체류는 하나의 마커로 합산하고, 클릭 시 총 체류 시간과 방문별 시작/종료/체류 시간을 확인할 수 있습니다.
|
||||||
|
|
||||||
재생 중 주기적 위치 갱신으로 히스토리 경로가 다시 그려져도 현재 재생 위치를 유지합니다. 재생 타이머는 시작 당시 지점 목록에 고정하지 않고 최신 히스토리 지점 목록을 기준으로 계속 진행합니다.
|
재생 중 주기적 위치 갱신으로 히스토리 경로가 다시 그려져도 현재 재생 위치를 유지합니다. 재생 타이머는 시작 당시 지점 목록에 고정하지 않고 최신 히스토리 지점 목록을 기준으로 계속 진행합니다.
|
||||||
|
|
||||||
|
|||||||
+39
-9
@@ -479,6 +479,7 @@ const PLAYBACK_SPEEDS = [2, 5, 10, 20, 50, 100, 500];
|
|||||||
const PLAYBACK_MIN_VISIBLE_STEPS = 80;
|
const PLAYBACK_MIN_VISIBLE_STEPS = 80;
|
||||||
const PLAYBACK_TICK_MS = 160;
|
const PLAYBACK_TICK_MS = 160;
|
||||||
const STAY_MIN_SECONDS = 10 * 60;
|
const STAY_MIN_SECONDS = 10 * 60;
|
||||||
|
const STAY_GROUP_RADIUS_M = 100;
|
||||||
|
|
||||||
const FALLBACK_BACK_TARGET = 'https://chaegeon.com/';
|
const FALLBACK_BACK_TARGET = 'https://chaegeon.com/';
|
||||||
const BACK_HISTORY_KEY = 'chaegeon.backHistory.v1';
|
const BACK_HISTORY_KEY = 'chaegeon.backHistory.v1';
|
||||||
@@ -1707,12 +1708,11 @@ function filterHistoryOutliers(points) {
|
|||||||
function buildStayPoints(points) {
|
function buildStayPoints(points) {
|
||||||
const stays = [];
|
const stays = [];
|
||||||
let start = 0;
|
let start = 0;
|
||||||
const radius = 100;
|
|
||||||
|
|
||||||
for (let i = 1; i <= points.length; i++) {
|
for (let i = 1; i <= points.length; i++) {
|
||||||
const base = points[start];
|
const base = points[start];
|
||||||
const current = points[i];
|
const current = points[i];
|
||||||
const outside = !current || distanceMeters(base, current) > radius;
|
const outside = !current || distanceMeters(base, current) > STAY_GROUP_RADIUS_M;
|
||||||
if (!outside) continue;
|
if (!outside) continue;
|
||||||
|
|
||||||
const end = points[Math.max(start, i - 1)];
|
const end = points[Math.max(start, i - 1)];
|
||||||
@@ -1725,19 +1725,43 @@ function buildStayPoints(points) {
|
|||||||
start = Math.max(0, i - 1);
|
start = Math.max(0, i - 1);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
stays.push({
|
const visit = {
|
||||||
lat,
|
|
||||||
lng,
|
|
||||||
latlng: new naver.maps.LatLng(lat, lng),
|
|
||||||
startTime: base.time,
|
startTime: base.time,
|
||||||
endTime: end.time,
|
endTime: end.time,
|
||||||
duration,
|
duration,
|
||||||
points: slice.length,
|
points: slice.length,
|
||||||
});
|
};
|
||||||
|
const existing = stays.find(stay => distanceMeters(stay, { lat, lng }) <= STAY_GROUP_RADIUS_M);
|
||||||
|
if (existing) {
|
||||||
|
const oldPoints = existing.points || 1;
|
||||||
|
const nextPoints = oldPoints + slice.length;
|
||||||
|
existing.lat = ((existing.lat * oldPoints) + (lat * slice.length)) / nextPoints;
|
||||||
|
existing.lng = ((existing.lng * oldPoints) + (lng * slice.length)) / nextPoints;
|
||||||
|
existing.latlng = new naver.maps.LatLng(existing.lat, existing.lng);
|
||||||
|
existing.startTime = toTimestamp(existing.startTime) <= toTimestamp(visit.startTime) ? existing.startTime : visit.startTime;
|
||||||
|
existing.endTime = toTimestamp(existing.endTime) >= toTimestamp(visit.endTime) ? existing.endTime : visit.endTime;
|
||||||
|
existing.duration += duration;
|
||||||
|
existing.points = nextPoints;
|
||||||
|
existing.visits.push(visit);
|
||||||
|
} else {
|
||||||
|
stays.push({
|
||||||
|
lat,
|
||||||
|
lng,
|
||||||
|
latlng: new naver.maps.LatLng(lat, lng),
|
||||||
|
startTime: base.time,
|
||||||
|
endTime: end.time,
|
||||||
|
duration,
|
||||||
|
points: slice.length,
|
||||||
|
visits: [visit],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
start = Math.max(0, i - 1);
|
start = Math.max(0, i - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stays.forEach(stay => {
|
||||||
|
stay.visits.sort((a, b) => toTimestamp(a.startTime) - toTimestamp(b.startTime));
|
||||||
|
});
|
||||||
return stays;
|
return stays;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1986,11 +2010,17 @@ function buildHistoryInfoContent(point, type, label) {
|
|||||||
|
|
||||||
function buildStayInfoContent(stay) {
|
function buildStayInfoContent(stay) {
|
||||||
const t = translations[langCode] || translations.ko;
|
const t = translations[langCode] || translations.ko;
|
||||||
|
const visits = Array.isArray(stay.visits) && stay.visits.length
|
||||||
|
? stay.visits
|
||||||
|
: [{ startTime: stay.startTime, endTime: stay.endTime, duration: stay.duration }];
|
||||||
|
const visitRows = visits.map((visit, index) => `
|
||||||
|
<span>${index + 1}. ${formatDateTime(visit.startTime)} - ${formatDateTime(visit.endTime)} · ${formatDuration(Math.round(visit.duration))} 체류</span>
|
||||||
|
`).join('');
|
||||||
return `
|
return `
|
||||||
<div class="history-info-window history-info-stay">
|
<div class="history-info-window history-info-stay">
|
||||||
<strong>${t.historyStay}</strong>
|
<strong>${t.historyStay}</strong>
|
||||||
<span>${formatDuration(Math.round(stay.duration))}</span>
|
<span>총 ${formatDuration(Math.round(stay.duration))}</span>
|
||||||
<span>${formatDateTime(stay.startTime)} - ${formatDateTime(stay.endTime)}</span>
|
${visitRows}
|
||||||
<span>${Number(stay.lat).toFixed(6)}, ${Number(stay.lng).toFixed(6)}</span>
|
<span>${Number(stay.lat).toFixed(6)}, ${Number(stay.lng).toFixed(6)}</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
Reference in New Issue
Block a user