@squawk/flightplan 0.6.3 → 0.6.4

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
  }
@@ -173,13 +173,13 @@ to the first `byIdent` match.
173
173
  Computes the total great-circle route distance and optional estimated time
174
174
  enroute from a parsed route. Walks the same ordered point sequence as the
175
175
  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.
176
+ segments use the FAA-published per-segment distance where the data carries a
177
+ positive one, falling back to great-circle computation otherwise.
178
178
 
179
179
  ```typescript
180
180
  import { computeRouteDistance } from '@squawk/flightplan';
181
181
 
182
- const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
182
+ const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
183
183
  const result = computeRouteDistance(route, 450);
184
184
  console.log(result.totalDistanceNm, result.estimatedTimeEnrouteHrs);
185
185
  ```
@@ -216,7 +216,7 @@ wind correction angle is zero.
216
216
  ```typescript
217
217
  import { computeRouteTiming } from '@squawk/flightplan';
218
218
 
219
- const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
219
+ const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
220
220
  const result = computeRouteTiming(route, {
221
221
  trueAirspeedKt: 450,
222
222
  windProvider: (lat, lon) => ({ directionDeg: 270, speedKt: 80 }),
@@ -269,7 +269,7 @@ two drawable points, since a `LineString` requires at least two positions.
269
269
  ```typescript
270
270
  import { routeToLineString } from '@squawk/flightplan';
271
271
 
272
- const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
272
+ const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
273
273
  const line = routeToLineString(route);
274
274
  if (line) {
275
275
  map.addSource('route', { type: 'geojson', data: line });
@@ -263,7 +263,7 @@ export interface FlightplanResolver {
263
263
  * procedures: createProcedureResolver({ data: usBundledProcedures.records }),
264
264
  * });
265
265
  *
266
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
266
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
267
267
  * for (const element of route.elements) {
268
268
  * console.log(element.type, element.raw);
269
269
  * }
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
  * }
@@ -55,14 +55,15 @@ export interface RouteDistanceResult {
55
55
  * indicate which parts of the route could not contribute to the distance
56
56
  * calculation.
57
57
  *
58
- * Airway segments use the FAA-published `distanceToNextNm` values when
59
- * available, falling back to great-circle computation otherwise.
58
+ * Airway segments use the FAA-published `distanceToNextNm` value where the
59
+ * source publishes a positive one, falling back to great-circle computation
60
+ * otherwise.
60
61
  *
61
62
  * ```typescript
62
63
  * import { createFlightplanResolver, computeRouteDistance } from '@squawk/flightplan';
63
64
  *
64
65
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
65
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
66
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
66
67
  * const result = computeRouteDistance(route, 450);
67
68
  * console.log(result.totalDistanceNm, result.estimatedTimeEnrouteHrs);
68
69
  * ```
@@ -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;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAClB,aAAa,CAAC,EAAE,MAAM,GACrB,mBAAmB,CAsCrB"}
@@ -20,14 +20,15 @@ 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.
25
26
  *
26
27
  * ```typescript
27
28
  * import { createFlightplanResolver, computeRouteDistance } from '@squawk/flightplan';
28
29
  *
29
30
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
30
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
31
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
31
32
  * const result = computeRouteDistance(route, 450);
32
33
  * console.log(result.totalDistanceNm, result.estimatedTimeEnrouteHrs);
33
34
  * ```
@@ -37,14 +37,14 @@ 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
43
  }
44
44
  /**
45
45
  * 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.
46
+ * points. Duplicate consecutive points (e.g. an airway entry fix at the same
47
+ * position as the preceding waypoint, within about a metre) are suppressed.
48
48
  *
49
49
  * Also collects all `unresolved` elements encountered during the walk.
50
50
  *
@@ -74,7 +74,7 @@ export declare function extractGeoPoints(elements: RouteElement[]): {
74
74
  * import { createFlightplanResolver, extractRoutePoints } from '@squawk/flightplan';
75
75
  *
76
76
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
77
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
77
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
78
78
  * const points = extractRoutePoints(route);
79
79
  * ```
80
80
  *
@@ -94,7 +94,7 @@ export declare function extractRoutePoints(route: ParsedRoute): RoutePoint[];
94
94
  * import { createFlightplanResolver, routeToLineString } from '@squawk/flightplan';
95
95
  *
96
96
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
97
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
97
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
98
98
  * const line = routeToLineString(route);
99
99
  * if (line) {
100
100
  * 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;CACtC;AAuBD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG;IAC1D,MAAM,EAAE,QAAQ,EAAE,CAAC;IACnB,kBAAkB,EAAE,YAAY,EAAE,CAAC;CACpC,CA8EA;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
  *
@@ -70,9 +77,10 @@ export function extractGeoPoints(elements) {
70
77
  lat: wp.lat,
71
78
  lon: wp.lon,
72
79
  };
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) {
80
+ // Only carry a published distance for non-last waypoints (the last
81
+ // waypoint's distanceToNextNm points beyond this segment), and only
82
+ // a positive one: a zero means the source published no distance.
83
+ if (!isLast && wp.distanceToNextNm !== undefined && wp.distanceToNextNm > 0) {
76
84
  point.precomputedDistanceToNextNm = wp.distanceToNextNm;
77
85
  }
78
86
  emit(point);
@@ -118,7 +126,7 @@ export function extractGeoPoints(elements) {
118
126
  * import { createFlightplanResolver, extractRoutePoints } from '@squawk/flightplan';
119
127
  *
120
128
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
121
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
129
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
122
130
  * const points = extractRoutePoints(route);
123
131
  * ```
124
132
  *
@@ -141,7 +149,7 @@ export function extractRoutePoints(route) {
141
149
  * import { createFlightplanResolver, routeToLineString } from '@squawk/flightplan';
142
150
  *
143
151
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
144
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
152
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
145
153
  * const line = routeToLineString(route);
146
154
  * if (line) {
147
155
  * map.addSource('route', { type: 'geojson', data: line });
@@ -152,7 +152,7 @@ export interface RouteTimingResult {
152
152
  * import { createFlightplanResolver, computeRouteTiming } from '@squawk/flightplan';
153
153
  *
154
154
  * const resolver = createFlightplanResolver({ airports, navaids, fixes, airways });
155
- * const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
155
+ * const route = resolver.parse('KLAX DCT LAX J60 SAX DCT KJFK');
156
156
  * const result = computeRouteTiming(route, {
157
157
  * trueAirspeedKt: 450,
158
158
  * windProvider: (lat, lon) => ({ directionDeg: 270, speedKt: 80 }),
@@ -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 }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squawk/flightplan",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
4
4
  "type": "module",
5
5
  "description": "Flight plan route string parsing and resolution using composed navigation resolvers",
6
6
  "author": "Neil Cochran",
@@ -50,8 +50,8 @@
50
50
  "devDependencies": {
51
51
  "@squawk/airport-data": "^0.8.2",
52
52
  "@squawk/airports": "^0.7.3",
53
- "@squawk/airway-data": "^0.6.2",
54
- "@squawk/airways": "^0.5.2",
53
+ "@squawk/airway-data": "^0.6.3",
54
+ "@squawk/airways": "^0.5.3",
55
55
  "@squawk/fix-data": "^0.7.2",
56
56
  "@squawk/fixes": "^0.5.3",
57
57
  "@squawk/navaid-data": "^0.7.2",