Unwrap great-circle arc longitudes across the antimeridian so routes don't chord across the map (ISS-120)
CI / build + unit + e2e (pull_request) Successful in 10m58s
CI / build + unit + e2e (pull_request) Successful in 10m58s
This commit is contained in:
+24
-3
@@ -74,16 +74,34 @@ export interface AirportCoord {
|
||||
// curved path the plane actually flies (same slerp as lib/sim/movement.ts).
|
||||
export const ROUTE_ARC_SEGMENTS = 24;
|
||||
|
||||
// ISS-120 (v0.53.0): positionAt wraps each sample's longitude into [−180, 180], so an arc that
|
||||
// crosses the antimeridian jumps +179.x → −179.x between consecutive samples — and MapLibre draws
|
||||
// LineStrings as-is (no ±180 splitting), rendering a chord across the whole map. Unwrap the sample
|
||||
// sequence into continuous longitudes: keep the first sample's raw lon, then shift each subsequent
|
||||
// one by ±360 relative to the previous UNWRAPPED value until |Δlon| ≤ 180. One line stays one line;
|
||||
// out-of-range values render on MapLibre's adjacent world copies (renderWorldCopies is default-on).
|
||||
function unwrapLons(coords: [number, number][]): [number, number][] {
|
||||
let prev = coords[0]![0];
|
||||
for (let i = 1; i < coords.length; i++) {
|
||||
let lon = coords[i]![0];
|
||||
while (lon - prev > 180) lon -= 360;
|
||||
while (prev - lon > 180) lon += 360;
|
||||
coords[i]![0] = lon;
|
||||
prev = lon;
|
||||
}
|
||||
return coords;
|
||||
}
|
||||
|
||||
// Great-circle arc between two airports as [lon, lat] coordinates: positionAt at
|
||||
// t = i/SEGMENTS for i in 0..SEGMENTS. Pure — endpoints are exactly the airport
|
||||
// coords (positionAt returns them verbatim at t=0 and t=1).
|
||||
// t = i/SEGMENTS for i in 0..SEGMENTS. Pure — the first sample is exactly `from` and the last is
|
||||
// ≡ `to` (mod 360) (positionAt returns them verbatim at t=0 and t=1, unwrapped relative to from).
|
||||
export function greatCircleArc(from: AirportCoord, to: AirportCoord): [number, number][] {
|
||||
const coords: [number, number][] = [];
|
||||
for (let i = 0; i <= ROUTE_ARC_SEGMENTS; i++) {
|
||||
const pos = positionAt(from.lat, from.lon, to.lat, to.lon, i / ROUTE_ARC_SEGMENTS);
|
||||
coords.push([pos.lon, pos.lat]);
|
||||
}
|
||||
return coords;
|
||||
return unwrapLons(coords);
|
||||
}
|
||||
|
||||
// ISS-022/025/027 (v0.22.0): the hover route trace — one great-circle LineString between two
|
||||
@@ -195,6 +213,9 @@ export function queuedRoutesGeoJson(
|
||||
const arc = greatCircleArc(pts[i]!, pts[i + 1]!);
|
||||
coords.push(...(i === 0 ? arc : arc.slice(1))); // drop the duplicated joint point
|
||||
}
|
||||
// ISS-120: each leg's arc is unwrapped relative to its OWN start airport, so at a joint the
|
||||
// sequence can jump ±360 (leg 1 ends at dest+360k, leg 2 restarts at the raw dest) — re-unify.
|
||||
unwrapLons(coords);
|
||||
features.push({
|
||||
type: 'Feature',
|
||||
geometry: { type: 'LineString', coordinates: coords },
|
||||
|
||||
Reference in New Issue
Block a user