@squawk/flightplan 0.6.3 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -33,7 +33,7 @@ const resolver = createFlightplanResolver({
33
33
  procedures: createProcedureResolver({ data: usBundledProcedures.records }),
34
34
  });
35
35
 
36
- const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
36
+ const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
37
37
  for (const element of route.elements) {
38
38
  console.log(element.type, element.raw);
39
39
  }
@@ -122,13 +122,19 @@ Each element has a `type` discriminant and a `raw` field with the original token
122
122
  | `airport` | Resolved airport (ICAO or FAA ID) | `airport` |
123
123
  | `sid` | Standard Instrument Departure | `procedure`, `legs` |
124
124
  | `star` | Standard Terminal Arrival Route | `procedure`, `legs` |
125
- | `airway` | Airway segment between entry and exit fix | `airway`, `entryFix`, `exitFix`, `waypoints` |
125
+ | `airway` | Airway segment between entry and exit fix | `airway`, `entryFix`, `exitFix`, `waypoints`, `gaps` |
126
126
  | `direct` | DCT (direct) indicator | - |
127
127
  | `waypoint` | Resolved fix or navaid | `fix` and/or `navaid`, `lat`, `lon` |
128
128
  | `coordinate` | Lat/lon specified in the route string | `lat`, `lon` |
129
129
  | `speedAltitude` | Speed/altitude group (e.g. N0450F350) | `speedKt`/`speedKmPerHr`/`mach`, `flightLevel`/`altitudeFt` |
130
130
  | `unresolved` | Token that could not be resolved | - |
131
131
 
132
+ An airway element's `gaps` lists any published discontinuity its expansion
133
+ crosses (the FAA discontinues some airways partway, such as J60 between JOT and
134
+ DJB). The waypoints on both sides are still returned so the route can be drawn
135
+ and measured, but a non-empty `gaps` means the route files the airway where it
136
+ is not defined and is not valid as filed.
137
+
132
138
  ### Coordinate formats
133
139
 
134
140
  - `DDMMN/DDDMMEW` (e.g. `4030N07045W` for 40 deg 30 min N, 70 deg 45 min W)
@@ -173,13 +179,13 @@ to the first `byIdent` match.
173
179
  Computes the total great-circle route distance and optional estimated time
174
180
  enroute from a parsed route. Walks the same ordered point sequence as the
175
181
  geometry helpers below, so distance and geometry stay in agreement. Airway
176
- segments use FAA-published per-segment distances when available, falling back
177
- to great-circle computation.
182
+ segments use the FAA-published per-segment distance where the data carries a
183
+ positive one, falling back to great-circle computation otherwise.
178
184
 
179
185
  ```typescript
180
186
  import { computeRouteDistance } from '@squawk/flightplan';
181
187
 
182
- const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
188
+ const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
183
189
  const result = computeRouteDistance(route, 450);
184
190
  console.log(result.totalDistanceNm, result.estimatedTimeEnrouteHrs);
185
191
  ```
@@ -191,7 +197,7 @@ console.log(result.totalDistanceNm, result.estimatedTimeEnrouteHrs);
191
197
 
192
198
  **Returns:** `RouteDistanceResult` with:
193
199
 
194
- - `legs` - ordered `RouteLeg` values, each with `from`/`to` point labels, their `fromLat`/`fromLon`/`toLat`/`toLon` coordinates in decimal degrees, `distanceNm`, and `cumulativeDistanceNm`
200
+ - `legs` - ordered `RouteLeg` values, each with `from`/`to` point labels, their `fromLat`/`fromLon`/`toLat`/`toLon` coordinates in decimal degrees, `distanceNm`, `cumulativeDistanceNm`, and `crossesGap`, which is true when the leg spans a published gap in the airway it came from (the leg is a straight line that counts toward the total but is not a flyable airway segment)
195
201
  - `totalDistanceNm` - total great-circle distance in nautical miles
196
202
  - `estimatedTimeEnrouteHrs` - ETE in hours, or `undefined` when no ground speed was given
197
203
  - `unresolvedElements` - route elements that could not contribute coordinates
