- Backend: mmsi별 최신 1건만 반환 (중복 제거) - 항적: signal-batch tracks API 호출 (6시간, 5분 캐시) - 범례: 위험도 점수 기준 상세 (위치/조업/AIS/허가, 0~100) - 선박 목록: maxHeight 300px 스크롤 가능 - 선박 클릭 → flyTo + 항적 표시 + 근거 상세 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
40 lines
1.2 KiB
TypeScript
40 lines
1.2 KiB
TypeScript
const SIGNAL_BATCH_BASE = '/signal-batch';
|
|
|
|
interface TrackResponse {
|
|
vesselId: string;
|
|
geometry: [number, number][];
|
|
speeds: number[];
|
|
timestamps: string[];
|
|
pointCount: number;
|
|
totalDistance: number;
|
|
shipName: string;
|
|
}
|
|
|
|
// mmsi별 캐시 (TTL 5분)
|
|
const trackCache = new Map<string, { time: number; coords: [number, number][] }>();
|
|
const CACHE_TTL = 5 * 60_000;
|
|
|
|
export async function fetchVesselTrack(mmsi: string, hours: number = 6): Promise<[number, number][]> {
|
|
const cached = trackCache.get(mmsi);
|
|
if (cached && Date.now() - cached.time < CACHE_TTL) return cached.coords;
|
|
|
|
const endTime = new Date().toISOString();
|
|
const startTime = new Date(Date.now() - hours * 3600_000).toISOString();
|
|
|
|
try {
|
|
const res = await fetch(`${SIGNAL_BATCH_BASE}/api/v2/tracks/vessels`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', accept: 'application/json' },
|
|
body: JSON.stringify({ startTime, endTime, vessels: [mmsi] }),
|
|
});
|
|
if (!res.ok) return [];
|
|
const data: TrackResponse[] = await res.json();
|
|
if (!data.length || !data[0].geometry?.length) return [];
|
|
const coords = data[0].geometry;
|
|
trackCache.set(mmsi, { time: Date.now(), coords });
|
|
return coords;
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|