From 98d173701e9537cf3a88d7ffdd0f70f7d372d29f Mon Sep 17 00:00:00 2001 From: htlee Date: Wed, 1 Apr 2026 14:22:59 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20=EC=96=B4?= =?UTF-8?q?=EA=B5=AC=20=ED=98=84=ED=99=A9=EC=97=90=EC=84=9C=20fallback=20?= =?UTF-8?q?=EA=B7=B8=EB=A3=B9=20=EC=A0=9C=EC=99=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 1h 실제 활성 멤버 < 2일 때 resolution='1h-fb' (fallback)로 저장 - LATEST_GROUPS_SQL은 resolution='1h'만 필터 → fallback 자동 제외 - 리플레이 history API는 1h-fb 포함 (리플레이/일치율 추적 유지) - 프론트 리플레이: '1h' + '1h-fb' 모두 1h 프레임으로 처리 Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/components/korea/FleetClusterLayer.tsx | 2 +- prediction/algorithms/polygon_builder.py | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/korea/FleetClusterLayer.tsx b/frontend/src/components/korea/FleetClusterLayer.tsx index a5d43c8..57f5a25 100644 --- a/frontend/src/components/korea/FleetClusterLayer.tsx +++ b/frontend/src/components/korea/FleetClusterLayer.tsx @@ -180,7 +180,7 @@ export function FleetClusterLayer({ ships, analysisMap: analysisMapProp, onShipS ]); // 2. resolution별 분리 → 1h(primary) + 6h(secondary) - const history1h = history.filter(h => h.resolution === '1h'); + const history1h = history.filter(h => h.resolution === '1h' || h.resolution === '1h-fb'); const history6h = history.filter(h => h.resolution === '6h'); // fallback: resolution 필드 없는 기존 데이터는 6h로 취급 const effective1h = history1h.length > 0 ? history1h : history; diff --git a/prediction/algorithms/polygon_builder.py b/prediction/algorithms/polygon_builder.py index 5d0be30..2520806 100644 --- a/prediction/algorithms/polygon_builder.py +++ b/prediction/algorithms/polygon_builder.py @@ -423,12 +423,16 @@ def build_all_group_snapshots( continue # ── 1h 활성 멤버 필터 ── - display_members_1h = [ + active_members_1h = [ gm for gm in gear_members if _get_time_bucket_age(gm.get('mmsi'), all_positions, now) <= DISPLAY_STALE_SEC ] - # fallback: 1h < 2이면 time_bucket 최신 2개 유지 (폴리곤 형태 보존) - if len(display_members_1h) < 2 and len(gear_members) >= 2: + active_count_1h = len(active_members_1h) + + # fallback: 1h < 2이면 time_bucket 최신 2개 유지 (리플레이/일치율 추적용) + # 라이브 현황에서는 active_count_1h로 필터 (fallback 그룹 제외) + display_members_1h = active_members_1h + if active_count_1h < 2 and len(gear_members) >= 2: sorted_by_age = sorted( gear_members, key=lambda gm: _get_time_bucket_age(gm.get('mmsi'), all_positions, now), @@ -443,7 +447,9 @@ def build_all_group_snapshots( display_members_6h = gear_members # ── resolution별 스냅샷 생성 ── - for resolution, members_for_snap in [('1h', display_members_1h), ('6h', display_members_6h)]: + # 1h-fb: fallback (실제 1h 활성 < 2) — 리플레이/일치율 추적용, 라이브 현황에서 제외 + res_1h = '1h' if active_count_1h >= 2 else '1h-fb' + for resolution, members_for_snap in [(res_1h, display_members_1h), ('6h', display_members_6h)]: if len(members_for_snap) < 2: continue # 6h: 최신 적재가 STALE_SEC(6h) 초과 시 스킵