optimized events
Test and publish container images / test (push) Successful in 2m26s
Test and publish container images / publish (push) Failing after 3s

This commit is contained in:
BuTzZ
2026-07-28 14:36:47 +02:00
parent b98ec8bc6f
commit 593dbd5f85
42 changed files with 2405 additions and 283 deletions
+49 -2
View File
@@ -1,4 +1,5 @@
import {
haversineDistanceNm,
orderWaypointsAlongRoute,
type Coordinate,
type RouteResult,
@@ -12,11 +13,13 @@ export type RouteEventKind = "harbour" | "lock" | "bridge";
export type RouteEventCorridors = Record<RouteEventKind, number>;
export const DEFAULT_ROUTE_EVENT_CORRIDORS_NM: Readonly<RouteEventCorridors> = Object.freeze({
harbour: 1.5,
harbour: 0.5,
lock: 0.25,
bridge: 0.08
});
const CROSS_KIND_DUPLICATE_DISTANCE_NM = 0.1;
export type RouteEventEtaSpeedSource = "gps-sog" | "vessel-cruise-speed";
export type RouteEventEtaReferenceSource = "current-time" | "route-departure";
@@ -148,7 +151,7 @@ export function upcomingRouteEvents(
input.route
);
return projected.flatMap<UpcomingRouteEvent>((projection) => {
const events = projected.flatMap<UpcomingRouteEvent>((projection) => {
const candidate = candidatesByProjectionId.get(projection.id);
if (
!candidate ||
@@ -179,6 +182,8 @@ export function upcomingRouteEvents(
return [{ ...common, kind: candidate.kind, feature: candidate.feature }];
}
});
return withoutLockHarbourDuplicates(events);
}
export function nextRouteEventsByKind(
@@ -320,3 +325,45 @@ function timestampValue(value: string | number | Date): number | null {
: Date.parse(value);
return Number.isFinite(timestamp) ? timestamp : null;
}
/**
* Some source objects are classified both as a lock and as a harbour. Preserve
* the operationally more specific lock event only when the normalized names
* match and both source coordinates clearly describe the same place.
*/
function withoutLockHarbourDuplicates(
events: UpcomingRouteEvent[]
): UpcomingRouteEvent[] {
const locksByName = new Map<string, LockRouteEvent[]>();
for (const event of events) {
if (event.kind !== "lock") {
continue;
}
const name = normalizedFacilityName(event.name);
if (!name) {
continue;
}
locksByName.set(name, [...(locksByName.get(name) ?? []), event]);
}
return events.filter((event) => {
if (event.kind !== "harbour") {
return true;
}
const possibleLocks = locksByName.get(normalizedFacilityName(event.name)) ?? [];
return !possibleLocks.some(
(lock) =>
haversineDistanceNm(lock.coordinate, event.coordinate) <=
CROSS_KIND_DUPLICATE_DISTANCE_NM
);
});
}
function normalizedFacilityName(value: string) {
return value
.normalize("NFKD")
.replace(/\p{Diacritic}/gu, "")
.toLocaleLowerCase("de-DE")
.replace(/[^\p{Letter}\p{Number}]+/gu, " ")
.trim();
}