optimized events
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user