@squawk/airspace 0.5.1 → 0.6.1

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
@@ -12,6 +12,13 @@ Part of the [@squawk](https://www.npmjs.com/org/squawk) aviation library suite.
12
12
 
13
13
  - Class B, C, D, and E controlled airspace (E2 through E7 subtypes)
14
14
  - Special Use Airspace: MOAs, restricted, prohibited, warning, alert, and national security areas
15
+ - ARTCC (Air Route Traffic Control Center) lateral boundaries for every
16
+ US-controlled center, published per stratum: LOW/HIGH for the 20 CONUS
17
+ centers and Anchorage (ZAN), UTA for Oakland (ZOA) only, and oceanic
18
+ CTA/FIR strata for the Pacific (ZAK, ZAP), Atlantic (ZWY), and the
19
+ contiguous overlays on ZAN, ZHN, ZHU, ZMA, ZSU. Within-stratum sector
20
+ polygons and per-sector enroute frequencies are not included - the FAA
21
+ does not publish either data set in machine-readable form.
15
22
 
16
23
  ## Usage
17
24
 
@@ -30,6 +37,9 @@ for (const f of overhead) {
30
37
 
31
38
  // Get every shell associated with an airport (for drawing the full wedding cake)
32
39
  const laxShells = resolver.byAirport('LAX');
40
+
41
+ // Get every ARTCC center boundary (one feature per stratum)
42
+ const ny = resolver.byArtcc('ZNY');
33
43
  ```
34
44
 
35
45
  Consumers who have their own GeoJSON airspace data can use this package standalone:
@@ -43,14 +53,18 @@ const resolver = createAirspaceResolver({ data: myGeoJson });
43
53
  ## How it works
44
54
 
45
55
  `createAirspaceResolver` parses the GeoJSON FeatureCollection at initialization and
46
- returns a resolver object with two methods:
56
+ returns a resolver object with three methods:
47
57
 
48
58
  - `query(AirspaceQuery)` - returns features containing the given position and
49
59
  altitude, via a ray casting point-in-polygon test combined with a vertical
50
60
  floor/ceiling comparison.
51
- - `byAirport(identifier, types?)` - returns every feature whose `identifier`
52
- matches (case-insensitive). For Class B/C/D/E2 this groups all sectors of the
53
- airspace around a given airport regardless of the point of interest.
61
+ - `byAirport(identifier, types?)` - returns every non-ARTCC feature whose
62
+ `identifier` matches (case-insensitive). For Class B/C/D/E2 this groups all
63
+ sectors of the airspace around a given airport regardless of the point of
64
+ interest.
65
+ - `byArtcc(identifier, stratum?)` - returns every ARTCC feature for the given
66
+ 3-letter center code (e.g. `"ZNY"`), optionally filtered to a single
67
+ stratum (`LOW`, `HIGH`, `UTA`, `CTA`, `FIR`, or `CTA/FIR`).
54
68
 
55
69
  All matching features are returned as `AirspaceFeature` objects (from `@squawk/types`),
56
70
  including the full polygon boundary coordinates.
@@ -87,8 +101,8 @@ Creates a resolver from a GeoJSON dataset.
87
101
 
88
102
  - `options.data` - a GeoJSON `FeatureCollection` with airspace features
89
103
 
90
- **Returns:** `AirspaceResolver` - an object exposing `query(AirspaceQuery)` and
91
- `byAirport(identifier, types?)` methods.
104
+ **Returns:** `AirspaceResolver` - an object exposing `query(AirspaceQuery)`,
105
+ `byAirport(identifier, types?)`, and `byArtcc(identifier, stratum?)` methods.
92
106
 
93
107
  ### `AirspaceQuery`
94
108
 
@@ -111,7 +125,7 @@ const controlled = resolver.query({
111
125
 
112
126
  ### `resolver.byAirport(identifier, types?)`
113
127
 
114
- Returns every airspace feature whose `identifier` property matches. For
128
+ Returns every non-ARTCC airspace feature whose `identifier` property matches. For
115
129
  Class B/C/D/E2 this is the associated airport's FAA location identifier
116
130
  (e.g. "JFK" for the NY Class B). For Special Use Airspace this is the NASR
117
131
  designator (e.g. "R-2508"). Lookup is case-insensitive. ICAO-prefixed codes
@@ -124,3 +138,45 @@ const jfkShells = resolver.byAirport('JFK');
124
138
  // Only the Class D for a towered field
125
139
  const safClassD = resolver.byAirport('SAF', new Set(['CLASS_D']));
126
140
  ```
141
+
142
+ ### `resolver.byArtcc(identifier, stratum?)`
143
+
144
+ Returns every ARTCC feature for the given three-letter center code
145
+ (e.g. `"ZNY"`, `"ZBW"`). Each US ARTCC is published as multiple features -
146
+ one per stratum (`LOW`, `HIGH`, plus oceanic `UTA`, `CTA`, `FIR`, or
147
+ `CTA/FIR` where applicable) - because the lateral extent can vary between
148
+ strata. Pass an optional stratum filter to narrow results.
149
+
150
+ ```typescript
151
+ // Both LOW and HIGH boundaries for the New York center
152
+ const zny = resolver.byArtcc('ZNY');
153
+
154
+ // Just the high-altitude boundary for the Boston center
155
+ const zbwHigh = resolver.byArtcc('ZBW', 'HIGH');
156
+ ```
157
+
158
+ ### ARTCC altitude bounds
159
+
160
+ ARTCC features carry stratum-aligned floor/ceiling values for use with
161
+ `query()`:
162
+
163
+ | Stratum | Floor | Ceiling |
164
+ | --------- | ------------- | ------------- |
165
+ | `LOW` | SFC | 18,000 ft MSL |
166
+ | `HIGH` | 18,000 ft MSL | 60,000 ft MSL |
167
+ | `UTA` | 60,000 ft MSL | 99,999 ft MSL |
168
+ | `CTA` | SFC | 99,999 ft MSL |
169
+ | `FIR` | SFC | 99,999 ft MSL |
170
+ | `CTA/FIR` | SFC | 99,999 ft MSL |
171
+
172
+ These are operational stratum approximations rather than legal limits.
173
+ The `99,999` ceiling is a sentinel meaning "effectively unlimited" -
174
+ queries above FL600 will still match the relevant stratum.
175
+
176
+ Oceanic FIR boundaries that cross the 180th meridian (ZAK FIR/CTA and ZAP
177
+ FIR/CTA in the central Pacific) are split at the antimeridian during the
178
+ data build, so each emitted feature has coordinates within the standard
179
+ `[-180, 180]` range. A single source stratum produces two features in
180
+ those cases - one for the eastern sub-polygon and one for the western - but
181
+ both share the same `identifier` and `artccStratum`, so `byArtcc("ZAK")`
182
+ returns them together.
@@ -1,5 +1,5 @@
1
1
  import type { FeatureCollection } from 'geojson';
2
- import type { AirspaceFeature, AirspaceType } from '@squawk/types';
2
+ import type { AirspaceFeature, AirspaceType, ArtccStratum } from '@squawk/types';
3
3
  /**
4
4
  * A query describing a geographic position and altitude to resolve
5
5
  * against loaded airspace data.
@@ -50,12 +50,33 @@ export interface AirspaceResolver {
50
50
  * only the bare identifier - ICAO-prefixed codes like "KJFK" will not
51
51
  * match; resolve to an FAA ID first via `@squawk/airports` if needed.
52
52
  *
53
+ * Note: ARTCC features share the identifier-keyed index but are typically
54
+ * looked up via {@link byArtcc} for clearer ergonomics. ARTCC features are
55
+ * excluded from `byAirport` results since their identifier is a center code
56
+ * (e.g. "ZNY"), not an airport identifier.
57
+ *
53
58
  * @param identifier - FAA identifier or NASR designator.
54
59
  * @param types - Optional type filter. Only features whose type is in this
55
- * set are returned. When omitted, all types are returned.
60
+ * set are returned. When omitted, all non-ARTCC types are
61
+ * returned.
56
62
  * @returns All features whose identifier matches, or an empty array.
57
63
  */
58
64
  byAirport(identifier: string, types?: ReadonlySet<AirspaceType>): AirspaceFeature[];
65
+ /**
66
+ * Returns every ARTCC feature associated with the given center identifier,
67
+ * independent of position or altitude. Lookup is case-insensitive.
68
+ *
69
+ * Each US ARTCC is published as multiple features - one per stratum (LOW,
70
+ * HIGH, UTA, CTA, FIR, CTA/FIR) - because the lateral extent can vary
71
+ * between strata. Pass an optional stratum filter to narrow results to a
72
+ * single stratum.
73
+ *
74
+ * @param identifier - Three-letter ARTCC code (e.g. "ZNY", "ZBW").
75
+ * @param stratum - Optional stratum filter. When provided, only features
76
+ * whose `artccStratum` matches are returned.
77
+ * @returns All matching ARTCC features, or an empty array.
78
+ */
79
+ byArtcc(identifier: string, stratum?: ArtccStratum): AirspaceFeature[];
59
80
  }
60
81
  /**
61
82
  * Creates a stateless airspace resolver. The resolver accepts a GeoJSON
@@ -81,6 +102,7 @@ export interface AirspaceResolver {
81
102
  * const resolver = createAirspaceResolver({ data: usBundledAirspace });
82
103
  * const overhead = resolver.query({ lat: 33.9425, lon: -118.4081, altitudeFt: 3000 });
83
104
  * const laxShells = resolver.byAirport('LAX');
105
+ * const newYorkArtcc = resolver.byArtcc('ZNY');
84
106
  * ```
85
107
  */
86
108
  export declare function createAirspaceResolver(options: AirspaceResolverOptions): AirspaceResolver;
@@ -1 +1 @@
1
- {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAW,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAiB,MAAM,eAAe,CAAC;AAIlF;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,8DAA8D;IAC9D,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE,CAAC;IAE/C;;;;;;;;;;;;;;OAcG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,GAAG,eAAe,EAAE,CAAC;CACrF;AAmDD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAuDzF"}
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAW,MAAM,SAAS,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAiB,YAAY,EAAE,MAAM,eAAe,CAAC;AAIhG;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,wEAAwE;IACxE,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC,8DAA8D;IAC9D,IAAI,EAAE,iBAAiB,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,KAAK,CAAC,KAAK,EAAE,aAAa,GAAG,eAAe,EAAE,CAAC;IAE/C;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,GAAG,eAAe,EAAE,CAAC;IAEpF;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,eAAe,EAAE,CAAC;CACxE;AAoDD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CAmEzF"}
package/dist/resolver.js CHANGED
@@ -28,6 +28,7 @@ function parseFeature(geoFeature) {
28
28
  state: props.state ?? null,
29
29
  controllingFacility: props.controllingFacility ?? null,
30
30
  scheduleDescription: props.scheduleDescription ?? null,
31
+ artccStratum: props.artccStratum ?? null,
31
32
  };
32
33
  return { feature, ring, boundingBox: polygon.boundingBox(ring) };
33
34
  }
@@ -55,6 +56,7 @@ function parseFeature(geoFeature) {
55
56
  * const resolver = createAirspaceResolver({ data: usBundledAirspace });
56
57
  * const overhead = resolver.query({ lat: 33.9425, lon: -118.4081, altitudeFt: 3000 });
57
58
  * const laxShells = resolver.byAirport('LAX');
59
+ * const newYorkArtcc = resolver.byArtcc('ZNY');
58
60
  * ```
59
61
  */
60
62
  export function createAirspaceResolver(options) {
@@ -103,9 +105,20 @@ export function createAirspaceResolver(options) {
103
105
  return [];
104
106
  }
105
107
  if (types === undefined) {
106
- return bucket.slice();
108
+ return bucket.filter((f) => f.type !== 'ARTCC');
107
109
  }
108
- return bucket.filter((f) => types.has(f.type));
110
+ return bucket.filter((f) => f.type !== 'ARTCC' && types.has(f.type));
111
+ },
112
+ byArtcc(identifier, stratum) {
113
+ const bucket = byIdentifierMap.get(identifier.toUpperCase());
114
+ if (bucket === undefined) {
115
+ return [];
116
+ }
117
+ const artccFeatures = bucket.filter((f) => f.type === 'ARTCC');
118
+ if (stratum === undefined) {
119
+ return artccFeatures;
120
+ }
121
+ return artccFeatures.filter((f) => f.artccStratum === stratum);
109
122
  },
110
123
  };
111
124
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squawk/airspace",
3
- "version": "0.5.1",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "description": "Pure logic library for querying US airspace geometry by position and altitude",
6
6
  "author": "Neil Cochran",
@@ -9,7 +9,7 @@
9
9
  "repository": {
10
10
  "type": "git",
11
11
  "url": "git+https://github.com/neilcochran/squawk.git",
12
- "directory": "packages/airspace"
12
+ "directory": "packages/libs/airspace"
13
13
  },
14
14
  "engines": {
15
15
  "node": ">=22"
@@ -34,11 +34,11 @@
34
34
  "lint:pack": "publint && attw --pack . --profile esm-only"
35
35
  },
36
36
  "dependencies": {
37
- "@squawk/geo": "^0.3.1",
38
- "@squawk/types": "^0.6.0"
37
+ "@squawk/geo": "^0.3.3",
38
+ "@squawk/types": "^0.7.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@squawk/airspace-data": "^0.3.2",
41
+ "@squawk/airspace-data": "^0.5.1",
42
42
  "@types/node": "^25.6.0"
43
43
  },
44
44
  "keywords": [