@@ -216,7 +222,7 @@ wind correction angle is zero.
216
222
  ```typescript
217
223
  import { computeRouteTiming } from '@squawk/flightplan';
218
224
 
219
- const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
225
+ const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
220
226
  const result = computeRouteTiming(route, {
221
227
  trueAirspeedKt: 450,
222
228
  windProvider: (lat, lon) => ({ directionDeg: 270, speedKt: 80 }),
@@ -269,7 +275,7 @@ two drawable points, since a `LineString` requires at least two positions.
269
275
  ```typescript
270
276
  import { routeToLineString } from '@squawk/flightplan';
271
277
 
272
- const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
278
+ const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
273
279
  const line = routeToLineString(route);
274
280
  if (line) {
275
281
  map.addSource('route', { type: 'geojson', data: line });
@@ -1,4 +1,4 @@
1
- import type { Airport, Airway, AirwayWaypoint, Fix, Navaid, Procedure, ProcedureLeg } from '@squawk/types';
1
+ import type { Airport, Airway, AirwayGap, AirwayWaypoint, Fix, Navaid, Procedure, ProcedureLeg } from '@squawk/types';
2
2
  /**
3
3
  * Minimal airport lookup interface consumed by the flightplan resolver.
4
4
  * Structurally compatible with the `AirportResolver` shape in `@squawk/airports`.
@@ -46,10 +46,16 @@ export interface FlightplanFixLookup {
46
46
  export interface FlightplanAirwayLookup {
47
47
  /** Looks up airways by designation (e.g. "V16", "J60"). Returns an empty array if none found. */
48
48
  byDesignation(designation: string): Airway[];
49
- /** Expands an airway between entry and exit fixes, returning ordered waypoints. */
49
+ /**
50
+ * Expands an airway between entry and exit fixes, returning ordered
51
+ * waypoints. `gaps` lists any published discontinuity the expansion crosses;
52
+ * a lookup that cannot report gaps may omit it, and the route then reports
53
+ * none.
54
+ */
50
55
  expand(designation: string, entryFix: string, exitFix: string): {
51
56
  airway: Airway;
52
57
  waypoints: AirwayWaypoint[];
58
+ gaps?: AirwayGap[];
53
59
  } | undefined;
54
60
  }
55
61
  /**
@@ -118,6 +124,14 @@ export interface AirwayRouteElement {
118
124
  exitFix: string;
119
125
  /** Ordered waypoints from entry to exit fix (inclusive). */
120
126
  waypoints: AirwayWaypoint[];
127
+ /**
128
+ * Published discontinuities the expansion crosses, in traversal order. Empty
129
+ * when the airway is continuous between the entry and exit fixes. A
130
+ * non-empty list means the route files the airway where it is not defined,
131
+ * so the route is not valid as filed; the waypoints are still returned so
132
+ * the route can be drawn and measured.
133
+ */
134
+ gaps: AirwayGap[];
121
135
  }
122
136
  /**
123
137
  * A DCT (direct) indicator between two waypoints.
@@ -263,7 +277,7 @@ export interface FlightplanResolver {
263
277
  * procedures: createProcedureResolver({ data: usBundledProcedures.records }),
264
278
  * });
265
279
  *
266
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
280
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
267
281
  * for (const element of route.elements) {
268
282
  * console.log(element.type, element.raw);
269
283
  * }
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
package/dist/resolver.js CHANGED
@@ -104,8 +104,8 @@ function tryAirport(token, airports) {
104
104
  * the arrival airport - but since the route is parsed left-to-right,
105
105
  * the most recent airport is the best available hint).
106
106
  *
107
- * Accepts both the bare CIFP identifier (e.g. `NUBLE4`) and the dotted
108
- * identifier-with-transition form (e.g. `NUBLE4.JJIMY`). When a
107
+ * Accepts both the bare CIFP identifier (e.g. `DEEZZ6`) and the dotted
108
+ * identifier-with-transition form (e.g. `DEEZZ6.CANDR`). When a
109
109
  * transition is supplied it is forwarded to the procedure resolver so
110
110
  * that the transition's legs are merged into the expansion.
111
111
  */
@@ -211,7 +211,7 @@ function tryNavaid(token, navaids, anchor) {
211
211
  * procedures: createProcedureResolver({ data: usBundledProcedures.records }),
212
212
  * });
213
213
  *
