@squawk/procedures
Advanced tools
+2
-1
| /** | ||
| * @packageDocumentation | ||
| * Pure logic library for querying US instrument procedure data (SIDs and STARs). | ||
| * Pure logic library for querying US instrument procedure data (SIDs, | ||
| * STARs, and Instrument Approach Procedures) sourced from FAA CIFP. | ||
| */ | ||
@@ -5,0 +6,0 @@ export { createProcedureResolver } from './resolver.js'; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,eAAe,CAAC"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACxD,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,eAAe,CAAC"} |
+2
-1
| /** | ||
| * @packageDocumentation | ||
| * Pure logic library for querying US instrument procedure data (SIDs and STARs). | ||
| * Pure logic library for querying US instrument procedure data (SIDs, | ||
| * STARs, and Instrument Approach Procedures) sourced from FAA CIFP. | ||
| */ | ||
| export { createProcedureResolver } from './resolver.js'; |
+65
-44
@@ -1,2 +0,2 @@ | ||
| import type { Procedure, ProcedureType, ProcedureWaypoint } from '@squawk/types'; | ||
| import type { ApproachType, Procedure, ProcedureLeg, ProcedureType } from '@squawk/types'; | ||
| /** | ||
@@ -6,7 +6,7 @@ * Options for creating a procedure resolver. | ||
| export interface ProcedureResolverOptions { | ||
| /** Array of Procedure records to index for queries. */ | ||
| /** Array of {@link Procedure} records to index for queries. */ | ||
| data: Procedure[]; | ||
| } | ||
| /** | ||
| * Result of expanding a procedure into a waypoint sequence. | ||
| * Result of expanding a procedure into an ordered leg sequence. | ||
| */ | ||
@@ -16,58 +16,78 @@ export interface ProcedureExpansionResult { | ||
| procedure: Procedure; | ||
| /** Ordered sequence of waypoints for the selected route. */ | ||
| waypoints: ProcedureWaypoint[]; | ||
| /** Ordered legs for the expansion (common route + optional transition). */ | ||
| legs: ProcedureLeg[]; | ||
| } | ||
| /** | ||
| * Options for a text search query against procedure names and computer codes. | ||
| * Options for a text search query against procedure names and identifiers. | ||
| */ | ||
| export interface ProcedureSearchQuery { | ||
| /** Case-insensitive substring to match against procedure name or computer code. */ | ||
| /** Case-insensitive substring matched against name and identifier. */ | ||
| text: string; | ||
| /** Maximum number of results to return. Defaults to 20. */ | ||
| limit?: number; | ||
| /** Optional procedure type to filter by (SID or STAR). */ | ||
| /** Optional procedure type to filter by. */ | ||
| type?: ProcedureType; | ||
| /** Optional approach type filter (applied only when matching IAPs). */ | ||
| approachType?: ApproachType; | ||
| } | ||
| /** | ||
| * A stateless resolver providing instrument procedure lookup, filtering, and | ||
| * expansion methods. | ||
| * A stateless resolver providing instrument procedure lookup, filtering, | ||
| * and expansion methods against a pre-indexed dataset. | ||
| */ | ||
| export interface ProcedureResolver { | ||
| /** | ||
| * Looks up a procedure by its FAA computer code (e.g. "AALLE4", "ACCRA5"). | ||
| * Case-insensitive. Returns undefined if no match is found. | ||
| * Looks up every procedure matching a CIFP identifier. The identifier | ||
| * alone is not globally unique in CIFP data - the same name (for | ||
| * example `SARDI1` or `I04L`) is published separately for every | ||
| * adapted airport. This method returns all of them. | ||
| */ | ||
| byName(computerCode: string): Procedure | undefined; | ||
| byIdentifier(identifier: string): Procedure[]; | ||
| /** | ||
| * Finds all procedures associated with a given airport identifier. | ||
| * Case-insensitive. Returns an empty array if no match is found. | ||
| * Looks up a single procedure by (airport, identifier). Returns | ||
| * `undefined` when the airport does not adapt the identifier. | ||
| */ | ||
| byAirportAndIdentifier(airportId: string, identifier: string): Procedure | undefined; | ||
| /** | ||
| * Returns every procedure adapted at the given airport (SIDs, STARs, | ||
| * and IAPs). | ||
| */ | ||
| byAirport(airportId: string): Procedure[]; | ||
| /** | ||
| * Returns all procedures of a given type (SID or STAR). | ||
| * Returns every procedure at an airport that serves a specific runway. | ||
| * | ||
| * - For IAPs this matches the `runway` field directly. | ||
| * - For SIDs and STARs this matches procedures that publish a runway transition named `RW<runway>` (e.g. `RW04L`). | ||
| */ | ||
| byAirportAndRunway(airportId: string, runway: string): Procedure[]; | ||
| /** | ||
| * Returns every procedure of a given type. | ||
| */ | ||
| byType(type: ProcedureType): Procedure[]; | ||
| /** | ||
| * Expands a procedure into an ordered waypoint sequence. | ||
| * Returns every IAP of a given approach classification (ILS, RNAV, etc.). | ||
| */ | ||
| byApproachType(approachType: ApproachType): Procedure[]; | ||
| /** | ||
| * Expands a procedure into an ordered leg sequence. When `transitionName` | ||
| * is omitted the expansion is the procedure's first common route. When | ||
| * `transitionName` is provided, the named transition's legs are merged | ||
| * with the common route in flying order: | ||
| * | ||
| * When called without a transition name, returns the first common route. | ||
| * - SID, enroute exit transition - common route first, then transition. | ||
| * - SID, runway transition (`RW*` name) - transition first, then common route. | ||
| * - STAR, enroute entry transition - transition first, then common route. | ||
| * - STAR, runway transition (`RW*` name) - common route first, then transition. | ||
| * - IAP, approach transition - transition first, then common route (final approach segment). | ||
| * | ||
| * When called with a transition name, the transition's waypoints are | ||
| * merged with the first common route's waypoints in flying order: | ||
| * - For STARs the transition feeds into the common route, so the | ||
| * transition waypoints come first followed by the common route. | ||
| * - For SIDs the common route departs the airport before joining the | ||
| * transition, so the common route comes first followed by the | ||
| * transition. | ||
| * The connecting fix between transition and common route is | ||
| * deduplicated when both segments reference it. | ||
| * | ||
| * In both cases the connecting fix where the transition meets the common | ||
| * route is deduplicated when present. | ||
| * | ||
| * Returns undefined if the procedure or transition is not found. | ||
| * Returns `undefined` when the procedure, airport, or transition is | ||
| * not found. | ||
| */ | ||
| expand(computerCode: string, transitionName?: string): ProcedureExpansionResult | undefined; | ||
| expand(airportId: string, identifier: string, transitionName?: string): ProcedureExpansionResult | undefined; | ||
| /** | ||
| * Searches procedures by name or computer code using case-insensitive | ||
| * substring matching. Results are returned in alphabetical order by | ||
| * computer code. | ||
| * Searches procedures by name or identifier using case-insensitive | ||
| * substring matching. Results are returned sorted by airport then | ||
| * identifier. | ||
| */ | ||
@@ -77,9 +97,9 @@ search(query: ProcedureSearchQuery): Procedure[]; | ||
| /** | ||
| * Creates a stateless procedure resolver. The resolver accepts an array of | ||
| * Procedure records at initialization (typically from `@squawk/procedure-data`) | ||
| * and returns an object with methods for looking up procedures by computer code, | ||
| * airport, type, expanding route segments, and searching by name. | ||
| * Creates a stateless procedure resolver. The resolver accepts an array | ||
| * of {@link Procedure} records at initialization (typically from | ||
| * `@squawk/procedure-data`) and returns an object with query methods. | ||
| * | ||
| * The resolver builds internal indexes at creation time for fast lookups | ||
| * by computer code and by airport identifier. | ||
| * The resolver builds internal indexes at creation time for fast lookup | ||
| * by identifier, by airport, by (airport, identifier), by type, and by | ||
| * approach type. | ||
| * | ||
@@ -92,7 +112,8 @@ * ```typescript | ||
| * | ||
| * const aalle = resolver.byName('AALLE4'); | ||
| * const denProcedures = resolver.byAirport('DEN'); | ||
| * const stars = resolver.byType('STAR'); | ||
| * const expanded = resolver.expand('AALLE4', 'BBOTL'); | ||
| * const results = resolver.search({ text: 'AALLE' }); | ||
| * const allSardi = resolver.byIdentifier('SARDI1'); | ||
| * const denAalle = resolver.byAirportAndIdentifier('KDEN', 'AALLE4'); | ||
| * const jfkApproaches = resolver.byAirport('KJFK').filter((p) => p.type === 'IAP'); | ||
| * const jfk04LApproaches = resolver.byAirportAndRunway('KJFK', '04L'); | ||
| * const allIls = resolver.byApproachType('ILS'); | ||
| * const expanded = resolver.expand('KDEN', 'AALLE4', 'BBOTL'); | ||
| * ``` | ||
@@ -99,0 +120,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,SAAS,EACT,aAAa,EACb,iBAAiB,EAElB,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,4DAA4D;IAC5D,SAAS,EAAE,iBAAiB,EAAE,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,IAAI,CAAC,EAAE,aAAa,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAEpD;;;OAGG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAE1C;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,CAAC;IAEzC;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,wBAAwB,GAAG,SAAS,CAAC;IAE5F;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC;CAClD;AAOD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CA0G5F"} | ||
| {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EAET,YAAY,EAEZ,aAAa,EACd,MAAM,eAAe,CAAC;AAEvB;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,+DAA+D;IAC/D,IAAI,EAAE,SAAS,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,uCAAuC;IACvC,SAAS,EAAE,SAAS,CAAC;IACrB,2EAA2E;IAC3E,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4CAA4C;IAC5C,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,uEAAuE;IACvE,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAE9C;;;OAGG;IACH,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAC;IAErF;;;OAGG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAE1C;;;;;OAKG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,CAAC;IAEnE;;OAEG;IACH,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,SAAS,EAAE,CAAC;IAEzC;;OAEG;IACH,cAAc,CAAC,YAAY,EAAE,YAAY,GAAG,SAAS,EAAE,CAAC;IAExD;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CACJ,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,MAAM,GACtB,wBAAwB,GAAG,SAAS,CAAC;IAExC;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,oBAAoB,GAAG,SAAS,EAAE,CAAC;CAClD;AAOD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAkJ5F"} |
+142
-71
@@ -6,9 +6,9 @@ /** | ||
| /** | ||
| * Creates a stateless procedure resolver. The resolver accepts an array of | ||
| * Procedure records at initialization (typically from `@squawk/procedure-data`) | ||
| * and returns an object with methods for looking up procedures by computer code, | ||
| * airport, type, expanding route segments, and searching by name. | ||
| * Creates a stateless procedure resolver. The resolver accepts an array | ||
| * of {@link Procedure} records at initialization (typically from | ||
| * `@squawk/procedure-data`) and returns an object with query methods. | ||
| * | ||
| * The resolver builds internal indexes at creation time for fast lookups | ||
| * by computer code and by airport identifier. | ||
| * The resolver builds internal indexes at creation time for fast lookup | ||
| * by identifier, by airport, by (airport, identifier), by type, and by | ||
| * approach type. | ||
| * | ||
@@ -21,7 +21,8 @@ * ```typescript | ||
| * | ||
| * const aalle = resolver.byName('AALLE4'); | ||
| * const denProcedures = resolver.byAirport('DEN'); | ||
| * const stars = resolver.byType('STAR'); | ||
| * const expanded = resolver.expand('AALLE4', 'BBOTL'); | ||
| * const results = resolver.search({ text: 'AALLE' }); | ||
| * const allSardi = resolver.byIdentifier('SARDI1'); | ||
| * const denAalle = resolver.byAirportAndIdentifier('KDEN', 'AALLE4'); | ||
| * const jfkApproaches = resolver.byAirport('KJFK').filter((p) => p.type === 'IAP'); | ||
| * const jfk04LApproaches = resolver.byAirportAndRunway('KJFK', '04L'); | ||
| * const allIls = resolver.byApproachType('ILS'); | ||
| * const expanded = resolver.expand('KDEN', 'AALLE4', 'BBOTL'); | ||
| * ``` | ||
@@ -31,16 +32,16 @@ */ | ||
| const procedures = options.data; | ||
| const byCodeMap = new Map(); | ||
| const byIdentifierMap = new Map(); | ||
| const byAirportMap = new Map(); | ||
| const byAirportIdentifierMap = new Map(); | ||
| const sidList = []; | ||
| const starList = []; | ||
| const iapList = []; | ||
| const byApproachTypeMap = new Map(); | ||
| for (const proc of procedures) { | ||
| byCodeMap.set(proc.computerCode.toUpperCase(), proc); | ||
| const identKey = proc.identifier.toUpperCase(); | ||
| appendToMap(byIdentifierMap, identKey, proc); | ||
| for (const airport of proc.airports) { | ||
| const key = airport.toUpperCase(); | ||
| let arr = byAirportMap.get(key); | ||
| if (!arr) { | ||
| arr = []; | ||
| byAirportMap.set(key, arr); | ||
| } | ||
| arr.push(proc); | ||
| const airportKey = airport.toUpperCase(); | ||
| appendToMap(byAirportMap, airportKey, proc); | ||
| byAirportIdentifierMap.set(`${airportKey}::${identKey}`, proc); | ||
| } | ||
@@ -50,48 +51,71 @@ if (proc.type === 'SID') { | ||
| } | ||
| else { | ||
| else if (proc.type === 'STAR') { | ||
| starList.push(proc); | ||
| } | ||
| else { | ||
| iapList.push(proc); | ||
| } | ||
| if (proc.approachType !== undefined) { | ||
| appendToMap(byApproachTypeMap, proc.approachType, proc); | ||
| } | ||
| } | ||
| return { | ||
| byName(computerCode) { | ||
| return byCodeMap.get(computerCode.toUpperCase()); | ||
| byIdentifier(identifier) { | ||
| return byIdentifierMap.get(identifier.toUpperCase()) ?? []; | ||
| }, | ||
| byAirportAndIdentifier(airportId, identifier) { | ||
| return byAirportIdentifierMap.get(`${airportId.toUpperCase()}::${identifier.toUpperCase()}`); | ||
| }, | ||
| byAirport(airportId) { | ||
| return byAirportMap.get(airportId.toUpperCase()) ?? []; | ||
| }, | ||
| byAirportAndRunway(airportId, runway) { | ||
| const candidates = byAirportMap.get(airportId.toUpperCase()) ?? []; | ||
| const runwayUpper = runway.toUpperCase(); | ||
| const runwayTransitionName = `RW${runwayUpper}`; | ||
| return candidates.filter((proc) => { | ||
| if (proc.runway !== undefined && proc.runway.toUpperCase() === runwayUpper) { | ||
| return true; | ||
| } | ||
| for (const transition of proc.transitions) { | ||
| if (transition.name.toUpperCase() === runwayTransitionName) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| }); | ||
| }, | ||
| byType(type) { | ||
| return type === 'SID' ? sidList : starList; | ||
| if (type === 'SID') { | ||
| return sidList; | ||
| } | ||
| if (type === 'STAR') { | ||
| return starList; | ||
| } | ||
| return iapList; | ||
| }, | ||
| expand(computerCode, transitionName) { | ||
| const proc = byCodeMap.get(computerCode.toUpperCase()); | ||
| if (!proc) { | ||
| byApproachType(approachType) { | ||
| return byApproachTypeMap.get(approachType) ?? []; | ||
| }, | ||
| expand(airportId, identifier, transitionName) { | ||
| const proc = byAirportIdentifierMap.get(`${airportId.toUpperCase()}::${identifier.toUpperCase()}`); | ||
| if (proc === undefined) { | ||
| return undefined; | ||
| } | ||
| const commonRoute = proc.commonRoutes[0]; | ||
| if (transitionName === undefined) { | ||
| const firstRoute = proc.commonRoutes[0]; | ||
| if (!firstRoute) { | ||
| if (commonRoute === undefined) { | ||
| return undefined; | ||
| } | ||
| return { | ||
| procedure: proc, | ||
| waypoints: firstRoute.waypoints, | ||
| }; | ||
| return { procedure: proc, legs: commonRoute.legs }; | ||
| } | ||
| const upperTransition = transitionName.toUpperCase(); | ||
| const transition = proc.transitions.find((t) => t.name.toUpperCase() === upperTransition); | ||
| if (!transition) { | ||
| const transition = proc.transitions.find((t) => t.name.toUpperCase() === transitionName.toUpperCase()); | ||
| if (transition === undefined) { | ||
| return undefined; | ||
| } | ||
| const firstRoute = proc.commonRoutes[0]; | ||
| if (!firstRoute) { | ||
| return { | ||
| procedure: proc, | ||
| waypoints: transition.waypoints, | ||
| }; | ||
| if (commonRoute === undefined) { | ||
| return { procedure: proc, legs: transition.legs }; | ||
| } | ||
| const waypoints = mergeTransitionAndRoute(transition.waypoints, firstRoute, proc.type); | ||
| return { | ||
| procedure: proc, | ||
| waypoints, | ||
| }; | ||
| const legs = mergeTransitionAndRoute(transition, commonRoute, proc.type); | ||
| return { procedure: proc, legs }; | ||
| }, | ||
@@ -106,6 +130,9 @@ search(query) { | ||
| for (const proc of procedures) { | ||
| if (query.type && proc.type !== query.type) { | ||
| if (query.type !== undefined && proc.type !== query.type) { | ||
| continue; | ||
| } | ||
| if (proc.computerCode.toUpperCase().includes(needle) || | ||
| if (query.approachType !== undefined && proc.approachType !== query.approachType) { | ||
| continue; | ||
| } | ||
| if (proc.identifier.toUpperCase().includes(needle) || | ||
| proc.name.toUpperCase().includes(needle)) { | ||
@@ -115,3 +142,9 @@ results.push(proc); | ||
| } | ||
| results.sort((a, b) => a.computerCode.localeCompare(b.computerCode)); | ||
| results.sort((a, b) => { | ||
| const airportDiff = (a.airports[0] ?? '').localeCompare(b.airports[0] ?? ''); | ||
| if (airportDiff !== 0) { | ||
| return airportDiff; | ||
| } | ||
| return a.identifier.localeCompare(b.identifier); | ||
| }); | ||
| return results.slice(0, limit); | ||
@@ -122,29 +155,67 @@ }, | ||
| /** | ||
| * Merges a transition's waypoints with a common route's waypoints in the | ||
| * order aircraft fly the procedure. SIDs depart along the common route and | ||
| * then continue onto the transition; STARs fly the transition into the | ||
| * common route. The connecting fix where the two segments meet is | ||
| * deduplicated when present. | ||
| * Appends a procedure to a keyed array within a map, lazily creating | ||
| * the array on first insertion. | ||
| */ | ||
| function mergeTransitionAndRoute(transitionWaypoints, route, procedureType) { | ||
| if (transitionWaypoints.length === 0) { | ||
| return route.waypoints; | ||
| function appendToMap(map, key, procedure) { | ||
| let arr = map.get(key); | ||
| if (arr === undefined) { | ||
| arr = []; | ||
| map.set(key, arr); | ||
| } | ||
| if (route.waypoints.length === 0) { | ||
| return transitionWaypoints; | ||
| arr.push(procedure); | ||
| } | ||
| /** | ||
| * Merges a transition's legs with a common route's legs in the order | ||
| * an aircraft flies the procedure. The direction depends on both the | ||
| * procedure type and whether the transition is a runway transition | ||
| * (name prefixed with `RW`). | ||
| * | ||
| * - SID + enroute transition - common route first, transition last. | ||
| * - SID + runway transition - transition first, common route last. | ||
| * - STAR + enroute transition - transition first, common route last. | ||
| * - STAR + runway transition - common route first, transition last. | ||
| * - IAP + approach transition - transition first, common route last. | ||
| * | ||
| * The connecting fix between the two segments is deduplicated when | ||
| * both segments reference it with the same identifier. | ||
| */ | ||
| function mergeTransitionAndRoute(transition, route, procedureType) { | ||
| if (transition.legs.length === 0) { | ||
| return route.legs; | ||
| } | ||
| if (route.legs.length === 0) { | ||
| return transition.legs; | ||
| } | ||
| const isRunwayTransition = transition.name.toUpperCase().startsWith('RW'); | ||
| let transitionFirst; | ||
| if (procedureType === 'SID') { | ||
| const lastRoute = route.waypoints[route.waypoints.length - 1]; | ||
| const firstTransition = transitionWaypoints[0]; | ||
| if (lastRoute.fixIdentifier.toUpperCase() === firstTransition.fixIdentifier.toUpperCase()) { | ||
| return [...route.waypoints, ...transitionWaypoints.slice(1)]; | ||
| } | ||
| return [...route.waypoints, ...transitionWaypoints]; | ||
| transitionFirst = isRunwayTransition; | ||
| } | ||
| const lastTransition = transitionWaypoints[transitionWaypoints.length - 1]; | ||
| const firstRoute = route.waypoints[0]; | ||
| if (lastTransition.fixIdentifier.toUpperCase() === firstRoute.fixIdentifier.toUpperCase()) { | ||
| return [...transitionWaypoints, ...route.waypoints.slice(1)]; | ||
| else if (procedureType === 'STAR') { | ||
| transitionFirst = !isRunwayTransition; | ||
| } | ||
| return [...transitionWaypoints, ...route.waypoints]; | ||
| else { | ||
| transitionFirst = true; | ||
| } | ||
| if (transitionFirst) { | ||
| return joinSegments(transition.legs, route.legs); | ||
| } | ||
| return joinSegments(route.legs, transition.legs); | ||
| } | ||
| /** | ||
| * Joins two leg segments, deduplicating the connecting fix when the | ||
| * last leg of `first` and the first leg of `second` terminate at the | ||
| * same fix identifier. | ||
| */ | ||
| function joinSegments(first, second) { | ||
| const last = first[first.length - 1]; | ||
| const next = second[0]; | ||
| if (last !== undefined && | ||
| next !== undefined && | ||
| last.fixIdentifier !== undefined && | ||
| next.fixIdentifier !== undefined && | ||
| last.fixIdentifier.toUpperCase() === next.fixIdentifier.toUpperCase()) { | ||
| return [...first, ...second.slice(1)]; | ||
| } | ||
| return [...first, ...second]; | ||
| } |
+8
-5
| { | ||
| "name": "@squawk/procedures", | ||
| "version": "0.2.4", | ||
| "version": "0.4.0", | ||
| "type": "module", | ||
| "description": "Instrument procedure lookup and expansion for SIDs and STARs", | ||
| "description": "Instrument procedure lookup and expansion for SIDs, STARs, and Instrument Approach Procedures (IAPs)", | ||
| "author": "Neil Cochran", | ||
@@ -37,6 +37,6 @@ "license": "MIT", | ||
| "dependencies": { | ||
| "@squawk/types": "^0.3.1" | ||
| "@squawk/types": "^0.5.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@squawk/procedure-data": "^0.3.3", | ||
| "@squawk/procedure-data": "^0.5.0", | ||
| "@types/node": "^25.6.0" | ||
@@ -49,2 +49,4 @@ }, | ||
| "star", | ||
| "iap", | ||
| "approach", | ||
| "instrument-procedure", | ||
@@ -54,3 +56,4 @@ "departure", | ||
| "faa", | ||
| "nasr" | ||
| "cifp", | ||
| "arinc-424" | ||
| ], | ||
@@ -57,0 +60,0 @@ "publishConfig": { |
+81
-42
@@ -5,7 +5,9 @@ <h1><img src="../../assets/squawk-logo.svg" alt="squawk logo" width="48" height="48" style="vertical-align: middle"> @squawk/procedures</h1> | ||
| Pure logic library for querying US instrument procedure data. Look up SIDs | ||
| and STARs by computer code, find procedures by airport, filter by type, expand | ||
| route segments with transitions, or search by name. Contains no bundled data - | ||
| accepts an array of Procedure records at initialization. For zero-config use, | ||
| pair with `@squawk/procedure-data`. | ||
| Pure logic library for querying US instrument procedure data sourced from FAA | ||
| CIFP (Coded Instrument Flight Procedures). Covers SIDs, STARs, and Instrument | ||
| Approach Procedures (IAPs) in a unified ARINC 424 leg model. Look up by | ||
| identifier, by airport, by runway, by approach type; expand a procedure into | ||
| an ordered leg sequence; or search by name. Contains no bundled data - accepts | ||
| an array of `Procedure` records at initialization. For zero-config use, pair | ||
| with `@squawk/procedure-data`. | ||
@@ -22,24 +24,32 @@ Part of the [@squawk](https://www.npmjs.com/org/squawk) aviation library suite. See all packages on npm. | ||
| // Look up by computer code | ||
| const aalle = resolver.byName('AALLE4'); | ||
| // Look up every adaptation of an identifier across airports | ||
| const allSardi = resolver.byIdentifier('SARDI1'); | ||
| // Find all procedures for an airport | ||
| const denProcedures = resolver.byAirport('DEN'); | ||
| // Resolve a specific procedure at an airport | ||
| const aalleAtDen = resolver.byAirportAndIdentifier('KDEN', 'AALLE4'); | ||
| const ilsAtJfk = resolver.byAirportAndIdentifier('KJFK', 'I04L'); | ||
| // Get all STARs | ||
| const stars = resolver.byType('STAR'); | ||
| // Find every procedure for an airport | ||
| const jfkProcedures = resolver.byAirport('KJFK'); | ||
| // Expand a procedure (common route only) | ||
| const route = resolver.expand('AALLE4'); | ||
| if (route) { | ||
| for (const wp of route.waypoints) { | ||
| console.log(wp.fixIdentifier, wp.lat, wp.lon); | ||
| // Find procedures that serve a specific runway | ||
| const jfk04LApproaches = resolver.byAirportAndRunway('KJFK', '04L'); | ||
| // Filter by type or approach classification | ||
| const allStars = resolver.byType('STAR'); | ||
| const allIls = resolver.byApproachType('ILS'); | ||
| // Expand a procedure into an ordered leg sequence (common route only) | ||
| const expansion = resolver.expand('KDEN', 'AALLE4'); | ||
| if (expansion) { | ||
| for (const leg of expansion.legs) { | ||
| console.log(leg.pathTerminator, leg.fixIdentifier ?? '(no fix)'); | ||
| } | ||
| } | ||
| // Expand with a named transition | ||
| const withTransition = resolver.expand('AALLE4', 'BBOTL'); | ||
| // Expand with a named transition (transition + common route merged in flying order) | ||
| const withTransition = resolver.expand('KDEN', 'AALLE4', 'BBOTL'); | ||
| // Search by name or code | ||
| const results = resolver.search({ text: 'AALLE' }); | ||
| // Search by name or identifier | ||
| const results = resolver.search({ text: 'AALLE', type: 'STAR' }); | ||
| ``` | ||
@@ -59,49 +69,78 @@ | ||
| Creates a resolver object from an array of Procedure records. | ||
| Creates a resolver object from an array of `Procedure` records. | ||
| **Parameters:** | ||
| - `options.data` - an array of `Procedure` objects (from `@squawk/types`) | ||
| - `options.data` - an array of `Procedure` objects (from `@squawk/types`). | ||
| **Returns:** `ProcedureResolver` - an object with the lookup methods described below. | ||
| ### `resolver.byName(computerCode)` | ||
| ### `resolver.byIdentifier(identifier)` | ||
| Looks up a procedure by its FAA computer code (e.g. "AALLE4", "ACCRA5"). | ||
| Case-insensitive. Returns `Procedure | undefined`. | ||
| Looks up every procedure matching a CIFP identifier (case-insensitive). CIFP | ||
| identifiers are not globally unique - the same identifier (for example | ||
| `SARDI1` or `I04L`) is published separately for each adapted airport, so this | ||
| returns all matches. Returns `Procedure[]`. | ||
| ### `resolver.byAirportAndIdentifier(airportId, identifier)` | ||
| Resolves a single procedure by (airport, identifier). Case-insensitive for | ||
| both arguments. Returns `Procedure | undefined`. | ||
| ### `resolver.byAirport(airportId)` | ||
| Finds all procedures associated with a given airport identifier. | ||
| Returns every procedure (SID, STAR, or IAP) adapted at the given airport. | ||
| Case-insensitive. Returns `Procedure[]`. | ||
| ### `resolver.byAirportAndRunway(airportId, runway)` | ||
| Returns procedures at an airport that serve a specific runway. For IAPs, the | ||
| match is on the `runway` field directly. For SIDs and STARs, the match is on a | ||
| runway transition named `RW<runway>` (for example `RW04L`). Case-insensitive. | ||
| Returns `Procedure[]`. | ||
| ### `resolver.byType(type)` | ||
| Returns all procedures of a given type. Pass `'SID'` or `'STAR'`. | ||
| Returns every procedure of a given type. Pass `'SID'`, `'STAR'`, or `'IAP'`. | ||
| Returns `Procedure[]`. | ||
| ### `resolver.expand(computerCode, transitionName?)` | ||
| ### `resolver.byApproachType(approachType)` | ||
| Expands a procedure into an ordered waypoint sequence. | ||
| Returns every IAP of a given approach classification (`'ILS'`, `'LOC'`, | ||
| `'LOC_BC'`, `'RNAV'`, `'RNAV_RNP'`, `'VOR'`, `'VOR_DME'`, `'NDB'`, `'NDB_DME'`, | ||
| `'TACAN'`, `'GLS'`, `'IGS'`, `'LDA'`, `'SDF'`, `'GPS'`, `'FMS'`, `'MLS'`). | ||
| Returns `Procedure[]`. | ||
| - Without a transition: returns the first common route's waypoints | ||
| - With a transition: returns the transition waypoints merged with the first | ||
| common route, deduplicating the connecting fix | ||
| ### `resolver.expand(airportId, identifier, transitionName?)` | ||
| Returns `ProcedureExpansionResult | undefined`. The result contains: | ||
| Expands a procedure into an ordered leg sequence. Without a transition name, | ||
| returns the procedure's first common route. With a transition name, merges the | ||
| named transition's legs with the common route in flying order: | ||
| - `procedure` - the full Procedure record | ||
| - `waypoints` - the ordered waypoint sequence | ||
| - **SID + enroute exit transition** - common route first, then transition. | ||
| - **SID + runway transition** (`RW*` name) - transition first, then common route. | ||
| - **STAR + enroute entry transition** - transition first, then common route. | ||
| - **STAR + runway transition** - common route first, then transition. | ||
| - **IAP + approach transition** - transition first, then final approach segment. | ||
| The connecting fix between transition and common route is deduplicated when | ||
| both segments reference it. | ||
| Returns `ProcedureExpansionResult | undefined`, containing: | ||
| - `procedure` - the full `Procedure` record. | ||
| - `legs` - the ordered `ProcedureLeg` sequence. | ||
| ### `resolver.search(query)` | ||
| Searches procedures by name or computer code using case-insensitive substring | ||
| matching. Results are returned in alphabetical order by computer code. | ||
| Searches procedures by name or identifier using case-insensitive substring | ||
| matching. Results are sorted by airport then identifier. | ||
| | Property | Type | Description | | ||
| | -------- | ------------- | ----------------------------------------------------------------- | | ||
| | `text` | string | Case-insensitive substring to match against name or computer code | | ||
| | `limit` | number | Optional. Maximum number of results. Defaults to 20 | | ||
| | `type` | ProcedureType | Optional. When provided, only this procedure type is returned | | ||
| | Property | Type | Description | | ||
| | -------------- | ------------- | -------------------------------------------------------------- | | ||
| | `text` | string | Case-insensitive substring to match against name or identifier | | ||
| | `limit` | number | Optional. Maximum number of results. Defaults to 20 | | ||
| | `type` | ProcedureType | Optional. Restrict to `'SID'`, `'STAR'`, or `'IAP'` only | | ||
| | `approachType` | ApproachType | Optional. Restrict to IAPs of a given approach classification | | ||
| Returns `Procedure[]`. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
22783
41.19%344
37.6%144
37.14%1
Infinity%+ Added
- Removed
Updated