feat(prediction): vessel_analysis_results 에 분석 시점 lat/lon 저장
AnalysisResult 에 lat/lon 필드 + to_db_tuple 반영 + upsert_results SQL 컬럼 추가. 분류 파이프라인(last_row) / 경량 분석(all_positions) 두 경로 모두 분석 시점의 선박 위치를 함께 기록해 프론트 미니맵에서 특이운항 판별 위치를 실제 항적 위에 표시할 수 있게 한다. 배포 후 첫 사이클 8173/8173 lat/lon non-null 확인.
This commit is contained in:
부모
da4557a5df
커밋
14eb4c7ea3
@ -72,7 +72,7 @@ def upsert_results(results: list['AnalysisResult']) -> int:
|
||||
insert_sql = """
|
||||
INSERT INTO vessel_analysis_results (
|
||||
mmsi, analyzed_at, vessel_type, confidence, fishing_pct,
|
||||
cluster_id, season, zone_code, dist_to_baseline_nm, activity_state,
|
||||
cluster_id, season, lat, lon, zone_code, dist_to_baseline_nm, activity_state,
|
||||
ucaf_score, ucft_score, is_dark, gap_duration_min,
|
||||
spoofing_score, bd09_offset_m, speed_jump_count,
|
||||
fleet_cluster_id, fleet_is_leader, fleet_role,
|
||||
@ -88,6 +88,8 @@ def upsert_results(results: list['AnalysisResult']) -> int:
|
||||
fishing_pct = EXCLUDED.fishing_pct,
|
||||
cluster_id = EXCLUDED.cluster_id,
|
||||
season = EXCLUDED.season,
|
||||
lat = EXCLUDED.lat,
|
||||
lon = EXCLUDED.lon,
|
||||
zone_code = EXCLUDED.zone_code,
|
||||
dist_to_baseline_nm = EXCLUDED.dist_to_baseline_nm,
|
||||
activity_state = EXCLUDED.activity_state,
|
||||
|
||||
@ -20,6 +20,8 @@ class AnalysisResult:
|
||||
# ALGO 01: 위치
|
||||
zone: str = 'EEZ_OR_BEYOND'
|
||||
dist_to_baseline_nm: float = 999.0
|
||||
lat: Optional[float] = None
|
||||
lon: Optional[float] = None
|
||||
|
||||
# ALGO 02: 활동 상태
|
||||
activity_state: str = 'UNKNOWN'
|
||||
@ -95,6 +97,10 @@ class AnalysisResult:
|
||||
|
||||
safe_features = _sanitize(self.features) if self.features else {}
|
||||
|
||||
def _opt_f(v: object) -> Optional[float]:
|
||||
"""Optional float (None 유지)."""
|
||||
return float(v) if v is not None else None
|
||||
|
||||
return (
|
||||
str(self.mmsi),
|
||||
self.analyzed_at, # analyzed_at (PK 파티션키)
|
||||
@ -103,6 +109,8 @@ class AnalysisResult:
|
||||
_f(self.fishing_pct),
|
||||
_i(self.cluster_id),
|
||||
str(self.season),
|
||||
_opt_f(self.lat),
|
||||
_opt_f(self.lon),
|
||||
str(self.zone), # → zone_code
|
||||
_f(self.dist_to_baseline_nm),
|
||||
str(self.activity_state),
|
||||
|
||||
@ -466,6 +466,9 @@ def run_analysis_cycle():
|
||||
'registered_fishery_code': registered_fishery_code or '',
|
||||
}
|
||||
|
||||
# 분석 시점의 선박 위치 — 특이운항 판별 근거 좌표로 DB에 저장
|
||||
lat_val = last_row.get('lat')
|
||||
lon_val = last_row.get('lon')
|
||||
results.append(AnalysisResult(
|
||||
mmsi=mmsi,
|
||||
timestamp=ts,
|
||||
@ -474,6 +477,8 @@ def run_analysis_cycle():
|
||||
fishing_pct=c['fishing_pct'],
|
||||
cluster_id=fleet_info.get('cluster_id', -1),
|
||||
season=c['season'],
|
||||
lat=float(lat_val) if lat_val is not None else None,
|
||||
lon=float(lon_val) if lon_val is not None else None,
|
||||
zone=zone_info.get('zone', 'EEZ_OR_BEYOND'),
|
||||
dist_to_baseline_nm=zone_info.get('dist_from_baseline_nm', 999.0),
|
||||
activity_state=activity,
|
||||
@ -615,6 +620,8 @@ def run_analysis_cycle():
|
||||
vessel_type='UNKNOWN',
|
||||
confidence=0.0,
|
||||
fishing_pct=0.0,
|
||||
lat=float(lat) if lat is not None else None,
|
||||
lon=float(lon) if lon is not None else None,
|
||||
zone=zone_info.get('zone', 'EEZ_OR_BEYOND'),
|
||||
dist_to_baseline_nm=zone_info.get('dist_from_baseline_nm', 999.0),
|
||||
activity_state=state,
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user