214
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
214
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
215
215
  * for (const element of route.elements) {
216
216
  * console.log(element.type, element.raw);
217
217
  * }
@@ -269,6 +269,7 @@ export function createFlightplanResolver(options) {
269
269
  entryFix: lastWaypointIdent,
270
270
  exitFix,
271
271
  waypoints: expansion.waypoints,
272
+ gaps: expansion.gaps ?? [],
272
273
  });
273
274
  // The exit fix is consumed as part of the airway; update last waypoint
274
275
  lastWaypointIdent = exitFix;
@@ -24,6 +24,13 @@ export interface RouteLeg {
24
24
  distanceNm: number;
25
25
  /** Cumulative distance from the route start through the end of this leg in nautical miles. */
26
26
  cumulativeDistanceNm: number;
27
+ /**
28
+ * Whether this leg spans a published gap in the airway it was expanded
29
+ * from. The airway is not defined between the two points, so the leg is a
30
+ * straight line that counts toward the total but is not a flyable airway
31
+ * segment, and the route is not valid as filed.
32
+ */
33
+ crossesGap: boolean;
27
34
  }
28
35
  /**
29
36
  * Result of computing route distance and estimated time enroute from a
@@ -55,14 +62,17 @@ export interface RouteDistanceResult {
55
62
  * indicate which parts of the route could not contribute to the distance
56
63
  * calculation.
57
64
  *
58
- * Airway segments use the FAA-published `distanceToNextNm` values when
59
- * available, falling back to great-circle computation otherwise.
65
+ * Airway segments use the FAA-published `distanceToNextNm` value where the
66
+ * source publishes a positive one, falling back to great-circle computation
67
+ * otherwise. A leg that spans a published gap in its airway is still measured
68
+ * as a straight line and counted, but is flagged with `crossesGap` so the
69
+ * caller can see that the route is not valid as filed.
60
70
  *
61
71
  * ```typescript
62
72
  * import { createFlightplanResolver, computeRouteDistance } from '@squawk/flightplan';
63
73
  *
64
74
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
65
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
75
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
66
76
  * const result = computeRouteDistance(route, 450);
67
77
  * console.log(result.totalDistanceNm, result.estimatedTimeEnrouteHrs);
68
78
  * ```
@@ -1 +1 @@
1
- {"version":3,"file":"route-distance.d.ts","sourceRoot":"","sources":["../src/route-distance.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAO/D;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB,uFAAuF;IACvF,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;;OAIG;IACH,kBAAkB,EAAE,YAAY,EAAE,CAAC;CACpC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,mBAAmB,CAsCrB"}
1
+ {"version":3,"file":"route-distance.d.ts","sourceRoot":"","sources":["../src/route-distance.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAO/D;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,0DAA0D;IAC1D,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB,uFAAuF;IACvF,uBAAuB,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C;;;;OAIG;IACH,kBAAkB,EAAE,YAAY,EAAE,CAAC;CACpC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,mBAAmB,CAuCrB"}
@@ -20,14 +20,17 @@ import { extractGeoPoints } from './route-geometry.js';
20
20
  * indicate which parts of the route could not contribute to the distance
21
21
  * calculation.
22
22
  *
23
- * Airway segments use the FAA-published `distanceToNextNm` values when
24
- * available, falling back to great-circle computation otherwise.
23
+ * Airway segments use the FAA-published `distanceToNextNm` value where the
24
+ * source publishes a positive one, falling back to great-circle computation
25
+ * otherwise. A leg that spans a published gap in its airway is still measured
26
+ * as a straight line and counted, but is flagged with `crossesGap` so the
27
+ * caller can see that the route is not valid as filed.
25
28
  *
26
29
  * ```typescript
27
30
  * import { createFlightplanResolver, computeRouteDistance } from '@squawk/flightplan';
28
31
  *
29
32
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
30
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
33
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
31
34
  * const result = computeRouteDistance(route, 450);
32
35
  * console.log(result.totalDistanceNm, result.estimatedTimeEnrouteHrs);
33
36
  * ```
@@ -57,6 +60,7 @@ export function computeRouteDistance(route, groundSpeedKt) {
57
60
  toLon: to.lon,
58
61
  distanceNm: legDistanceNm,
59
62
  cumulativeDistanceNm: totalDistanceNm,
63
+ crossesGap: from.gapToNext === true,
60
64
  });
61
65
  }
62
66
  const estimatedTimeEnrouteHrs = groundSpeedKt !== undefined && groundSpeedKt > 0 ? totalDistanceNm / groundSpeedKt : undefined;
@@ -37,14 +37,19 @@ export interface GeoPoint {
37
37
  lon: number;
38
38
  /**
39
39
  * Pre-computed distance to the next point along an airway segment in
40
- * nautical miles, if available from the source data.
40
+ * nautical miles, when the source data publishes a positive value.
41
41
  */
