import type { CurrentVesselPosition, ProcessedTrack } from '../model/track.types'; function calculateHeading(from: [number, number], to: [number, number]): number { const dx = to[0] - from[0]; const dy = to[1] - from[1]; let angle = (Math.atan2(dx, dy) * 180) / Math.PI; if (angle < 0) angle += 360; return angle; } function interpolate( from: [number, number], to: [number, number], fromTs: number, toTs: number, currentTs: number, ): [number, number] { if (toTs <= fromTs) return from; if (currentTs <= fromTs) return from; if (currentTs >= toTs) return to; const ratio = (currentTs - fromTs) / (toTs - fromTs); return [ from[0] + (to[0] - from[0]) * ratio, from[1] + (to[1] - from[1]) * ratio, ]; } export function getCurrentPosition(track: ProcessedTrack, currentTime: number): CurrentVesselPosition | null { const len = track.timestampsMs.length; if (len === 0 || track.geometry.length === 0) return null; const firstTime = track.timestampsMs[0]; const lastTime = track.timestampsMs[len - 1]; if (currentTime < firstTime || currentTime > lastTime) return null; if (len === 1) { return { vesselId: track.vesselId, targetId: track.targetId, sigSrcCd: track.sigSrcCd, shipName: track.shipName, shipKindCode: track.shipKindCode, nationalCode: track.nationalCode, position: track.geometry[0], heading: 0, speed: track.speeds[0] || 0, timestamp: firstTime, }; } let hi = len - 1; let lo = 0; while (lo <= hi) { const mid = Math.floor((lo + hi) / 2); if (track.timestampsMs[mid] <= currentTime) lo = mid + 1; else hi = mid - 1; } const idx = Math.max(0, Math.min(len - 2, hi)); const from = track.geometry[idx]; const to = track.geometry[idx + 1]; const fromTs = track.timestampsMs[idx]; const toTs = track.timestampsMs[idx + 1]; const position = interpolate(from, to, fromTs, toTs, currentTime); const heading = calculateHeading(from, to); const speed = track.speeds[idx] || 0; return { vesselId: track.vesselId, targetId: track.targetId, sigSrcCd: track.sigSrcCd, shipName: track.shipName, shipKindCode: track.shipKindCode, nationalCode: track.nationalCode, position, heading, speed, timestamp: currentTime, }; } export function getCurrentPositions(tracks: ProcessedTrack[], currentTime: number): CurrentVesselPosition[] { const out: CurrentVesselPosition[] = []; for (const track of tracks) { const pos = getCurrentPosition(track, currentTime); if (pos) out.push(pos); } return out; }