From befcd122778a81359bc11d9673da401402d3619f Mon Sep 17 00:00:00 2001 From: htlee Date: Fri, 20 Mar 2026 18:50:12 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=B9=84=ED=97=88=EA=B0=80=20=EC=96=B4?= =?UTF-8?q?=EA=B5=AC=20=EA=B7=B8=EB=A3=B9=ED=95=91=EC=97=90=20=EA=B1=B0?= =?UTF-8?q?=EB=A6=AC=EC=A0=9C=ED=95=9C(10NM)=20+=20=EC=88=98=EC=8B=A0?= =?UTF-8?q?=EC=8B=9C=EA=B0=81(60=EB=B6=84)=20=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- .../components/korea/AnalysisStatsPanel.tsx | 3 +++ .../components/korea/FleetClusterLayer.tsx | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/korea/AnalysisStatsPanel.tsx b/frontend/src/components/korea/AnalysisStatsPanel.tsx index 63b1508..dd8b27f 100644 --- a/frontend/src/components/korea/AnalysisStatsPanel.tsx +++ b/frontend/src/components/korea/AnalysisStatsPanel.tsx @@ -82,8 +82,11 @@ export function AnalysisStatsPanel({ stats, lastUpdated, isLoading, analysisMap, const gearStats = useMemo(() => { const source = allShips ?? ships; const gearPattern = /^(.+?)_\d+_\d+_?$/; + const STALE_MS = 60 * 60_000; // 60분 이내만 + const now = Date.now(); const parentMap = new Map(); for (const s of source) { + if (now - s.lastSeen > STALE_MS) continue; const m = (s.name || '').match(gearPattern); if (m) { const parent = m[1].trim(); diff --git a/frontend/src/components/korea/FleetClusterLayer.tsx b/frontend/src/components/korea/FleetClusterLayer.tsx index 2365f95..57fa7c2 100644 --- a/frontend/src/components/korea/FleetClusterLayer.tsx +++ b/frontend/src/components/korea/FleetClusterLayer.tsx @@ -127,6 +127,10 @@ export function FleetClusterLayer({ ships, analysisMap, clusters, onShipSelect, // 비허가 어구 클러스터: parentName → { parent: Ship | null, gears: Ship[] } const gearGroupMap = useMemo(() => { const gearPattern = /^(.+?)_\d+_\d+_?$/; + const MAX_DIST_DEG = 0.15; // ~10NM — 모선과 어구 간 최대 거리 + const STALE_MS = 60 * 60_000; // 60분 이내 수신 신호만 + const now = Date.now(); + const nameToShip = new Map(); for (const s of ships) { const nm = (s.name || '').trim(); @@ -134,12 +138,25 @@ export function FleetClusterLayer({ ships, analysisMap, clusters, onShipSelect, nameToShip.set(nm, s); } } + const map = new Map(); for (const s of ships) { + // 60분 이내 수신 신호만 + if (now - s.lastSeen > STALE_MS) continue; + const m = (s.name || '').match(gearPattern); if (!m) continue; const parentName = m[1].trim(); - const entry = map.get(parentName) ?? { parent: nameToShip.get(parentName) ?? null, gears: [] }; + const parent = nameToShip.get(parentName) ?? null; + + // 모선이 있으면 거리 제한 적용 + if (parent) { + const dlat = Math.abs(s.lat - parent.lat); + const dlng = Math.abs(s.lng - parent.lng); + if (dlat > MAX_DIST_DEG || dlng > MAX_DIST_DEG) continue; + } + + const entry = map.get(parentName) ?? { parent, gears: [] }; entry.gears.push(s); map.set(parentName, entry); } -- 2.45.2