42
42
  precomputedDistanceToNextNm?: number;
43
+ /**
44
+ * Set when the leg from this point to the next crosses a published gap in
45
+ * the airway it was expanded from.
46
+ */
47
+ gapToNext?: true;
43
48
  }
44
49
  /**
45
50
  * Walks the route elements and extracts an ordered array of geographic
46
- * points. Duplicate consecutive points (e.g. an airway entry fix that
47
- * matches the preceding waypoint) are suppressed.
51
+ * points. Duplicate consecutive points (e.g. an airway entry fix at the same
52
+ * position as the preceding waypoint, within about a metre) are suppressed.
48
53
  *
49
54
  * Also collects all `unresolved` elements encountered during the walk.
50
55
  *
@@ -74,7 +79,7 @@ export declare function extractGeoPoints(elements: RouteElement[]): {
74
79
  * import { createFlightplanResolver, extractRoutePoints } from '@squawk/flightplan';
75
80
  *
76
81
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
77
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
82
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
78
83
  * const points = extractRoutePoints(route);
79
84
  * ```
80
85
  *
@@ -94,7 +99,7 @@ export declare function extractRoutePoints(route: ParsedRoute): RoutePoint[];
94
99
  * import { createFlightplanResolver, routeToLineString } from '@squawk/flightplan';
95
100
  *
96
101
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
97
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
102
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
98
103
  * const line = routeToLineString(route);
99
104
  * if (line) {
100
105
  * map.addSource('route', { type: 'geojson', data: line });
@@ -1 +1 @@
1
- {"version":3,"file":"route-geometry.d.ts","sourceRoot":"","sources":["../src/route-geometry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAM/D;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;CACb;AAMD;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;CACtC;AAgBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG;IAC1D,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,kBAAkB,EAAE,YAAY,EAAE,CAAC;CACpC,CA6EA;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,EAAE,CAGnE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,CAS5E"}
1
+ {"version":3,"file":"route-geometry.d.ts","sourceRoot":"","sources":["../src/route-geometry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAM/D;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;CACb;AAMD;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC;;;OAGG;IACH,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AAuBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG;IAC1D,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,kBAAkB,EAAE,YAAY,EAAE,CAAC;CACpC,CAwFA;AAMD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,EAAE,CAGnE;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,SAAS,CAS5E"}
@@ -11,8 +11,15 @@
11
11
  // ---------------------------------------------------------------------------
12
12
  // Internal helpers
13
13
  // ---------------------------------------------------------------------------
14
- /** Epsilon for comparing coordinates to detect duplicate points. */
15
- const COORD_EPSILON = 1e-9;
14
+ /**
15
+ * Epsilon in decimal degrees for treating two consecutive points as the same
16
+ * position. The data packages round coordinates to different precisions
17
+ * (airway waypoints to six decimals, fixes and navaids to eight), so the same
18
+ * fix reached through two of them can differ by up to 5e-7 degrees. 1e-5
19
+ * degrees is about a metre: far above that rounding noise and far below the
20
+ * spacing of any two distinct route points.
21
+ */
22
+ const COORD_EPSILON = 1e-5;
16
23
  /**
17
24
  * Returns true if two points share the same coordinates (within epsilon).
18
25
  */
@@ -21,8 +28,8 @@ function samePosition(a, b) {
21
28
  }
