@squawk/airspace 0.7.1 → 0.8.6
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 +92 -2
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/resolver.d.ts +116 -0
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +48 -1
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ The `/browser` entry is identical to the main entry; the separate subpath exists
|
|
|
67
67
|
## How it works
|
|
68
68
|
|
|
69
69
|
`createAirspaceResolver` parses the GeoJSON FeatureCollection at initialization and
|
|
70
|
-
returns a resolver object with
|
|
70
|
+
returns a resolver object with the following methods:
|
|
71
71
|
|
|
72
72
|
- `query(AirspaceQuery)` - returns features containing the given position and
|
|
73
73
|
altitude, via a ray casting point-in-polygon test combined with a vertical
|
|
@@ -79,6 +79,19 @@ returns a resolver object with three methods:
|
|
|
79
79
|
- `byArtcc(identifier, stratum?)` - returns every ARTCC feature for the given
|
|
80
80
|
3-letter center code (e.g. `"ZNY"`), optionally filtered to a single
|
|
81
81
|
stratum (`LOW`, `HIGH`, `UTA`, `CTA`, `FIR`, or `CTA/FIR`).
|
|
82
|
+
- `byIdentifier(identifier, options?)` - type-agnostic identifier lookup that
|
|
83
|
+
spans both partitions in one call, with an optional `types` inclusion
|
|
84
|
+
filter and `includeArtcc` toggle.
|
|
85
|
+
- `byCentroid({ lon, lat, toleranceDeg? })` - returns features whose polygon
|
|
86
|
+
centroid lies within tolerance of the query coordinates. Useful for
|
|
87
|
+
resolving features whose `identifier` is empty (some Class E5 surfaces),
|
|
88
|
+
where the centroid is the only stable handle.
|
|
89
|
+
- `withinBbox(bbox)` - returns features whose pre-indexed bounding box
|
|
90
|
+
overlaps the query bbox. Reuses the bounding box cached at init time
|
|
91
|
+
rather than recomputing per call.
|
|
92
|
+
- `forEachIndexed(callback)` - read-only iteration over the indexed corpus
|
|
93
|
+
with positional `(feature, ring, boundingBox)` arguments, for callers
|
|
94
|
+
that need to filter the corpus without reparsing the source GeoJSON.
|
|
82
95
|
|
|
83
96
|
All matching features are returned as `AirspaceFeature` objects (from `@squawk/types`),
|
|
84
97
|
including the full polygon boundary coordinates.
|
|
@@ -116,7 +129,9 @@ Creates a resolver from a GeoJSON dataset.
|
|
|
116
129
|
- `options.data` - a GeoJSON `FeatureCollection` with airspace features
|
|
117
130
|
|
|
118
131
|
**Returns:** `AirspaceResolver` - an object exposing `query(AirspaceQuery)`,
|
|
119
|
-
`byAirport(identifier, types?)`,
|
|
132
|
+
`byAirport(identifier, types?)`, `byArtcc(identifier, stratum?)`,
|
|
133
|
+
`byIdentifier(identifier, options?)`, `byCentroid(query)`, `withinBbox(bbox)`,
|
|
134
|
+
and `forEachIndexed(callback)` methods.
|
|
120
135
|
|
|
121
136
|
### `AirspaceQuery`
|
|
122
137
|
|
|
@@ -169,6 +184,81 @@ const zny = resolver.byArtcc('ZNY');
|
|
|
169
184
|
const zbwHigh = resolver.byArtcc('ZBW', 'HIGH');
|
|
170
185
|
```
|
|
171
186
|
|
|
187
|
+
### `resolver.byIdentifier(identifier, options?)`
|
|
188
|
+
|
|
189
|
+
Type-agnostic identifier lookup. Returns every feature whose `identifier`
|
|
190
|
+
matches, spanning both the ARTCC and non-ARTCC partitions in one call.
|
|
191
|
+
Reach for this when the airspace type is not known up-front (e.g. parsed
|
|
192
|
+
from a URL); for ergonomic shortcuts, the partition-specific `byAirport` /
|
|
193
|
+
`byArtcc` wrappers stay available.
|
|
194
|
+
|
|
195
|
+
| Option | Type | Description |
|
|
196
|
+
| -------------- | -------------------------- | ----------------------------------------------------------------------------------------------- |
|
|
197
|
+
| `types` | ReadonlySet\<AirspaceType> | Optional. When provided, acts as the authoritative inclusion filter; `includeArtcc` is ignored. |
|
|
198
|
+
| `includeArtcc` | boolean | Defaults to `true`. When `false` and `types` is omitted, ARTCC features are excluded. |
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
// Every feature for the identifier, ARTCC included
|
|
202
|
+
const all = resolver.byIdentifier('ZNY');
|
|
203
|
+
|
|
204
|
+
// Only the non-ARTCC partition
|
|
205
|
+
const nonArtcc = resolver.byIdentifier('ZNY', { includeArtcc: false });
|
|
206
|
+
|
|
207
|
+
// Only matching types
|
|
208
|
+
const onlyClassB = resolver.byIdentifier('JFK', { types: new Set(['CLASS_B']) });
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### `resolver.byCentroid(query)`
|
|
212
|
+
|
|
213
|
+
Returns every feature whose polygon centroid is within `toleranceDeg` of
|
|
214
|
+
`(lon, lat)`. The centroid is computed per call (not cached) so this is
|
|
215
|
+
O(n) over the corpus - suitable for occasional URL-driven lookups, not
|
|
216
|
+
hot loops. Useful for resolving features with an empty `identifier` (some
|
|
217
|
+
Class E5 surfaces), where the centroid is the fallback URL handle.
|
|
218
|
+
|
|
219
|
+
| Property | Type | Description |
|
|
220
|
+
| -------------- | ------ | -------------------------------------------------------------------------------------------------------- |
|
|
221
|
+
| `lon` | number | Longitude in decimal degrees (WGS84) |
|
|
222
|
+
| `lat` | number | Latitude in decimal degrees (WGS84) |
|
|
223
|
+
| `toleranceDeg` | number | Optional. Centroid match tolerance in degrees, applied independently to lon and lat. Defaults to 0.0001. |
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
const matches = resolver.byCentroid({ lon: -118.4081, lat: 33.9425 });
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### `resolver.withinBbox(bbox)`
|
|
230
|
+
|
|
231
|
+
Returns every feature whose pre-indexed bounding box overlaps the query
|
|
232
|
+
bbox. Reuses the bounding box cached at init time, so this is suitable
|
|
233
|
+
for tight loops over the corpus. Bounding-box overlap is a coarse spatial
|
|
234
|
+
filter: callers that need true polygon-polygon intersection should follow
|
|
235
|
+
up with their own geometry test on the returned features.
|
|
236
|
+
|
|
237
|
+
```typescript
|
|
238
|
+
const overlapping = resolver.withinBbox({
|
|
239
|
+
minLon: -119,
|
|
240
|
+
minLat: 33,
|
|
241
|
+
maxLon: -118,
|
|
242
|
+
maxLat: 35,
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### `resolver.forEachIndexed(callback)`
|
|
247
|
+
|
|
248
|
+
Read-only iteration over the indexed corpus, invoking `callback` once per
|
|
249
|
+
feature with positional `(feature, ring, boundingBox)`. Exposes the
|
|
250
|
+
resolver's pre-parsed shape so callers that need to filter the corpus
|
|
251
|
+
themselves do not have to reparse the source GeoJSON or recompute geometry
|
|
252
|
+
per call. The `ring` and `boundingBox` arguments are the resolver's
|
|
253
|
+
internal caches and must not be mutated.
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
resolver.forEachIndexed((feature, ring, boundingBox) => {
|
|
257
|
+
// ring is the parsed exterior ring (number[][]).
|
|
258
|
+
// boundingBox is the pre-computed axis-aligned bbox.
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
172
262
|
### ARTCC altitude bounds
|
|
173
263
|
|
|
174
264
|
ARTCC features carry stratum-aligned floor/ceiling values for use with
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* Pure logic library for point-in-airspace queries against US airspace polygons.
|
|
4
|
+
*/
|
|
1
5
|
export { createAirspaceResolver } from './resolver.js';
|
|
2
|
-
export type { AirspaceResolver, AirspaceResolverOptions, AirspaceQuery } from './resolver.js';
|
|
6
|
+
export type { AirspaceResolver, AirspaceResolverOptions, AirspaceQuery, AirspaceCentroidQuery, AirspaceByIdentifierOptions, } from './resolver.js';
|
|
3
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACvD,YAAY,EACV,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACb,qBAAqB,EACrB,2BAA2B,GAC5B,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/resolver.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { FeatureCollection } from 'geojson';
|
|
2
|
+
import { type BoundingBox } from '@squawk/geo';
|
|
2
3
|
import type { AirspaceFeature, AirspaceType, ArtccStratum } from '@squawk/types';
|
|
3
4
|
/**
|
|
4
5
|
* A query describing a geographic position and altitude to resolve
|
|
@@ -21,6 +22,48 @@ export interface AirspaceQuery {
|
|
|
21
22
|
*/
|
|
22
23
|
types?: ReadonlySet<AirspaceType>;
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* A query describing a geographic position and tolerance for a centroid-based
|
|
27
|
+
* airspace lookup.
|
|
28
|
+
*/
|
|
29
|
+
export interface AirspaceCentroidQuery {
|
|
30
|
+
/** Longitude in decimal degrees (WGS84). */
|
|
31
|
+
lon: number;
|
|
32
|
+
/** Latitude in decimal degrees (WGS84). */
|
|
33
|
+
lat: number;
|
|
34
|
+
/**
|
|
35
|
+
* Optional tolerance in degrees for the centroid match. A feature is
|
|
36
|
+
* returned when both `|centroidLon - lon|` and `|centroidLat - lat|`
|
|
37
|
+
* fall below this value. Defaults to `0.0001` (~11 m), generous enough to
|
|
38
|
+
* absorb floating-point round-trips through URL parsing for centroids
|
|
39
|
+
* encoded to ~5 decimal places.
|
|
40
|
+
*/
|
|
41
|
+
toleranceDeg?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Options accepted by {@link AirspaceResolver.byIdentifier}.
|
|
45
|
+
*/
|
|
46
|
+
export interface AirspaceByIdentifierOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Optional set of airspace types to include in the results. When provided,
|
|
49
|
+
* acts as an inclusion filter and overrides the partition between ARTCC and
|
|
50
|
+
* non-ARTCC features: callers who want ARTCC results in addition to the
|
|
51
|
+
* usual partition include `'ARTCC'` in this set explicitly, and callers who
|
|
52
|
+
* want only non-ARTCC results pass a set of the non-ARTCC types. When
|
|
53
|
+
* omitted, every type is eligible (subject to {@link includeArtcc}).
|
|
54
|
+
*/
|
|
55
|
+
types?: ReadonlySet<AirspaceType>;
|
|
56
|
+
/**
|
|
57
|
+
* When `true` (the default), ARTCC features for the identifier are included
|
|
58
|
+
* alongside the airport-associated and SUA features. When `false`, ARTCC
|
|
59
|
+
* features are excluded - useful when you only want the non-ARTCC partition
|
|
60
|
+
* for an identifier without enumerating every non-ARTCC type yourself.
|
|
61
|
+
*
|
|
62
|
+
* Ignored when {@link types} is provided: `types` is the authoritative
|
|
63
|
+
* inclusion list in that case.
|
|
64
|
+
*/
|
|
65
|
+
includeArtcc?: boolean;
|
|
66
|
+
}
|
|
24
67
|
/**
|
|
25
68
|
* Options for creating an airspace resolver.
|
|
26
69
|
*/
|
|
@@ -77,6 +120,76 @@ export interface AirspaceResolver {
|
|
|
77
120
|
* @returns All matching ARTCC features, or an empty array.
|
|
78
121
|
*/
|
|
79
122
|
byArtcc(identifier: string, stratum?: ArtccStratum): AirspaceFeature[];
|
|
123
|
+
/**
|
|
124
|
+
* Returns every airspace feature whose polygon centroid lies within the
|
|
125
|
+
* given tolerance of the query coordinates. Useful for resolving features
|
|
126
|
+
* that have an empty `identifier` (some Class E5 surfaces) and therefore
|
|
127
|
+
* have no stable identifier-keyed lookup - the polygon centroid is the
|
|
128
|
+
* fallback handle.
|
|
129
|
+
*
|
|
130
|
+
* Reach for this when you have a centroid encoded into a URL or other
|
|
131
|
+
* external string and want to recover the original feature(s); for
|
|
132
|
+
* identifier-keyed lookups, prefer {@link byIdentifier} (or the more
|
|
133
|
+
* specific {@link byAirport} / {@link byArtcc}). Centroid is computed
|
|
134
|
+
* per call - no internal caching - so this is O(n) over the indexed
|
|
135
|
+
* corpus, suitable for occasional URL-driven lookups but not for
|
|
136
|
+
* tight loops.
|
|
137
|
+
*
|
|
138
|
+
* @param query - Query coordinates and optional tolerance.
|
|
139
|
+
* @returns All features whose centroid is within tolerance, in dataset order.
|
|
140
|
+
*/
|
|
141
|
+
byCentroid(query: AirspaceCentroidQuery): AirspaceFeature[];
|
|
142
|
+
/**
|
|
143
|
+
* Returns every airspace feature for the given identifier across both the
|
|
144
|
+
* ARTCC and non-ARTCC partitions, independent of position or altitude.
|
|
145
|
+
* Lookup is case-insensitive.
|
|
146
|
+
*
|
|
147
|
+
* Reach for this when you have an identifier whose airspace type is not
|
|
148
|
+
* known up-front (e.g. parsed from a URL) and you want a single call that
|
|
149
|
+
* returns the matching feature(s) regardless of partition. For ergonomic
|
|
150
|
+
* shortcuts when the partition is known, prefer {@link byAirport} (returns
|
|
151
|
+
* non-ARTCC features only) or {@link byArtcc} (returns ARTCC features
|
|
152
|
+
* only) - those wrappers encode the common "shells for this airport" /
|
|
153
|
+
* "stratums for this center" questions and stay available alongside this
|
|
154
|
+
* type-agnostic form.
|
|
155
|
+
*
|
|
156
|
+
* @param identifier - FAA identifier, NASR designator, or ARTCC code.
|
|
157
|
+
* @param options - Optional `types` inclusion filter and `includeArtcc`
|
|
158
|
+
* toggle. See {@link AirspaceByIdentifierOptions}.
|
|
159
|
+
* @returns All matching features, or an empty array.
|
|
160
|
+
*/
|
|
161
|
+
byIdentifier(identifier: string, options?: AirspaceByIdentifierOptions): AirspaceFeature[];
|
|
162
|
+
/**
|
|
163
|
+
* Returns every airspace feature whose pre-indexed bounding box overlaps
|
|
164
|
+
* the given bounding box. Reuses the bounding box computed once at
|
|
165
|
+
* resolver creation time rather than recomputing per call, so this is
|
|
166
|
+
* suitable for tight loops over the corpus (e.g. a chip rebuild against
|
|
167
|
+
* a selection footprint).
|
|
168
|
+
*
|
|
169
|
+
* Bounding-box overlap is a coarse spatial filter: it matches any feature
|
|
170
|
+
* whose axis-aligned rectangle intersects the query rectangle, including
|
|
171
|
+
* features whose actual polygon does not. Callers that need true
|
|
172
|
+
* polygon-polygon intersection should follow up with their own geometry
|
|
173
|
+
* test on the returned features.
|
|
174
|
+
*
|
|
175
|
+
* @param bbox - Query bounding box.
|
|
176
|
+
* @returns All features whose pre-indexed bounding box overlaps, in dataset order.
|
|
177
|
+
*/
|
|
178
|
+
withinBbox(bbox: BoundingBox): AirspaceFeature[];
|
|
179
|
+
/**
|
|
180
|
+
* Iterates the indexed corpus in dataset order, invoking `callback` once
|
|
181
|
+
* per feature with the parsed feature, its exterior ring, and its
|
|
182
|
+
* pre-computed bounding box. Exposes the resolver's pre-parsed shape so
|
|
183
|
+
* callers that need to filter the corpus themselves do not have to
|
|
184
|
+
* reparse the source GeoJSON or recompute geometry per call.
|
|
185
|
+
*
|
|
186
|
+
* The `ring` and `boundingBox` arguments are the resolver's internal
|
|
187
|
+
* caches and must not be mutated by the callback - copy them first if a
|
|
188
|
+
* mutation is needed.
|
|
189
|
+
*
|
|
190
|
+
* @param callback - Function invoked once per indexed feature.
|
|
191
|
+
*/
|
|
192
|
+
forEachIndexed(callback: (feature: AirspaceFeature, ring: readonly number[][], boundingBox: BoundingBox) => void): void;
|
|
80
193
|
}
|
|
81
194
|
/**
|
|
82
195
|
* Creates a stateless airspace resolver. The resolver accepts a GeoJSON
|
|
@@ -103,6 +216,9 @@ export interface AirspaceResolver {
|
|
|
103
216
|
* const overhead = resolver.query({ lat: 33.9425, lon: -118.4081, altitudeFt: 3000 });
|
|
104
217
|
* const laxShells = resolver.byAirport('LAX');
|
|
105
218
|
* const newYorkArtcc = resolver.byArtcc('ZNY');
|
|
219
|
+
* const anyZnyFeature = resolver.byIdentifier('ZNY');
|
|
220
|
+
* const nearbyByCentroid = resolver.byCentroid({ lon: -118.4, lat: 33.9 });
|
|
221
|
+
* const overlapping = resolver.withinBbox({ minLon: -119, minLat: 33, maxLon: -118, maxLat: 35 });
|
|
106
222
|
* ```
|
|
107
223
|
*/
|
|
108
224
|
export declare function createAirspaceResolver(options: AirspaceResolverOptions): AirspaceResolver;
|
package/dist/resolver.d.ts.map
CHANGED
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAW,MAAM,SAAS,CAAC;AAE1D,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AACxE,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;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,4CAA4C;IAC5C,GAAG,EAAE,MAAM,CAAC;IACZ,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,WAAW,CAAC,YAAY,CAAC,CAAC;IAClC;;;;;;;;OAQG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;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;IAEvE;;;;;;;;;;;;;;;;;OAiBG;IACH,UAAU,CAAC,KAAK,EAAE,qBAAqB,GAAG,eAAe,EAAE,CAAC;IAE5D;;;;;;;;;;;;;;;;;;OAkBG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,2BAA2B,GAAG,eAAe,EAAE,CAAC;IAE3F;;;;;;;;;;;;;;;OAeG;IACH,UAAU,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,EAAE,CAAC;IAEjD;;;;;;;;;;;;OAYG;IACH,cAAc,CACZ,QAAQ,EAAE,CACR,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,EACzB,WAAW,EAAE,WAAW,KACrB,IAAI,GACR,IAAI,CAAC;CACT;AAoDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,gBAAgB,CA2HzF"}
|
package/dist/resolver.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { polygon } from '@squawk/geo';
|
|
1
|
+
import { polygon, polygonGeoJson } from '@squawk/geo';
|
|
2
2
|
import { altitudeMatches } from './vertical-filter.js';
|
|
3
3
|
/**
|
|
4
4
|
* Parses a GeoJSON Feature into an IndexedFeature, extracting the
|
|
@@ -57,6 +57,9 @@ function parseFeature(geoFeature) {
|
|
|
57
57
|
* const overhead = resolver.query({ lat: 33.9425, lon: -118.4081, altitudeFt: 3000 });
|
|
58
58
|
* const laxShells = resolver.byAirport('LAX');
|
|
59
59
|
* const newYorkArtcc = resolver.byArtcc('ZNY');
|
|
60
|
+
* const anyZnyFeature = resolver.byIdentifier('ZNY');
|
|
61
|
+
* const nearbyByCentroid = resolver.byCentroid({ lon: -118.4, lat: 33.9 });
|
|
62
|
+
* const overlapping = resolver.withinBbox({ minLon: -119, minLat: 33, maxLon: -118, maxLat: 35 });
|
|
60
63
|
* ```
|
|
61
64
|
*/
|
|
62
65
|
export function createAirspaceResolver(options) {
|
|
@@ -120,5 +123,49 @@ export function createAirspaceResolver(options) {
|
|
|
120
123
|
}
|
|
121
124
|
return artccFeatures.filter((f) => f.artccStratum === stratum);
|
|
122
125
|
},
|
|
126
|
+
byCentroid(query) {
|
|
127
|
+
const tolerance = query.toleranceDeg ?? 0.0001;
|
|
128
|
+
const results = [];
|
|
129
|
+
for (const { feature } of indexed) {
|
|
130
|
+
const centroid = polygonGeoJson.polygonCentroid(feature.boundary);
|
|
131
|
+
if (centroid === undefined) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (Math.abs(centroid[0] - query.lon) < tolerance &&
|
|
135
|
+
Math.abs(centroid[1] - query.lat) < tolerance) {
|
|
136
|
+
results.push(feature);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return results;
|
|
140
|
+
},
|
|
141
|
+
byIdentifier(identifier, options) {
|
|
142
|
+
const bucket = byIdentifierMap.get(identifier.toUpperCase());
|
|
143
|
+
if (bucket === undefined) {
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
146
|
+
const types = options?.types;
|
|
147
|
+
if (types !== undefined) {
|
|
148
|
+
return bucket.filter((f) => types.has(f.type));
|
|
149
|
+
}
|
|
150
|
+
const includeArtcc = options?.includeArtcc ?? true;
|
|
151
|
+
if (includeArtcc) {
|
|
152
|
+
return bucket.slice();
|
|
153
|
+
}
|
|
154
|
+
return bucket.filter((f) => f.type !== 'ARTCC');
|
|
155
|
+
},
|
|
156
|
+
withinBbox(bbox) {
|
|
157
|
+
const results = [];
|
|
158
|
+
for (const { feature, boundingBox } of indexed) {
|
|
159
|
+
if (polygonGeoJson.boundingBoxesOverlap(bbox, boundingBox)) {
|
|
160
|
+
results.push(feature);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return results;
|
|
164
|
+
},
|
|
165
|
+
forEachIndexed(callback) {
|
|
166
|
+
for (const { feature, ring, boundingBox } of indexed) {
|
|
167
|
+
callback(feature, ring, boundingBox);
|
|
168
|
+
}
|
|
169
|
+
},
|
|
123
170
|
};
|
|
124
171
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squawk/airspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pure logic library for querying US airspace geometry by position and altitude",
|
|
6
6
|
"author": "Neil Cochran",
|
|
@@ -36,7 +36,9 @@
|
|
|
36
36
|
"test": "vitest run",
|
|
37
37
|
"test:coverage": "vitest run --coverage",
|
|
38
38
|
"lint": "tsc --noEmit && eslint src",
|
|
39
|
-
"lint:pack": "publint && attw --pack . --profile esm-only"
|
|
39
|
+
"lint:pack": "publint && attw --pack . --profile esm-only",
|
|
40
|
+
"api:check": "api-extractor run --verbose",
|
|
41
|
+
"api:report": "api-extractor run --local --verbose"
|
|
40
42
|
},
|
|
41
43
|
"dependencies": {
|
|
42
44
|
"@squawk/geo": "^0.4.3",
|
|
@@ -45,7 +47,7 @@
|
|
|
45
47
|
},
|
|
46
48
|
"devDependencies": {
|
|
47
49
|
"@squawk/airspace-data": "^0.5.2",
|
|
48
|
-
"@types/node": "^25.
|
|
50
|
+
"@types/node": "^25.9.3"
|
|
49
51
|
},
|
|
50
52
|
"keywords": [
|
|
51
53
|
"aviation",
|