92 lines
2.6 KiB
TypeScript
92 lines
2.6 KiB
TypeScript
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;
|
|
}
|