mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 03:14:00 +00:00
feat(layers): add FAA drone restrictions airspace layer
Show US prohibited and restricted special-use airspace from the keyless FAA SUA FeatureServer as an off-by-default Infrastructure layer, with share-link token 3 and an informational-only disclaimer.
This commit is contained in:
1 parent
8b8ccc9a81
commit
ffb332472d
35 files changed
+1573
-74
No files matched your search
@@ -327,6 +327,30 @@ const layers = [
|
||||
context: true,
|
||||
query: { fields: { name: ['t'], class: ['t'], kind: ['t'] } },
|
||||
},
|
||||
{
|
||||
id: 'drone-airspace',
|
||||
aliases: [
|
||||
'drone restrictions',
|
||||
'drone airspace',
|
||||
'no-fly zones',
|
||||
'no fly zones',
|
||||
'drones',
|
||||
],
|
||||
context: false,
|
||||
query: {
|
||||
fields: {
|
||||
name: ['t'],
|
||||
typeCode: ['t'],
|
||||
type: ['t'],
|
||||
lowerDesc: ['t'],
|
||||
upperDesc: ['t'],
|
||||
timesOfUse: ['t'],
|
||||
agency: ['t'],
|
||||
city: ['t'],
|
||||
state: ['t'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'local-datacenters',
|
||||
aliases: ['datacenters', 'data centers', 'data centres'],
|
||||
|
||||
@@ -20,14 +20,28 @@ test('every catalog layer is in the voice manifest or explicitly voice-off', ()
|
||||
const missing = catalogIds.filter(
|
||||
(id) => !manifestIds.includes(id) && !Object.hasOwn(VOICE_OFF_LAYERS, id),
|
||||
);
|
||||
assert.deepEqual(missing, [], `add a voice manifest entry for: ${missing.join(', ')}`);
|
||||
assert.deepEqual(
|
||||
missing,
|
||||
[],
|
||||
`add a voice manifest entry for: ${missing.join(', ')}`,
|
||||
);
|
||||
for (const id of Object.keys(VOICE_OFF_LAYERS)) {
|
||||
assert.ok(catalogIds.includes(id), `${id} is voice-off but not in the catalog`);
|
||||
assert.ok(!manifestIds.includes(id), `${id} is both voice-off and in the manifest`);
|
||||
assert.ok(
|
||||
catalogIds.includes(id),
|
||||
`${id} is voice-off but not in the catalog`,
|
||||
);
|
||||
assert.ok(
|
||||
!manifestIds.includes(id),
|
||||
`${id} is both voice-off and in the manifest`,
|
||||
);
|
||||
}
|
||||
const unknown = manifestIds.filter((id) => !catalogIds.includes(id));
|
||||
assert.deepEqual(unknown, [], 'the manifest names only catalog layers');
|
||||
assert.equal(new Set(manifestIds).size, manifestIds.length, 'one entry per layer');
|
||||
assert.equal(
|
||||
new Set(manifestIds).size,
|
||||
manifestIds.length,
|
||||
'one entry per layer',
|
||||
);
|
||||
});
|
||||
|
||||
test('each layer declares a query or the reason it has none', () => {
|
||||
@@ -41,7 +55,8 @@ test('each layer declares a query or the reason it has none', () => {
|
||||
});
|
||||
|
||||
test('tool enums are generated from the manifest', () => {
|
||||
const toggle = tool('set_layer_visibility').parameters.properties.layerId.enum;
|
||||
const toggle = tool('set_layer_visibility').parameters.properties.layerId
|
||||
.enum;
|
||||
assert.deepEqual(toggle, [...VOICE_TOGGLE_LAYER_IDS]);
|
||||
for (const id of [
|
||||
'transit',
|
||||
@@ -57,9 +72,13 @@ test('tool enums are generated from the manifest', () => {
|
||||
'rocket-launches',
|
||||
'bikeshare',
|
||||
'local-adsb',
|
||||
'drone-airspace',
|
||||
])
|
||||
assert.ok(toggle.includes(id), `${id} is voice-toggleable`);
|
||||
assert.ok(!toggle.includes('military-awareness'), 'Global Context is reached through set_context_mode');
|
||||
assert.ok(
|
||||
!toggle.includes('military-awareness'),
|
||||
'Global Context is reached through set_context_mode',
|
||||
);
|
||||
assert.deepEqual(
|
||||
tool('show_data_layers_menu').parameters.properties.layerId.enum,
|
||||
[...VOICE_TOGGLE_LAYER_IDS],
|
||||
@@ -68,47 +87,98 @@ test('tool enums are generated from the manifest', () => {
|
||||
tool('get_entity_context').parameters.properties.layerId.enum,
|
||||
[...VOICE_CONTEXT_LAYER_IDS],
|
||||
);
|
||||
assert.ok(!VOICE_CONTEXT_LAYER_IDS.includes('telegeography-submarine-cables'), 'cables register no context records');
|
||||
assert.ok(
|
||||
!VOICE_CONTEXT_LAYER_IDS.includes('telegeography-submarine-cables'),
|
||||
'cables register no context records',
|
||||
);
|
||||
assert.deepEqual(
|
||||
tool('analyst_query').parameters.properties.layers.items.enum,
|
||||
[...VOICE_QUERY_LAYER_IDS],
|
||||
);
|
||||
assert.deepEqual(Object.keys(ANALYST_LAYERS), [...VOICE_QUERY_LAYER_IDS]);
|
||||
const panels = tool('set_panel_open').parameters.properties.panelId.enum;
|
||||
assert.ok(panels.includes('weather-panel') && panels.includes('recent-imagery-panel'));
|
||||
assert.ok(
|
||||
panels.includes('weather-panel') && panels.includes('recent-imagery-panel'),
|
||||
);
|
||||
});
|
||||
|
||||
test('spoken aliases resolve to real layers, keep every legacy phrase and never collide', () => {
|
||||
const aliases = voiceLayerAliasMap();
|
||||
for (const id of aliases.values()) assert.ok(manifestIds.includes(id), id);
|
||||
const legacy = {
|
||||
planes: 'flights', aircraft: 'flights', 'military flights': 'military', quakes: 'earthquakes',
|
||||
'space mission': 'rocket-launches', 'space missions': 'rocket-launches', missions: 'rocket-launches',
|
||||
'street traffic': 'traffic', cameras: 'cctv', 'internet radio': 'radio', 'radio stations': 'radio',
|
||||
bikes: 'bikeshare', ais: 'ais-live-vessels', ships: 'ais-live-vessels', vessels: 'ais-live-vessels',
|
||||
'live vessels': 'ais-live-vessels', datacenters: 'local-datacenters', 'data centers': 'local-datacenters',
|
||||
'data centres': 'local-datacenters', dams: 'local-dams', 'submarine cables': 'telegeography-submarine-cables',
|
||||
cables: 'telegeography-submarine-cables', telegeography: 'telegeography-submarine-cables',
|
||||
firms: 'local-firms', fires: 'local-firms', 'active fires': 'local-firms', alpr: 'alpr-cameras',
|
||||
'alpr cameras': 'alpr-cameras', 'flock cameras': 'alpr-cameras', 'license plate readers': 'alpr-cameras',
|
||||
'license plate cameras': 'alpr-cameras', 'plate readers': 'alpr-cameras', 'local-adsb': 'local-adsb',
|
||||
'local adsb': 'local-adsb', 'local ads-b': 'local-adsb', 'my receiver': 'local-adsb',
|
||||
'my antenna': 'local-adsb', 'my sdr': 'local-adsb',
|
||||
planes: 'flights',
|
||||
aircraft: 'flights',
|
||||
'military flights': 'military',
|
||||
quakes: 'earthquakes',
|
||||
'space mission': 'rocket-launches',
|
||||
'space missions': 'rocket-launches',
|
||||
missions: 'rocket-launches',
|
||||
'street traffic': 'traffic',
|
||||
cameras: 'cctv',
|
||||
'internet radio': 'radio',
|
||||
'radio stations': 'radio',
|
||||
bikes: 'bikeshare',
|
||||
ais: 'ais-live-vessels',
|
||||
ships: 'ais-live-vessels',
|
||||
vessels: 'ais-live-vessels',
|
||||
'live vessels': 'ais-live-vessels',
|
||||
datacenters: 'local-datacenters',
|
||||
'data centers': 'local-datacenters',
|
||||
'data centres': 'local-datacenters',
|
||||
dams: 'local-dams',
|
||||
'submarine cables': 'telegeography-submarine-cables',
|
||||
cables: 'telegeography-submarine-cables',
|
||||
telegeography: 'telegeography-submarine-cables',
|
||||
firms: 'local-firms',
|
||||
fires: 'local-firms',
|
||||
'active fires': 'local-firms',
|
||||
alpr: 'alpr-cameras',
|
||||
'alpr cameras': 'alpr-cameras',
|
||||
'flock cameras': 'alpr-cameras',
|
||||
'license plate readers': 'alpr-cameras',
|
||||
'license plate cameras': 'alpr-cameras',
|
||||
'plate readers': 'alpr-cameras',
|
||||
'local-adsb': 'local-adsb',
|
||||
'local adsb': 'local-adsb',
|
||||
'local ads-b': 'local-adsb',
|
||||
'my receiver': 'local-adsb',
|
||||
'my antenna': 'local-adsb',
|
||||
'my sdr': 'local-adsb',
|
||||
};
|
||||
for (const [phrase, id] of Object.entries(legacy)) assert.equal(aliases.get(phrase), id, phrase);
|
||||
for (const [phrase, id] of Object.entries(legacy))
|
||||
assert.equal(aliases.get(phrase), id, phrase);
|
||||
const added = {
|
||||
buses: 'transit', trains: 'transit', hurricanes: 'weather-cyclones', cyclones: 'weather-cyclones',
|
||||
'weather radar': 'weather-radar', lightning: 'weather-lightning', 'military bases': 'military-installations',
|
||||
'recent imagery': 'recent-imagery', 'bike share': 'bikeshare', 'rocket launches': 'rocket-launches',
|
||||
'street level': 'street-level', mapillary: 'street-level',
|
||||
wind: 'wind', directions: 'directions', 'fire perimeters': 'fire-perimeters',
|
||||
'wildfire perimeters': 'fire-perimeters', 'burn areas': 'fire-perimeters',
|
||||
buses: 'transit',
|
||||
trains: 'transit',
|
||||
hurricanes: 'weather-cyclones',
|
||||
cyclones: 'weather-cyclones',
|
||||
'weather radar': 'weather-radar',
|
||||
lightning: 'weather-lightning',
|
||||
'military bases': 'military-installations',
|
||||
'recent imagery': 'recent-imagery',
|
||||
'bike share': 'bikeshare',
|
||||
'rocket launches': 'rocket-launches',
|
||||
'street level': 'street-level',
|
||||
mapillary: 'street-level',
|
||||
wind: 'wind',
|
||||
directions: 'directions',
|
||||
'fire perimeters': 'fire-perimeters',
|
||||
'wildfire perimeters': 'fire-perimeters',
|
||||
'burn areas': 'fire-perimeters',
|
||||
'drone restrictions': 'drone-airspace',
|
||||
'drone airspace': 'drone-airspace',
|
||||
'no-fly zones': 'drone-airspace',
|
||||
drones: 'drone-airspace',
|
||||
};
|
||||
for (const [phrase, id] of Object.entries(added)) assert.equal(aliases.get(phrase), id, phrase);
|
||||
for (const [phrase, id] of Object.entries(added))
|
||||
assert.equal(aliases.get(phrase), id, phrase);
|
||||
const claimed = new Map();
|
||||
for (const entry of VOICE_LAYER_MANIFEST)
|
||||
for (const alias of entry.aliases) {
|
||||
assert.ok(!claimed.has(alias), `"${alias}" claimed by ${claimed.get(alias)} and ${entry.id}`);
|
||||
assert.ok(
|
||||
!claimed.has(alias),
|
||||
`"${alias}" claimed by ${claimed.get(alias)} and ${entry.id}`,
|
||||
);
|
||||
claimed.set(alias, entry.id);
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user