@squawk/airspace 0.2.3 → 0.3.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 +36 -13
- package/dist/resolver.d.ts +35 -10
- package/dist/resolver.d.ts.map +1 -1
- package/dist/resolver.js +47 -23
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -19,14 +19,17 @@ Part of the [@squawk](https://www.npmjs.com/org/squawk) aviation library suite.
|
|
|
19
19
|
import { usBundledAirspace } from '@squawk/airspace-data';
|
|
20
20
|
import { createAirspaceResolver } from '@squawk/airspace';
|
|
21
21
|
|
|
22
|
-
const
|
|
22
|
+
const resolver = createAirspaceResolver({ data: usBundledAirspace });
|
|
23
23
|
|
|
24
24
|
// Query a position and altitude
|
|
25
|
-
const
|
|
25
|
+
const overhead = resolver.query({ lat: 33.9425, lon: -118.4081, altitudeFt: 3000 });
|
|
26
26
|
|
|
27
|
-
for (const f of
|
|
27
|
+
for (const f of overhead) {
|
|
28
28
|
console.log(f.type, f.name, f.identifier);
|
|
29
29
|
}
|
|
30
|
+
|
|
31
|
+
// Get every shell associated with an airport (for drawing the full wedding cake)
|
|
32
|
+
const laxShells = resolver.byAirport('LAX');
|
|
30
33
|
```
|
|
31
34
|
|
|
32
35
|
Consumers who have their own GeoJSON airspace data can use this package standalone:
|
|
@@ -34,19 +37,23 @@ Consumers who have their own GeoJSON airspace data can use this package standalo
|
|
|
34
37
|
```typescript
|
|
35
38
|
import { createAirspaceResolver } from '@squawk/airspace';
|
|
36
39
|
|
|
37
|
-
const
|
|
40
|
+
const resolver = createAirspaceResolver({ data: myGeoJson });
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
## How it works
|
|
41
44
|
|
|
42
45
|
`createAirspaceResolver` parses the GeoJSON FeatureCollection at initialization and
|
|
43
|
-
returns a resolver
|
|
44
|
-
feature:
|
|
46
|
+
returns a resolver object with two methods:
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
- `query(AirspaceQuery)` - returns features containing the given position and
|
|
49
|
+
altitude, via a ray casting point-in-polygon test combined with a vertical
|
|
50
|
+
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.
|
|
48
54
|
|
|
49
|
-
All matching features are returned as `AirspaceFeature` objects (from `@squawk/types`)
|
|
55
|
+
All matching features are returned as `AirspaceFeature` objects (from `@squawk/types`),
|
|
56
|
+
including the full polygon boundary coordinates.
|
|
50
57
|
|
|
51
58
|
## AGL altitude handling
|
|
52
59
|
|
|
@@ -74,14 +81,14 @@ for (const f of features) {
|
|
|
74
81
|
|
|
75
82
|
### `createAirspaceResolver(options)`
|
|
76
83
|
|
|
77
|
-
Creates a resolver
|
|
84
|
+
Creates a resolver from a GeoJSON dataset.
|
|
78
85
|
|
|
79
86
|
**Parameters:**
|
|
80
87
|
|
|
81
88
|
- `options.data` - a GeoJSON `FeatureCollection` with airspace features
|
|
82
89
|
|
|
83
|
-
**Returns:** `AirspaceResolver` -
|
|
84
|
-
`(
|
|
90
|
+
**Returns:** `AirspaceResolver` - an object exposing `query(AirspaceQuery)` and
|
|
91
|
+
`byAirport(identifier, types?)` methods.
|
|
85
92
|
|
|
86
93
|
### `AirspaceQuery`
|
|
87
94
|
|
|
@@ -94,10 +101,26 @@ Creates a resolver function from a GeoJSON dataset.
|
|
|
94
101
|
|
|
95
102
|
```typescript
|
|
96
103
|
// Only query tower-controlled airspace (exclude Class E and SUA)
|
|
97
|
-
const controlled =
|
|
104
|
+
const controlled = resolver.query({
|
|
98
105
|
lat: 33.9425,
|
|
99
106
|
lon: -118.4081,
|
|
100
107
|
altitudeFt: 3000,
|
|
101
108
|
types: new Set(['CLASS_B', 'CLASS_C', 'CLASS_D']),
|
|
102
109
|
});
|
|
103
110
|
```
|
|
111
|
+
|
|
112
|
+
### `resolver.byAirport(identifier, types?)`
|
|
113
|
+
|
|
114
|
+
Returns every airspace feature whose `identifier` property matches. For
|
|
115
|
+
Class B/C/D/E2 this is the associated airport's FAA location identifier
|
|
116
|
+
(e.g. "JFK" for the NY Class B). For Special Use Airspace this is the NASR
|
|
117
|
+
designator (e.g. "R-2508"). Lookup is case-insensitive. ICAO-prefixed codes
|
|
118
|
+
like "KJFK" will not match - resolve to an FAA ID first via `@squawk/airports`.
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// Every sector of the NY Class B around JFK, with full polygon boundaries
|
|
122
|
+
const jfkShells = resolver.byAirport('JFK');
|
|
123
|
+
|
|
124
|
+
// Only the Class D for a towered field
|
|
125
|
+
const safClassD = resolver.byAirport('SAF', new Set(['CLASS_D']));
|
|
126
|
+
```
|
package/dist/resolver.d.ts
CHANGED
|
@@ -29,17 +29,41 @@ export interface AirspaceResolverOptions {
|
|
|
29
29
|
data: FeatureCollection;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
33
|
-
* airspace features that contain that point laterally and vertically.
|
|
32
|
+
* Stateless resolver exposing airspace query methods.
|
|
34
33
|
*/
|
|
35
|
-
export
|
|
34
|
+
export interface AirspaceResolver {
|
|
35
|
+
/**
|
|
36
|
+
* Returns every airspace feature whose lateral polygon contains the given
|
|
37
|
+
* position and whose vertical bounds contain the given altitude.
|
|
38
|
+
*
|
|
39
|
+
* @param query - Position, altitude, and optional type filter.
|
|
40
|
+
* @returns All matching features, in no particular order.
|
|
41
|
+
*/
|
|
42
|
+
query(query: AirspaceQuery): AirspaceFeature[];
|
|
43
|
+
/**
|
|
44
|
+
* Returns every airspace feature associated with the given identifier,
|
|
45
|
+
* independent of position or altitude. Lookup is case-insensitive.
|
|
46
|
+
*
|
|
47
|
+
* For Class B/C/D/E2 airspace, the feature `identifier` is the associated
|
|
48
|
+
* airport's FAA location identifier (e.g. "JFK" for the NY Class B). For
|
|
49
|
+
* Special Use Airspace, it is the NASR designator (e.g. "R-2508"). Pass
|
|
50
|
+
* only the bare identifier - ICAO-prefixed codes like "KJFK" will not
|
|
51
|
+
* match; resolve to an FAA ID first via `@squawk/airports` if needed.
|
|
52
|
+
*
|
|
53
|
+
* @param identifier - FAA identifier or NASR designator.
|
|
54
|
+
* @param types - Optional type filter. Only features whose type is in this
|
|
55
|
+
* set are returned. When omitted, all types are returned.
|
|
56
|
+
* @returns All features whose identifier matches, or an empty array.
|
|
57
|
+
*/
|
|
58
|
+
byAirport(identifier: string, types?: ReadonlySet<AirspaceType>): AirspaceFeature[];
|
|
59
|
+
}
|
|
36
60
|
/**
|
|
37
|
-
* Creates a stateless airspace resolver
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
61
|
+
* Creates a stateless airspace resolver. The resolver accepts a GeoJSON
|
|
62
|
+
* FeatureCollection at initialization (typically from `@squawk/airspace-data`)
|
|
63
|
+
* and returns an object with methods for querying by position and altitude
|
|
64
|
+
* or by associated airport / SUA identifier.
|
|
41
65
|
*
|
|
42
|
-
*
|
|
66
|
+
* Position queries perform two checks per feature:
|
|
43
67
|
* 1. **Lateral** - point-in-polygon test against the feature boundary
|
|
44
68
|
* 2. **Vertical** - altitude comparison against floor/ceiling bounds
|
|
45
69
|
*
|
|
@@ -54,8 +78,9 @@ export type AirspaceResolver = (query: AirspaceQuery) => AirspaceFeature[];
|
|
|
54
78
|
* import { usBundledAirspace } from '@squawk/airspace-data';
|
|
55
79
|
* import { createAirspaceResolver } from '@squawk/airspace';
|
|
56
80
|
*
|
|
57
|
-
* const
|
|
58
|
-
* const
|
|
81
|
+
* const resolver = createAirspaceResolver({ data: usBundledAirspace });
|
|
82
|
+
* const overhead = resolver.query({ lat: 33.9425, lon: -118.4081, altitudeFt: 3000 });
|
|
83
|
+
* const laxShells = resolver.byAirport('LAX');
|
|
59
84
|
* ```
|
|
60
85
|
*/
|
|
61
86
|
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;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
|
|
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"}
|
package/dist/resolver.js
CHANGED
|
@@ -32,12 +32,12 @@ function parseFeature(geoFeature) {
|
|
|
32
32
|
return { feature, ring, boundingBox: polygon.boundingBox(ring) };
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* Creates a stateless airspace resolver
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
35
|
+
* Creates a stateless airspace resolver. The resolver accepts a GeoJSON
|
|
36
|
+
* FeatureCollection at initialization (typically from `@squawk/airspace-data`)
|
|
37
|
+
* and returns an object with methods for querying by position and altitude
|
|
38
|
+
* or by associated airport / SUA identifier.
|
|
39
39
|
*
|
|
40
|
-
*
|
|
40
|
+
* Position queries perform two checks per feature:
|
|
41
41
|
* 1. **Lateral** - point-in-polygon test against the feature boundary
|
|
42
42
|
* 2. **Vertical** - altitude comparison against floor/ceiling bounds
|
|
43
43
|
*
|
|
@@ -52,36 +52,60 @@ function parseFeature(geoFeature) {
|
|
|
52
52
|
* import { usBundledAirspace } from '@squawk/airspace-data';
|
|
53
53
|
* import { createAirspaceResolver } from '@squawk/airspace';
|
|
54
54
|
*
|
|
55
|
-
* const
|
|
56
|
-
* const
|
|
55
|
+
* const resolver = createAirspaceResolver({ data: usBundledAirspace });
|
|
56
|
+
* const overhead = resolver.query({ lat: 33.9425, lon: -118.4081, altitudeFt: 3000 });
|
|
57
|
+
* const laxShells = resolver.byAirport('LAX');
|
|
57
58
|
* ```
|
|
58
59
|
*/
|
|
59
60
|
export function createAirspaceResolver(options) {
|
|
60
61
|
const indexed = [];
|
|
62
|
+
const byIdentifierMap = new Map();
|
|
61
63
|
for (const geoFeature of options.data.features) {
|
|
62
64
|
const parsed = parseFeature(geoFeature);
|
|
63
65
|
if (parsed) {
|
|
64
66
|
indexed.push(parsed);
|
|
67
|
+
const key = parsed.feature.identifier.toUpperCase();
|
|
68
|
+
if (key.length > 0) {
|
|
69
|
+
const bucket = byIdentifierMap.get(key);
|
|
70
|
+
if (bucket === undefined) {
|
|
71
|
+
byIdentifierMap.set(key, [parsed.feature]);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
bucket.push(parsed.feature);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
65
77
|
}
|
|
66
78
|
}
|
|
67
|
-
return
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
79
|
+
return {
|
|
80
|
+
query(query) {
|
|
81
|
+
const results = [];
|
|
82
|
+
const { lon, lat, altitudeFt, types } = query;
|
|
83
|
+
for (const { feature, ring, boundingBox } of indexed) {
|
|
84
|
+
if (types && !types.has(feature.type)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (!polygon.pointInBoundingBox(lon, lat, boundingBox)) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (!polygon.pointInPolygon(lon, lat, ring)) {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (!altitudeMatches(altitudeFt, feature.floor, feature.ceiling)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
results.push(feature);
|
|
76
97
|
}
|
|
77
|
-
|
|
78
|
-
|
|
98
|
+
return results;
|
|
99
|
+
},
|
|
100
|
+
byAirport(identifier, types) {
|
|
101
|
+
const bucket = byIdentifierMap.get(identifier.toUpperCase());
|
|
102
|
+
if (bucket === undefined) {
|
|
103
|
+
return [];
|
|
79
104
|
}
|
|
80
|
-
if (
|
|
81
|
-
|
|
105
|
+
if (types === undefined) {
|
|
106
|
+
return bucket.slice();
|
|
82
107
|
}
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
return results;
|
|
108
|
+
return bucket.filter((f) => types.has(f.type));
|
|
109
|
+
},
|
|
86
110
|
};
|
|
87
111
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@squawk/airspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Pure logic library for querying US airspace geometry by position and altitude",
|
|
6
6
|
"author": "Neil Cochran",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"types": "./dist/index.d.ts",
|
|
20
20
|
"exports": {
|
|
21
21
|
".": {
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"import": "./dist/index.js"
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"files": [
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "tsc",
|
|
32
32
|
"test": "node --test 'dist/**/*.spec.js'",
|
|
33
|
-
"lint": "tsc --noEmit && eslint src"
|
|
33
|
+
"lint": "tsc --noEmit && eslint src",
|
|
34
|
+
"lint:pack": "publint && attw --pack . --profile esm-only"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
36
37
|
"@squawk/geo": "*",
|