22
29
  /**
23
30
  * Walks the route elements and extracts an ordered array of geographic
24
- * points. Duplicate consecutive points (e.g. an airway entry fix that
25
- * matches the preceding waypoint) are suppressed.
31
+ * points. Duplicate consecutive points (e.g. an airway entry fix at the same
32
+ * position as the preceding waypoint, within about a metre) are suppressed.
26
33
  *
27
34
  * Also collects all `unresolved` elements encountered during the walk.
28
35
  *
@@ -46,6 +53,11 @@ export function extractGeoPoints(elements) {
46
53
  last.precomputedDistanceToNextNm === undefined) {
47
54
  last.precomputedDistanceToNextNm = point.precomputedDistanceToNextNm;
48
55
  }
56
+ // Likewise carry a gap marker, so the leg out of the merged point is
57
+ // still reported as crossing the gap.
58
+ if (point.gapToNext) {
59
+ last.gapToNext = true;
60
+ }
49
61
  return;
50
62
  }
51
63
  points.push(point);
@@ -61,7 +73,8 @@ export function extractGeoPoints(elements) {
61
73
  case 'coordinate':
62
74
  emit({ label: el.raw, lat: el.lat, lon: el.lon });
63
75
  break;
64
- case 'airway':
76
+ case 'airway': {
77
+ const gapIndexes = new Set(el.gaps.map((gap) => gap.index));
65
78
  for (let i = 0; i < el.waypoints.length; i++) {
66
79
  const wp = el.waypoints[i];
67
80
  const isLast = i === el.waypoints.length - 1;
@@ -70,14 +83,19 @@ export function extractGeoPoints(elements) {
70
83
  lat: wp.lat,
71
84
  lon: wp.lon,
72
85
  };
73
- // Only carry precomputed distance for non-last waypoints (the
74
- // last waypoint's distanceToNextNm points beyond this segment).
75
- if (!isLast && wp.distanceToNextNm !== undefined) {
86
+ // Only carry a published distance for non-last waypoints (the last
87
+ // waypoint's distanceToNextNm points beyond this segment), and only
88
+ // a positive one: a zero means the source published no distance.
89
+ if (!isLast && wp.distanceToNextNm !== undefined && wp.distanceToNextNm > 0) {
76
90
  point.precomputedDistanceToNextNm = wp.distanceToNextNm;
77
91
  }
92
+ if (!isLast && gapIndexes.has(i)) {
93
+ point.gapToNext = true;
94
+ }
78
95
  emit(point);
79
96
  }
80
97
  break;
98
+ }
81
99
  case 'sid':
82
100
  case 'star':
83
101
  for (const leg of el.legs) {
@@ -118,7 +136,7 @@ export function extractGeoPoints(elements) {
118
136
  * import { createFlightplanResolver, extractRoutePoints } from '@squawk/flightplan';
119
137
  *
120
138
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
121
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
139
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
122
140
  * const points = extractRoutePoints(route);
123
141
  * ```
124
142
  *
@@ -141,7 +159,7 @@ export function extractRoutePoints(route) {
141
159
  * import { createFlightplanResolver, routeToLineString } from '@squawk/flightplan';
142
160
  *
143
161
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
144
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
162
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
145
163
  * const line = routeToLineString(route);
146
164
  * if (line) {
147
165
  * map.addSource('route', { type: 'geojson', data: line });
@@ -68,6 +68,8 @@ export interface RouteTimingLeg {
68
68
  distanceNm: number;
69
69
  /** Cumulative distance from the route start through the end of this leg in nautical miles. */
70
70
  cumulativeDistanceNm: number;
71
+ /** Whether this leg spans a published gap in the airway it was expanded from, as on `RouteLeg`. */
72
+ crossesGap: boolean;
71
73
  /** Initial great-circle true course for the leg in degrees (0-360). */
72
74
  trueCourseDeg: number;
73
75
  /** True heading to fly to hold the course given the wind, in degrees (0-360). */
