release: 2026-03-31 (39건 커밋) #210

병합
htlee develop 에서 main 로 39 commits 를 머지했습니다 2026-03-31 10:12:10 +09:00
3개의 변경된 파일33개의 추가작업 그리고 20개의 파일을 삭제
Showing only changes of commit 8eacbb2c91 - Show all commits

파일 보기

@ -412,7 +412,7 @@ export function useGearReplayLayers(
}
}
// 8. Operational polygons (per model — union of member positions + high-score correlation vessels)
// 8. Operational polygons (멤버 위치 + enabledVessels ON인 연관 선박으로 폴리곤 생성)
for (const [mn, items] of correlationByModel) {
if (!enabledModels.has(mn)) continue;
const color = MODEL_COLORS[mn] ?? '#94a3b8';
@ -420,13 +420,15 @@ export function useGearReplayLayers(
const extraPts: [number, number][] = [];
for (const c of items as GearCorrelationItem[]) {
if (c.score < 0.7) continue;
// enabledVessels로 개별 on/off 제어 (토글 대응)
if (!enabledVessels.has(c.targetMmsi)) continue;
const cp = corrPositions.find(p => p.mmsi === c.targetMmsi);
if (cp) extraPts.push([cp.lon, cp.lat]);
}
if (extraPts.length === 0) continue;
const opPolygon = buildInterpPolygon([...memberPts, ...extraPts]);
const basePts = enabledModels.has('identity') ? memberPts : [];
const opPolygon = buildInterpPolygon([...basePts, ...extraPts]);
if (!opPolygon) continue;
layers.push(new PolygonLayer({

파일 보기

@ -144,8 +144,9 @@ export interface CorrelationTrackPoint {
export interface CorrelationVesselTrack {
mmsi: string;
name: string;
type: string; // 'VESSEL' | 'GEAR'
score: number;
modelName: string;
models: Record<string, number>; // { modelName: score }
track: CorrelationTrackPoint[];
}

파일 보기

@ -78,8 +78,9 @@ def get_correlation_tracks(
):
"""Return correlated vessels with their track history for map rendering.
Queries gear_correlation_scores (default model) and enriches with
Queries gear_correlation_scores (ALL active models) and enriches with
24h track data from in-memory vessel_store.
Each vessel includes which models detected it.
"""
from cache.vessel_store import vessel_store
@ -87,7 +88,7 @@ def get_correlation_tracks(
conn = kcgdb.get_conn()
cur = conn.cursor()
# Get correlated vessels from default model
# Get correlated vessels from ALL active models
cur.execute("""
SELECT s.target_mmsi, s.target_type, s.target_name,
s.current_score, m.name AS model_name
@ -95,7 +96,6 @@ def get_correlation_tracks(
JOIN kcg.correlation_param_models m ON s.model_id = m.id
WHERE s.group_key = %s
AND s.current_score >= %s
AND m.is_default = TRUE
AND m.is_active = TRUE
ORDER BY s.current_score DESC
""", (group_key, min_score))
@ -107,31 +107,41 @@ def get_correlation_tracks(
if not rows:
return {'groupKey': group_key, 'vessels': []}
# Collect target MMSIs
vessel_info = []
mmsis = []
# Group by MMSI: collect all models per vessel, keep highest score
vessel_map: dict[str, dict] = {}
for row in rows:
vessel_info.append({
'mmsi': row[0],
'type': row[1],
'name': row[2] or '',
'score': float(row[3]),
'modelName': row[4],
})
mmsis.append(row[0])
mmsi = row[0]
model_name = row[4]
score = float(row[3])
if mmsi not in vessel_map:
vessel_map[mmsi] = {
'mmsi': mmsi,
'type': row[1],
'name': row[2] or '',
'score': score,
'models': {model_name: score},
}
else:
entry = vessel_map[mmsi]
entry['models'][model_name] = score
if score > entry['score']:
entry['score'] = score
mmsis = list(vessel_map.keys())
# Get tracks from vessel_store
tracks = vessel_store.get_vessel_tracks(mmsis, hours)
# Build response
vessels = []
for info in vessel_info:
for info in vessel_map.values():
track = tracks.get(info['mmsi'], [])
vessels.append({
'mmsi': info['mmsi'],
'name': info['name'],
'type': info['type'],
'score': info['score'],
'modelName': info['modelName'],
'models': info['models'], # {modelName: score, ...}
'track': track,
})