@squawk/flightplan 0.2.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 +121 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/resolver.d.ts +259 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +310 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# @squawk/flightplan
|
|
2
|
+
|
|
3
|
+
Pure logic library for parsing flight plan route strings into structured,
|
|
4
|
+
coordinate-resolved route elements. Composes airport, navaid, fix, airway,
|
|
5
|
+
and procedure resolvers to classify and resolve each token in a route string.
|
|
6
|
+
Contains no bundled data - accepts resolver instances at initialization. For
|
|
7
|
+
zero-config use, pair with the companion data and resolver packages.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { createFlightplanResolver } from '@squawk/flightplan';
|
|
13
|
+
import { createAirportResolver } from '@squawk/airports';
|
|
14
|
+
import { createNavaidResolver } from '@squawk/navaids';
|
|
15
|
+
import { createFixResolver } from '@squawk/fixes';
|
|
16
|
+
import { createAirwayResolver } from '@squawk/airways';
|
|
17
|
+
import { createProcedureResolver } from '@squawk/procedures';
|
|
18
|
+
import { usBundledAirports } from '@squawk/airport-data';
|
|
19
|
+
import { usBundledNavaids } from '@squawk/navaid-data';
|
|
20
|
+
import { usBundledFixes } from '@squawk/fix-data';
|
|
21
|
+
import { usBundledAirways } from '@squawk/airway-data';
|
|
22
|
+
import { usBundledProcedures } from '@squawk/procedure-data';
|
|
23
|
+
|
|
24
|
+
const resolver = createFlightplanResolver({
|
|
25
|
+
airports: createAirportResolver({ data: usBundledAirports.records }),
|
|
26
|
+
navaids: createNavaidResolver({ data: usBundledNavaids.records }),
|
|
27
|
+
fixes: createFixResolver({ data: usBundledFixes.records }),
|
|
28
|
+
airways: createAirwayResolver({ data: usBundledAirways.records }),
|
|
29
|
+
procedures: createProcedureResolver({ data: usBundledProcedures.records }),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
|
|
33
|
+
for (const element of route.elements) {
|
|
34
|
+
console.log(element.type, element.raw);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
All resolver providers are optional. Tokens that require a missing provider
|
|
39
|
+
are marked as `unresolved`:
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { createFlightplanResolver } from '@squawk/flightplan';
|
|
43
|
+
|
|
44
|
+
// Works with only the resolvers you have
|
|
45
|
+
const resolver = createFlightplanResolver({ airports: myAirportResolver });
|
|
46
|
+
const route = resolver.parse('KJFK DCT KLAX');
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## API
|
|
50
|
+
|
|
51
|
+
### `createFlightplanResolver(options)`
|
|
52
|
+
|
|
53
|
+
Creates a resolver from optional lookup providers.
|
|
54
|
+
|
|
55
|
+
**Parameters:**
|
|
56
|
+
|
|
57
|
+
- `options.airports` - airport lookup (must provide `byFaaId` and `byIcao`)
|
|
58
|
+
- `options.navaids` - navaid lookup (must provide `byIdent`)
|
|
59
|
+
- `options.fixes` - fix lookup (must provide `byIdent`)
|
|
60
|
+
- `options.airways` - airway lookup (must provide `byDesignation` and `expand`)
|
|
61
|
+
- `options.procedures` - procedure lookup (must provide `byName` and `expand`)
|
|
62
|
+
|
|
63
|
+
**Returns:** `FlightplanResolver` - an object with the `parse` method described below.
|
|
64
|
+
|
|
65
|
+
### `resolver.parse(routeString)`
|
|
66
|
+
|
|
67
|
+
Parses a flight plan route string into an ordered sequence of resolved
|
|
68
|
+
elements. Each whitespace-separated token is classified and resolved against
|
|
69
|
+
the configured lookup providers.
|
|
70
|
+
|
|
71
|
+
**Returns:** `ParsedRoute` with:
|
|
72
|
+
|
|
73
|
+
- `raw` - the original route string
|
|
74
|
+
- `elements` - ordered array of `RouteElement` values
|
|
75
|
+
|
|
76
|
+
### Route element types
|
|
77
|
+
|
|
78
|
+
Each element has a `type` discriminant and a `raw` field with the original token.
|
|
79
|
+
|
|
80
|
+
| Type | Description | Key fields |
|
|
81
|
+
| --------------- | ----------------------------------------- | ----------------------------------------------------------- |
|
|
82
|
+
| `airport` | Resolved airport (ICAO or FAA ID) | `airport` |
|
|
83
|
+
| `sid` | Standard Instrument Departure | `procedure`, `waypoints` |
|
|
84
|
+
| `star` | Standard Terminal Arrival Route | `procedure`, `waypoints` |
|
|
85
|
+
| `airway` | Airway segment between entry and exit fix | `airway`, `entryFix`, `exitFix`, `waypoints` |
|
|
86
|
+
| `direct` | DCT (direct) indicator | - |
|
|
87
|
+
| `waypoint` | Resolved fix or navaid | `fix` and/or `navaid`, `lat`, `lon` |
|
|
88
|
+
| `coordinate` | Lat/lon specified in the route string | `lat`, `lon` |
|
|
89
|
+
| `speedAltitude` | Speed/altitude group (e.g. N0450F350) | `speedKt`/`speedKmPerHr`/`mach`, `flightLevel`/`altitudeFt` |
|
|
90
|
+
| `unresolved` | Token that could not be resolved | - |
|
|
91
|
+
|
|
92
|
+
### Coordinate formats
|
|
93
|
+
|
|
94
|
+
- `DDMMN/DDDMMEW` (e.g. `4030N07045W` for 40 deg 30 min N, 70 deg 45 min W)
|
|
95
|
+
- `DDN/DDDEW` (e.g. `40N070W` for 40 deg N, 70 deg W)
|
|
96
|
+
|
|
97
|
+
### Speed/altitude formats
|
|
98
|
+
|
|
99
|
+
- `N0450F350` - 450 knots at FL350
|
|
100
|
+
- `K0830F350` - 830 km/h at FL350
|
|
101
|
+
- `M082F350` - Mach 0.82 at FL350
|
|
102
|
+
- `N0250A065` - 250 knots at 6500 ft
|
|
103
|
+
|
|
104
|
+
### Airway handling
|
|
105
|
+
|
|
106
|
+
When a token matches an airway designation and both a previous waypoint
|
|
107
|
+
and a next token (exit fix) are available, the resolver expands the airway
|
|
108
|
+
between those fixes. The exit fix token is consumed as part of the airway
|
|
109
|
+
element. If expansion fails, the airway token falls through to other
|
|
110
|
+
resolution strategies.
|
|
111
|
+
|
|
112
|
+
### Identifier ambiguity
|
|
113
|
+
|
|
114
|
+
When a token could match multiple entity types (e.g. a 3-letter code matching
|
|
115
|
+
both an airport and a navaid), the resolver uses this priority order:
|
|
116
|
+
|
|
117
|
+
1. Airway (if previous waypoint context exists and next token available)
|
|
118
|
+
2. Airport (ICAO or FAA ID)
|
|
119
|
+
3. Procedure (SID/STAR)
|
|
120
|
+
4. Fix
|
|
121
|
+
5. Navaid
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Flight plan route string parsing and resolution. Composes airport, navaid,
|
|
4
|
+
* fix, airway, and procedure resolvers to parse route strings into structured,
|
|
5
|
+
* coordinate-resolved route elements.
|
|
6
|
+
*/
|
|
7
|
+
export { createFlightplanResolver } from './resolver.js';
|
|
8
|
+
export type { FlightplanResolver, FlightplanResolverOptions, FlightplanAirportLookup, FlightplanNavaidLookup, FlightplanFixLookup, FlightplanAirwayLookup, FlightplanProcedureLookup, ParsedRoute, RouteElement, AirportRouteElement, SidRouteElement, StarRouteElement, AirwayRouteElement, DirectRouteElement, WaypointRouteElement, CoordinateRouteElement, SpeedAltitudeRouteElement, UnresolvedRouteElement, } from './resolver.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,wBAAwB,EAAE,MAAM,eAAe,CAAC;AACzD,YAAY,EACV,kBAAkB,EAClB,yBAAyB,EACzB,uBAAuB,EACvB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,yBAAyB,EACzB,sBAAsB,GACvB,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Flight plan route string parsing and resolution. Composes airport, navaid,
|
|
4
|
+
* fix, airway, and procedure resolvers to parse route strings into structured,
|
|
5
|
+
* coordinate-resolved route elements.
|
|
6
|
+
*/
|
|
7
|
+
export { createFlightplanResolver } from './resolver.js';
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import type { Airport, Airway, AirwayWaypoint, Fix, Navaid, Procedure, ProcedureWaypoint } from '@squawk/types';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal airport lookup interface consumed by the flightplan resolver.
|
|
4
|
+
* Structurally compatible with {@link @squawk/airports!AirportResolver}.
|
|
5
|
+
*/
|
|
6
|
+
export interface FlightplanAirportLookup {
|
|
7
|
+
/** Looks up an airport by FAA location identifier (e.g. "JFK"). */
|
|
8
|
+
byFaaId(faaId: string): Airport | undefined;
|
|
9
|
+
/** Looks up an airport by ICAO code (e.g. "KJFK"). */
|
|
10
|
+
byIcao(icao: string): Airport | undefined;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Minimal navaid lookup interface consumed by the flightplan resolver.
|
|
14
|
+
* Structurally compatible with {@link @squawk/navaids!NavaidResolver}.
|
|
15
|
+
*/
|
|
16
|
+
export interface FlightplanNavaidLookup {
|
|
17
|
+
/** Looks up navaids by identifier. Returns an empty array if none found. */
|
|
18
|
+
byIdent(ident: string): Navaid[];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Minimal fix lookup interface consumed by the flightplan resolver.
|
|
22
|
+
* Structurally compatible with {@link @squawk/fixes!FixResolver}.
|
|
23
|
+
*/
|
|
24
|
+
export interface FlightplanFixLookup {
|
|
25
|
+
/** Looks up fixes by identifier. Returns an empty array if none found. */
|
|
26
|
+
byIdent(ident: string): Fix[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Minimal airway lookup interface consumed by the flightplan resolver.
|
|
30
|
+
* Structurally compatible with {@link @squawk/airways!AirwayResolver}.
|
|
31
|
+
*/
|
|
32
|
+
export interface FlightplanAirwayLookup {
|
|
33
|
+
/** Looks up airways by designation (e.g. "V16", "J60"). Returns an empty array if none found. */
|
|
34
|
+
byDesignation(designation: string): Airway[];
|
|
35
|
+
/** Expands an airway between entry and exit fixes, returning ordered waypoints. */
|
|
36
|
+
expand(designation: string, entryFix: string, exitFix: string): {
|
|
37
|
+
airway: Airway;
|
|
38
|
+
waypoints: AirwayWaypoint[];
|
|
39
|
+
} | undefined;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Minimal procedure lookup interface consumed by the flightplan resolver.
|
|
43
|
+
* Structurally compatible with {@link @squawk/procedures!ProcedureResolver}.
|
|
44
|
+
*/
|
|
45
|
+
export interface FlightplanProcedureLookup {
|
|
46
|
+
/** Looks up a procedure by FAA computer code (e.g. "AALLE4"). */
|
|
47
|
+
byName(computerCode: string): Procedure | undefined;
|
|
48
|
+
/** Expands a procedure into an ordered waypoint sequence. */
|
|
49
|
+
expand(computerCode: string, transitionName?: string): {
|
|
50
|
+
procedure: Procedure;
|
|
51
|
+
waypoints: ProcedureWaypoint[];
|
|
52
|
+
} | undefined;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A departure or arrival airport in the route.
|
|
56
|
+
*/
|
|
57
|
+
export interface AirportRouteElement {
|
|
58
|
+
/** Discriminant for airport elements. */
|
|
59
|
+
type: 'airport';
|
|
60
|
+
/** Raw token from the route string. */
|
|
61
|
+
raw: string;
|
|
62
|
+
/** Resolved airport record. */
|
|
63
|
+
airport: Airport;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A Standard Instrument Departure (SID) in the route.
|
|
67
|
+
*/
|
|
68
|
+
export interface SidRouteElement {
|
|
69
|
+
/** Discriminant for SID elements. */
|
|
70
|
+
type: 'sid';
|
|
71
|
+
/** Raw token from the route string. */
|
|
72
|
+
raw: string;
|
|
73
|
+
/** Resolved procedure record. */
|
|
74
|
+
procedure: Procedure;
|
|
75
|
+
/** Ordered waypoint sequence for the procedure. */
|
|
76
|
+
waypoints: ProcedureWaypoint[];
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* A Standard Terminal Arrival Route (STAR) in the route.
|
|
80
|
+
*/
|
|
81
|
+
export interface StarRouteElement {
|
|
82
|
+
/** Discriminant for STAR elements. */
|
|
83
|
+
type: 'star';
|
|
84
|
+
/** Raw token from the route string. */
|
|
85
|
+
raw: string;
|
|
86
|
+
/** Resolved procedure record. */
|
|
87
|
+
procedure: Procedure;
|
|
88
|
+
/** Ordered waypoint sequence for the procedure. */
|
|
89
|
+
waypoints: ProcedureWaypoint[];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* A segment along a published airway between an entry fix and exit fix.
|
|
93
|
+
*/
|
|
94
|
+
export interface AirwayRouteElement {
|
|
95
|
+
/** Discriminant for airway elements. */
|
|
96
|
+
type: 'airway';
|
|
97
|
+
/** Raw token from the route string (airway designation). */
|
|
98
|
+
raw: string;
|
|
99
|
+
/** Resolved airway record. */
|
|
100
|
+
airway: Airway;
|
|
101
|
+
/** Identifier of the entry fix. */
|
|
102
|
+
entryFix: string;
|
|
103
|
+
/** Identifier of the exit fix. */
|
|
104
|
+
exitFix: string;
|
|
105
|
+
/** Ordered waypoints from entry to exit fix (inclusive). */
|
|
106
|
+
waypoints: AirwayWaypoint[];
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* A DCT (direct) indicator between two waypoints.
|
|
110
|
+
*/
|
|
111
|
+
export interface DirectRouteElement {
|
|
112
|
+
/** Discriminant for direct elements. */
|
|
113
|
+
type: 'direct';
|
|
114
|
+
/** Raw token from the route string ("DCT"). */
|
|
115
|
+
raw: string;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* A resolved waypoint (fix or navaid) in the route.
|
|
119
|
+
*/
|
|
120
|
+
export interface WaypointRouteElement {
|
|
121
|
+
/** Discriminant for waypoint elements. */
|
|
122
|
+
type: 'waypoint';
|
|
123
|
+
/** Raw token from the route string. */
|
|
124
|
+
raw: string;
|
|
125
|
+
/** Resolved fix record, if the waypoint matched a fix. */
|
|
126
|
+
fix?: Fix;
|
|
127
|
+
/** Resolved navaid record, if the waypoint matched a navaid. */
|
|
128
|
+
navaid?: Navaid;
|
|
129
|
+
/** Latitude in decimal degrees, positive north. */
|
|
130
|
+
lat: number;
|
|
131
|
+
/** Longitude in decimal degrees, positive east. */
|
|
132
|
+
lon: number;
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* A latitude/longitude coordinate specified directly in the route string.
|
|
136
|
+
*/
|
|
137
|
+
export interface CoordinateRouteElement {
|
|
138
|
+
/** Discriminant for coordinate elements. */
|
|
139
|
+
type: 'coordinate';
|
|
140
|
+
/** Raw token from the route string. */
|
|
141
|
+
raw: string;
|
|
142
|
+
/** Latitude in decimal degrees, positive north. */
|
|
143
|
+
lat: number;
|
|
144
|
+
/** Longitude in decimal degrees, positive east. */
|
|
145
|
+
lon: number;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* A speed and/or altitude group from the route string (e.g. "N0450F350").
|
|
149
|
+
*/
|
|
150
|
+
export interface SpeedAltitudeRouteElement {
|
|
151
|
+
/** Discriminant for speed/altitude elements. */
|
|
152
|
+
type: 'speedAltitude';
|
|
153
|
+
/** Raw token from the route string. */
|
|
154
|
+
raw: string;
|
|
155
|
+
/** Speed in knots, if specified with N prefix. */
|
|
156
|
+
speedKt?: number;
|
|
157
|
+
/** Speed in km/h, if specified with K prefix. */
|
|
158
|
+
speedKmPerHr?: number;
|
|
159
|
+
/** Mach number (e.g. 0.82), if specified with M prefix. */
|
|
160
|
+
mach?: number;
|
|
161
|
+
/** Flight level (e.g. 350 for FL350), if specified with F prefix. */
|
|
162
|
+
flightLevel?: number;
|
|
163
|
+
/** Altitude in feet, if specified with A prefix. */
|
|
164
|
+
altitudeFt?: number;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* A route token that could not be resolved to any known element.
|
|
168
|
+
*/
|
|
169
|
+
export interface UnresolvedRouteElement {
|
|
170
|
+
/** Discriminant for unresolved elements. */
|
|
171
|
+
type: 'unresolved';
|
|
172
|
+
/** Raw token from the route string. */
|
|
173
|
+
raw: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* A single element in a parsed flight plan route. Uses a discriminated union
|
|
177
|
+
* on the `type` field.
|
|
178
|
+
*/
|
|
179
|
+
export type RouteElement = AirportRouteElement | SidRouteElement | StarRouteElement | AirwayRouteElement | DirectRouteElement | WaypointRouteElement | CoordinateRouteElement | SpeedAltitudeRouteElement | UnresolvedRouteElement;
|
|
180
|
+
/**
|
|
181
|
+
* The result of parsing a flight plan route string.
|
|
182
|
+
*/
|
|
183
|
+
export interface ParsedRoute {
|
|
184
|
+
/** The original route string that was parsed. */
|
|
185
|
+
raw: string;
|
|
186
|
+
/** Ordered sequence of resolved route elements. */
|
|
187
|
+
elements: RouteElement[];
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Options for creating a flightplan resolver. All lookup providers are optional;
|
|
191
|
+
* tokens that require a missing provider will be marked as unresolved.
|
|
192
|
+
*/
|
|
193
|
+
export interface FlightplanResolverOptions {
|
|
194
|
+
/** Airport lookup provider. */
|
|
195
|
+
airports?: FlightplanAirportLookup;
|
|
196
|
+
/** Navaid lookup provider. */
|
|
197
|
+
navaids?: FlightplanNavaidLookup;
|
|
198
|
+
/** Fix lookup provider. */
|
|
199
|
+
fixes?: FlightplanFixLookup;
|
|
200
|
+
/** Airway lookup provider. */
|
|
201
|
+
airways?: FlightplanAirwayLookup;
|
|
202
|
+
/** Procedure lookup provider. */
|
|
203
|
+
procedures?: FlightplanProcedureLookup;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* A stateless resolver that parses flight plan route strings into structured,
|
|
207
|
+
* resolved route elements.
|
|
208
|
+
*/
|
|
209
|
+
export interface FlightplanResolver {
|
|
210
|
+
/**
|
|
211
|
+
* Parses a flight plan route string into an ordered sequence of resolved
|
|
212
|
+
* route elements. Each token in the route string is classified and resolved
|
|
213
|
+
* against the configured lookup providers.
|
|
214
|
+
*
|
|
215
|
+
* Airway tokens are expanded into waypoint sequences between entry and exit
|
|
216
|
+
* fixes. Procedure tokens (SIDs/STARs) are expanded into waypoint sequences.
|
|
217
|
+
* Unresolvable tokens are preserved as `unresolved` elements.
|
|
218
|
+
*/
|
|
219
|
+
parse(routeString: string): ParsedRoute;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Creates a stateless flightplan resolver that parses route strings into
|
|
223
|
+
* structured, resolved route elements. The resolver composes optional lookup
|
|
224
|
+
* providers for airports, navaids, fixes, airways, and procedures to resolve
|
|
225
|
+
* each token in the route string.
|
|
226
|
+
*
|
|
227
|
+
* All lookup providers are optional. Tokens that require a missing provider
|
|
228
|
+
* are marked as `unresolved`. This allows consumers to use only the resolvers
|
|
229
|
+
* they have available.
|
|
230
|
+
*
|
|
231
|
+
* ```typescript
|
|
232
|
+
* import { createFlightplanResolver } from '@squawk/flightplan';
|
|
233
|
+
* import { createAirportResolver } from '@squawk/airports';
|
|
234
|
+
* import { createNavaidResolver } from '@squawk/navaids';
|
|
235
|
+
* import { createFixResolver } from '@squawk/fixes';
|
|
236
|
+
* import { createAirwayResolver } from '@squawk/airways';
|
|
237
|
+
* import { createProcedureResolver } from '@squawk/procedures';
|
|
238
|
+
* import { usBundledAirports } from '@squawk/airport-data';
|
|
239
|
+
* import { usBundledNavaids } from '@squawk/navaid-data';
|
|
240
|
+
* import { usBundledFixes } from '@squawk/fix-data';
|
|
241
|
+
* import { usBundledAirways } from '@squawk/airway-data';
|
|
242
|
+
* import { usBundledProcedures } from '@squawk/procedure-data';
|
|
243
|
+
*
|
|
244
|
+
* const resolver = createFlightplanResolver({
|
|
245
|
+
* airports: createAirportResolver({ data: usBundledAirports.records }),
|
|
246
|
+
* navaids: createNavaidResolver({ data: usBundledNavaids.records }),
|
|
247
|
+
* fixes: createFixResolver({ data: usBundledFixes.records }),
|
|
248
|
+
* airways: createAirwayResolver({ data: usBundledAirways.records }),
|
|
249
|
+
* procedures: createProcedureResolver({ data: usBundledProcedures.records }),
|
|
250
|
+
* });
|
|
251
|
+
*
|
|
252
|
+
* const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
|
|
253
|
+
* for (const element of route.elements) {
|
|
254
|
+
* console.log(element.type, element.raw);
|
|
255
|
+
* }
|
|
256
|
+
* ```
|
|
257
|
+
*/
|
|
258
|
+
export declare function createFlightplanResolver(options: FlightplanResolverOptions): FlightplanResolver;
|
|
259
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"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"}
|
package/dist/resolver.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Coordinate parsing
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
/**
|
|
5
|
+
* Pattern for DDMM[N/S]DDDMM[E/W] coordinates (e.g. "4000N07000W").
|
|
6
|
+
*/
|
|
7
|
+
const COORD_DDMM_RE = /^(\d{2})(\d{2})([NS])(\d{3})(\d{2})([EW])$/;
|
|
8
|
+
/**
|
|
9
|
+
* Pattern for DD[N/S]DDD[E/W] coordinates (e.g. "40N070W").
|
|
10
|
+
*/
|
|
11
|
+
const COORD_DD_RE = /^(\d{2})([NS])(\d{3})([EW])$/;
|
|
12
|
+
/**
|
|
13
|
+
* Attempts to parse a token as a latitude/longitude coordinate.
|
|
14
|
+
* Supports DDMMN/DDDMMEW and DDN/DDDEW formats.
|
|
15
|
+
* Returns the parsed lat/lon or undefined if the token is not a coordinate.
|
|
16
|
+
*/
|
|
17
|
+
function parseCoordinate(token) {
|
|
18
|
+
let match = COORD_DDMM_RE.exec(token);
|
|
19
|
+
if (match) {
|
|
20
|
+
const latDeg = parseInt(match[1], 10);
|
|
21
|
+
const latMin = parseInt(match[2], 10);
|
|
22
|
+
const latSign = match[3] === 'S' ? -1 : 1;
|
|
23
|
+
const lonDeg = parseInt(match[4], 10);
|
|
24
|
+
const lonMin = parseInt(match[5], 10);
|
|
25
|
+
const lonSign = match[6] === 'W' ? -1 : 1;
|
|
26
|
+
return {
|
|
27
|
+
lat: latSign * (latDeg + latMin / 60),
|
|
28
|
+
lon: lonSign * (lonDeg + lonMin / 60),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
match = COORD_DD_RE.exec(token);
|
|
32
|
+
if (match) {
|
|
33
|
+
const latDeg = parseInt(match[1], 10);
|
|
34
|
+
const latSign = match[2] === 'S' ? -1 : 1;
|
|
35
|
+
const lonDeg = parseInt(match[3], 10);
|
|
36
|
+
const lonSign = match[4] === 'W' ? -1 : 1;
|
|
37
|
+
return {
|
|
38
|
+
lat: latSign * latDeg,
|
|
39
|
+
lon: lonSign * lonDeg,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Speed/altitude parsing
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
/**
|
|
48
|
+
* Pattern for speed/altitude groups.
|
|
49
|
+
* Speed: N (knots, 4 digits), K (km/h, 4 digits), M (mach, 3 digits).
|
|
50
|
+
* Altitude: F (flight level, 3 digits), A (altitude in hundreds of feet, 3 digits).
|
|
51
|
+
*/
|
|
52
|
+
const SPEED_ALT_RE = /^([NKM])(\d{3,4})([FA])(\d{3,4})$/;
|
|
53
|
+
/**
|
|
54
|
+
* Attempts to parse a token as a speed/altitude group.
|
|
55
|
+
* Returns the parsed values or undefined if the token is not a speed/altitude group.
|
|
56
|
+
*/
|
|
57
|
+
function parseSpeedAltitude(token) {
|
|
58
|
+
const match = SPEED_ALT_RE.exec(token);
|
|
59
|
+
if (!match) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
const speedPrefix = match[1];
|
|
63
|
+
const speedValue = parseInt(match[2], 10);
|
|
64
|
+
const altPrefix = match[3];
|
|
65
|
+
const altValue = parseInt(match[4], 10);
|
|
66
|
+
const result = {
|
|
67
|
+
type: 'speedAltitude',
|
|
68
|
+
raw: token,
|
|
69
|
+
};
|
|
70
|
+
if (speedPrefix === 'N') {
|
|
71
|
+
result.speedKt = speedValue;
|
|
72
|
+
}
|
|
73
|
+
else if (speedPrefix === 'K') {
|
|
74
|
+
result.speedKmPerHr = speedValue;
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
result.mach = speedValue / 100;
|
|
78
|
+
}
|
|
79
|
+
if (altPrefix === 'F') {
|
|
80
|
+
result.flightLevel = altValue;
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
result.altitudeFt = altValue * 100;
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Token resolution helpers
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
/**
|
|
91
|
+
* Attempts to resolve a token as an airport (ICAO or FAA ID).
|
|
92
|
+
*/
|
|
93
|
+
function tryAirport(token, airports) {
|
|
94
|
+
if (!airports) {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const airport = airports.byIcao(token) ?? airports.byFaaId(token);
|
|
98
|
+
if (airport) {
|
|
99
|
+
return { type: 'airport', raw: token, airport };
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Attempts to resolve a token as a SID or STAR procedure.
|
|
105
|
+
*/
|
|
106
|
+
function tryProcedure(token, procedures) {
|
|
107
|
+
if (!procedures) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const proc = procedures.byName(token);
|
|
111
|
+
if (!proc) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
const expansion = procedures.expand(token);
|
|
115
|
+
const waypoints = expansion ? expansion.waypoints : [];
|
|
116
|
+
if (proc.type === 'SID') {
|
|
117
|
+
return { type: 'sid', raw: token, procedure: proc, waypoints };
|
|
118
|
+
}
|
|
119
|
+
return { type: 'star', raw: token, procedure: proc, waypoints };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Attempts to resolve a token as a fix waypoint.
|
|
123
|
+
*/
|
|
124
|
+
function tryFix(token, fixes) {
|
|
125
|
+
if (!fixes) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const results = fixes.byIdent(token);
|
|
129
|
+
if (results.length > 0) {
|
|
130
|
+
const fix = results[0];
|
|
131
|
+
return { type: 'waypoint', raw: token, fix, lat: fix.lat, lon: fix.lon };
|
|
132
|
+
}
|
|
133
|
+
return undefined;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Attempts to resolve a token as a navaid waypoint.
|
|
137
|
+
*/
|
|
138
|
+
function tryNavaid(token, navaids) {
|
|
139
|
+
if (!navaids) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
const results = navaids.byIdent(token);
|
|
143
|
+
if (results.length > 0) {
|
|
144
|
+
const navaid = results[0];
|
|
145
|
+
return { type: 'waypoint', raw: token, navaid, lat: navaid.lat, lon: navaid.lon };
|
|
146
|
+
}
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
// ---------------------------------------------------------------------------
|
|
150
|
+
// Main parse implementation
|
|
151
|
+
// ---------------------------------------------------------------------------
|
|
152
|
+
/**
|
|
153
|
+
* Creates a stateless flightplan resolver that parses route strings into
|
|
154
|
+
* structured, resolved route elements. The resolver composes optional lookup
|
|
155
|
+
* providers for airports, navaids, fixes, airways, and procedures to resolve
|
|
156
|
+
* each token in the route string.
|
|
157
|
+
*
|
|
158
|
+
* All lookup providers are optional. Tokens that require a missing provider
|
|
159
|
+
* are marked as `unresolved`. This allows consumers to use only the resolvers
|
|
160
|
+
* they have available.
|
|
161
|
+
*
|
|
162
|
+
* ```typescript
|
|
163
|
+
* import { createFlightplanResolver } from '@squawk/flightplan';
|
|
164
|
+
* import { createAirportResolver } from '@squawk/airports';
|
|
165
|
+
* import { createNavaidResolver } from '@squawk/navaids';
|
|
166
|
+
* import { createFixResolver } from '@squawk/fixes';
|
|
167
|
+
* import { createAirwayResolver } from '@squawk/airways';
|
|
168
|
+
* import { createProcedureResolver } from '@squawk/procedures';
|
|
169
|
+
* import { usBundledAirports } from '@squawk/airport-data';
|
|
170
|
+
* import { usBundledNavaids } from '@squawk/navaid-data';
|
|
171
|
+
* import { usBundledFixes } from '@squawk/fix-data';
|
|
172
|
+
* import { usBundledAirways } from '@squawk/airway-data';
|
|
173
|
+
* import { usBundledProcedures } from '@squawk/procedure-data';
|
|
174
|
+
*
|
|
175
|
+
* const resolver = createFlightplanResolver({
|
|
176
|
+
* airports: createAirportResolver({ data: usBundledAirports.records }),
|
|
177
|
+
* navaids: createNavaidResolver({ data: usBundledNavaids.records }),
|
|
178
|
+
* fixes: createFixResolver({ data: usBundledFixes.records }),
|
|
179
|
+
* airways: createAirwayResolver({ data: usBundledAirways.records }),
|
|
180
|
+
* procedures: createProcedureResolver({ data: usBundledProcedures.records }),
|
|
181
|
+
* });
|
|
182
|
+
*
|
|
183
|
+
* const route = resolver.parse('KJFK DCT MERIT J60 MARTN DCT KLAX');
|
|
184
|
+
* for (const element of route.elements) {
|
|
185
|
+
* console.log(element.type, element.raw);
|
|
186
|
+
* }
|
|
187
|
+
* ```
|
|
188
|
+
*/
|
|
189
|
+
export function createFlightplanResolver(options) {
|
|
190
|
+
const { airports, navaids, fixes, airways, procedures } = options;
|
|
191
|
+
return {
|
|
192
|
+
parse(routeString) {
|
|
193
|
+
const raw = routeString.trim();
|
|
194
|
+
if (raw.length === 0) {
|
|
195
|
+
return { raw, elements: [] };
|
|
196
|
+
}
|
|
197
|
+
const tokens = raw.split(/\s+/);
|
|
198
|
+
const elements = [];
|
|
199
|
+
let lastWaypointIdent;
|
|
200
|
+
let i = 0;
|
|
201
|
+
while (i < tokens.length) {
|
|
202
|
+
const token = tokens[i].toUpperCase();
|
|
203
|
+
// DCT (direct)
|
|
204
|
+
if (token === 'DCT') {
|
|
205
|
+
elements.push({ type: 'direct', raw: token });
|
|
206
|
+
i++;
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
// Lat/lon coordinate
|
|
210
|
+
const coord = parseCoordinate(token);
|
|
211
|
+
if (coord) {
|
|
212
|
+
elements.push({ type: 'coordinate', raw: token, lat: coord.lat, lon: coord.lon });
|
|
213
|
+
lastWaypointIdent = undefined;
|
|
214
|
+
i++;
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
// Speed/altitude group
|
|
218
|
+
const speedAlt = parseSpeedAltitude(token);
|
|
219
|
+
if (speedAlt) {
|
|
220
|
+
elements.push(speedAlt);
|
|
221
|
+
i++;
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
// Try airway expansion (requires previous waypoint and next token as exit fix)
|
|
225
|
+
if (lastWaypointIdent && airways && i + 1 < tokens.length) {
|
|
226
|
+
const candidates = airways.byDesignation(token);
|
|
227
|
+
if (candidates.length > 0) {
|
|
228
|
+
const exitFix = tokens[i + 1].toUpperCase();
|
|
229
|
+
const expansion = airways.expand(token, lastWaypointIdent, exitFix);
|
|
230
|
+
if (expansion) {
|
|
231
|
+
elements.push({
|
|
232
|
+
type: 'airway',
|
|
233
|
+
raw: token,
|
|
234
|
+
airway: expansion.airway,
|
|
235
|
+
entryFix: lastWaypointIdent,
|
|
236
|
+
exitFix,
|
|
237
|
+
waypoints: expansion.waypoints,
|
|
238
|
+
});
|
|
239
|
+
// The exit fix is consumed as part of the airway; update last waypoint
|
|
240
|
+
lastWaypointIdent = exitFix;
|
|
241
|
+
i += 2;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
// Airway recognized but expansion failed (entry/exit fixes not on it).
|
|
245
|
+
// Mark as unresolved rather than falling through to other resolution
|
|
246
|
+
// strategies, since airway designations should not resolve as fixes,
|
|
247
|
+
// navaids, or airports. The exit fix token is NOT consumed and will be
|
|
248
|
+
// parsed independently on the next iteration.
|
|
249
|
+
elements.push({ type: 'unresolved', raw: token });
|
|
250
|
+
lastWaypointIdent = undefined;
|
|
251
|
+
i++;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// Token is a recognized airway but has no previous waypoint context or
|
|
256
|
+
// no next token to use as an exit fix. Mark as unresolved.
|
|
257
|
+
if (airways && airways.byDesignation(token).length > 0) {
|
|
258
|
+
elements.push({ type: 'unresolved', raw: token });
|
|
259
|
+
lastWaypointIdent = undefined;
|
|
260
|
+
i++;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
// Try airport
|
|
264
|
+
const airportElement = tryAirport(token, airports);
|
|
265
|
+
if (airportElement) {
|
|
266
|
+
elements.push(airportElement);
|
|
267
|
+
// Preserve the token as lastWaypointIdent so a following airway can
|
|
268
|
+
// use it as the entry fix (e.g. "BOS V16 CCC" where BOS is both an
|
|
269
|
+
// airport and a navaid on V16).
|
|
270
|
+
lastWaypointIdent = token;
|
|
271
|
+
i++;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
// Try procedure (SID/STAR)
|
|
275
|
+
const procElement = tryProcedure(token, procedures);
|
|
276
|
+
if (procElement) {
|
|
277
|
+
elements.push(procElement);
|
|
278
|
+
// Update last waypoint to the last fix in the procedure
|
|
279
|
+
if (procElement.waypoints.length > 0) {
|
|
280
|
+
lastWaypointIdent =
|
|
281
|
+
procElement.waypoints[procElement.waypoints.length - 1].fixIdentifier;
|
|
282
|
+
}
|
|
283
|
+
i++;
|
|
284
|
+
continue;
|
|
285
|
+
}
|
|
286
|
+
// Try fix
|
|
287
|
+
const fixElement = tryFix(token, fixes);
|
|
288
|
+
if (fixElement) {
|
|
289
|
+
elements.push(fixElement);
|
|
290
|
+
lastWaypointIdent = token;
|
|
291
|
+
i++;
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
// Try navaid
|
|
295
|
+
const navaidElement = tryNavaid(token, navaids);
|
|
296
|
+
if (navaidElement) {
|
|
297
|
+
elements.push(navaidElement);
|
|
298
|
+
lastWaypointIdent = token;
|
|
299
|
+
i++;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
// Unresolved
|
|
303
|
+
elements.push({ type: 'unresolved', raw: token });
|
|
304
|
+
lastWaypointIdent = undefined;
|
|
305
|
+
i++;
|
|
306
|
+
}
|
|
307
|
+
return { raw, elements };
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@squawk/flightplan",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Flight plan route string parsing and resolution using composed navigation resolvers",
|
|
6
|
+
"author": "Neil Cochran",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/neilcochran/squawk",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/neilcochran/squawk.git",
|
|
12
|
+
"directory": "packages/flightplan"
|
|
13
|
+
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=22"
|
|
16
|
+
},
|
|
17
|
+
"typedocMain": "src/index.ts",
|
|
18
|
+
"main": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": "./dist/index.js",
|
|
23
|
+
"types": "./dist/index.d.ts"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"!dist/**/*.spec.*"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc",
|
|
32
|
+
"test": "node --test 'dist/**/*.spec.js'",
|
|
33
|
+
"lint": "tsc --noEmit && eslint src"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@squawk/types": "*"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@squawk/airport-data": "*",
|
|
40
|
+
"@squawk/airports": "*",
|
|
41
|
+
"@squawk/airway-data": "*",
|
|
42
|
+
"@squawk/airways": "*",
|
|
43
|
+
"@squawk/fix-data": "*",
|
|
44
|
+
"@squawk/fixes": "*",
|
|
45
|
+
"@squawk/navaid-data": "*",
|
|
46
|
+
"@squawk/navaids": "*",
|
|
47
|
+
"@squawk/procedure-data": "*",
|
|
48
|
+
"@squawk/procedures": "*",
|
|
49
|
+
"@types/node": "^25.5.2"
|
|
50
|
+
},
|
|
51
|
+
"keywords": [
|
|
52
|
+
"aviation",
|
|
53
|
+
"typescript",
|
|
54
|
+
"flight-plan",
|
|
55
|
+
"route",
|
|
56
|
+
"navigation",
|
|
57
|
+
"ifr"
|
|
58
|
+
],
|
|
59
|
+
"publishConfig": {
|
|
60
|
+
"access": "public"
|
|
61
|
+
}
|
|
62
|
+
}
|