@@ -152,7 +154,7 @@ export interface RouteTimingResult {
152
154
  * import { createFlightplanResolver, computeRouteTiming } from '@squawk/flightplan';
153
155
  *
154
156
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
155
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
157
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
156
158
  * const result = computeRouteTiming(route, {
157
159
  * trueAirspeedKt: 450,
158
160
  * windProvider: (lat, lon) => ({ directionDeg: 270, speedKt: 80 }),
@@ -1 +1 @@
1
- {"version":3,"file":"route-timing.d.ts","sourceRoot":"","sources":["../src/route-timing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAO/D;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,oBAAoB,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAC7B,mGAAmG;IACnG,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;OAGG;IACH,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;OAGG;IACH,cAAc,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC;;;OAGG;IACH,kBAAkB,EAAE,YAAY,EAAE,CAAC;CACpC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,kBAAkB,GAC1B,iBAAiB,CAwFnB"}
1
+ {"version":3,"file":"route-timing.d.ts","sourceRoot":"","sources":["../src/route-timing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAO/D;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,EAAE,EAAE,MAAM,CAAC;IACX,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,8FAA8F;IAC9F,oBAAoB,EAAE,MAAM,CAAC;IAC7B,mGAAmG;IACnG,UAAU,EAAE,OAAO,CAAC;IACpB,uEAAuE;IACvE,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,cAAc,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,sBAAsB,EAAE,MAAM,CAAC;IAC/B,mDAAmD;IACnD,aAAa,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,IAAI,EAAE,UAAU,GAAG,SAAS,CAAC;IAC7B,mGAAmG;IACnG,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,qDAAqD;IACrD,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,2DAA2D;IAC3D,eAAe,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;OAGG;IACH,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC;;;OAGG;IACH,cAAc,EAAE,OAAO,GAAG,SAAS,CAAC;IACpC;;;OAGG;IACH,kBAAkB,EAAE,YAAY,EAAE,CAAC;CACpC;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,kBAAkB,GAC1B,iBAAiB,CAyFnB"}
@@ -43,7 +43,7 @@ import { computeRouteDistance } from './route-distance.js';
43
43
  * import { createFlightplanResolver, computeRouteTiming } from '@squawk/flightplan';
44
44
  *
45
45
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
46
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
46
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
47
47
  * const result = computeRouteTiming(route, {
48
48
  * trueAirspeedKt: 450,
49
49
  * windProvider: (lat, lon) => ({ directionDeg: 270, speedKt: 80 }),
@@ -100,6 +100,7 @@ export function computeRouteTiming(route, options) {
100
100
  toLon: leg.toLon,
101
101
  distanceNm: leg.distanceNm,
102
102
  cumulativeDistanceNm: leg.cumulativeDistanceNm,
103
+ crossesGap: leg.crossesGap,
103
104
  trueCourseDeg,
104
105
  trueHeadingDeg: triangle.trueHeadingDeg,
105
106
  windCorrectionAngleDeg: triangle.windCorrectionAngleDeg,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squawk/flightplan",
3
- "version": "0.6.3",
3
+ "version": "0.7.0",
4
4
  "type": "module",
5
5
  "description": "Flight plan route string parsing and resolution using composed navigation resolvers",
6
6
  "author": "Neil Cochran",
@@ -43,21 +43,21 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@squawk/flight-math": "^0.5.9",
46
- "@squawk/geo": "^0.5.0",
47
- "@squawk/types": "^0.10.0",
46
+ "@squawk/geo": "^0.5.1",
47
+ "@squawk/types": "^0.11.0",
48
48
  "@types/geojson": "^7946.0.16"
49
49
  },
50
50
  "devDependencies": {
51
- "@squawk/airport-data": "^0.8.2",
52
- "@squawk/airports": "^0.7.3",
53
- "@squawk/airway-data": "^0.6.2",
54
- "@squawk/airways": "^0.5.2",
55
- "@squawk/fix-data": "^0.7.2",
56
- "@squawk/fixes": "^0.5.3",
57
- "@squawk/navaid-data": "^0.7.2",
58
- "@squawk/navaids": "^0.6.3",
59
- "@squawk/procedure-data": "^0.8.2",
60
- "@squawk/procedures": "^0.7.2",
51
+ "@squawk/airport-data": "^0.8.3",
52
+ "@squawk/airports": "^0.7.4",
53
+ "@squawk/airway-data": "^0.6.4",
54
+ "@squawk/airways": "^0.6.0",
55
+ "@squawk/fix-data": "^0.7.3",
56
+ "@squawk/fixes": "^0.5.4",
57
+ "@squawk/navaid-data": "^0.7.3",
58
+ "@squawk/navaids": "^0.6.4",
59
+ "@squawk/procedure-data": "^0.8.3",
60
+ "@squawk/procedures": "^0.7.3",
61
61
  "@types/node": "^26.6.4"
62
62
  },
63
63
  "keywords": [