diff --git a/frontend/src/components/korea/FleetClusterLayer.tsx b/frontend/src/components/korea/FleetClusterLayer.tsx index 57fa7c2..920bc45 100644 --- a/frontend/src/components/korea/FleetClusterLayer.tsx +++ b/frontend/src/components/korea/FleetClusterLayer.tsx @@ -127,8 +127,8 @@ 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 MAX_DIST_DEG = 0.15; // ~10NM + const STALE_MS = 60 * 60_000; const now = Date.now(); const nameToShip = new Map(); @@ -139,26 +139,35 @@ export function FleetClusterLayer({ ships, analysisMap, clusters, onShipSelect, } } - const map = new Map(); + // 1단계: 같은 모선명 어구 수집 (60분 이내만) + const rawGroups = 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 arr = rawGroups.get(parentName) ?? []; + arr.push(s); + rawGroups.set(parentName, arr); + } + + // 2단계: 거리 기반 서브 클러스터링 (같은 이름이라도 멀면 분리) + const map = new Map(); + for (const [parentName, gears] of rawGroups) { 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 anchor = parent ?? gears[0]; + if (!anchor) continue; - const entry = map.get(parentName) ?? { parent, gears: [] }; - entry.gears.push(s); - map.set(parentName, entry); + const nearby = gears.filter(g => { + const dlat = Math.abs(g.lat - anchor.lat); + const dlng = Math.abs(g.lng - anchor.lng); + return dlat <= MAX_DIST_DEG && dlng <= MAX_DIST_DEG; + }); + + if (nearby.length === 0) continue; + map.set(parentName, { parent, gears: nearby }); } return map; }, [ships]);