feat(weather): add globe wind, observed weather and cyclone context (#617)

Wind (NOAA GFS and ECMWF IFS: animated particles, colour field, a reading at map centre), observed weather (NOAA MRMS radar, GOES and NESDIS infrared clouds, lightning density) on one shared history timeline, NHC and CPHC cyclone advisories with tracks and cones, and a WEATHER panel in the right rail. On 3D Tiles the observed products render as raised shells with a sharper window around the view.

Co-authored-by: Gustavo Beneduzi <[email protected]>
Co-authored-by: Daniel Slay <[email protected]>
Co-authored-by: Rehaan Delmotra <[email protected]>
Co-authored-by: Cursor Agent <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-09-22 17:45:39 -05:00
1 parent f01b6a5d84
commit 0251c74109
180 files changed
+30645 -434

No files matched your search

+18
View File
@@ -771,6 +771,9 @@ const schemas = [
'ais-live-vessels',
'local-firms',
'earthquakes',
'satellites',
'local-datacenters',
'local-dams',
],
},
},
@@ -860,6 +863,21 @@ const schemas = [
},
},
},
{
name: 'next_satellite_pass',
parameters: {
type: 'object',
additionalProperties: false,
required: ['target'],
properties: {
target: { type: 'string', minLength: 1, maxLength: 120 },
latitude: { type: 'number', minimum: -90, maximum: 90 },
longitude: { type: 'number', minimum: -180, maximum: 180 },
minElevationDeg: { type: 'number', minimum: 5, maximum: 60 },
visibleOnly: { type: 'boolean' },
},
},
},
];
function freeze(value) {
+18 -2
View File
@@ -15,13 +15,13 @@ const stable = (value) =>
)
: value;
test('the complete Realtime tool payload retains its pre-extraction contract and wording', () => {
test('the complete Realtime tool payload pins the additive analyst and satellite release', () => {
const digest = createHash('sha256')
.update(JSON.stringify(stable(GEV_REALTIME_TOOLS)))
.digest('hex');
assert.equal(
digest,
'956381c3456d3644ed7c9cda72910dc68a34d9191e0b3e414ee200c348245214',
'9e33ac0fa5a860a17bb6d79f2c43646b6c36d0c932590bf114814e891a1c96de',
);
});
@@ -76,3 +76,19 @@ test('metadata cannot add tools, fields, types or enum values', () => {
])
assert.throws(() => createActionTools(descriptions), TypeError);
});
test('all legacy action arguments are byte-identical after removing the deliberate additions', () => {
const legacy = structuredClone(GEV_ACTION_SCHEMAS).filter(
(tool) => tool.name !== 'next_satellite_pass',
);
const layers = legacy.find((tool) => tool.name === 'analyst_query').parameters
.properties.layers.items;
layers.enum = layers.enum.filter(
(key) => !['satellites', 'local-datacenters', 'local-dams'].includes(key),
);
// Independently derived by executing trusted c9f9896 actionSchemas in the restricted container.
assert.equal(
createHash('sha256').update(JSON.stringify(legacy)).digest('hex'),
'820fff21658f6907e1010b2b79c5431a77f4e34afd2277d62d8de46c368b6f8c',
);
});
+116 -1
View File
@@ -1,3 +1,8 @@
import {
layerSnapshot,
layerSnapshots,
feedProvenanceEnvelope,
} from '../data/layerSnapshot.js';
import { readLayerLifecycleSummary } from './layerSummary.js';
export { readLayerLifecycleSummary } from './layerSummary.js';
import { defaultGeospatial } from '../search/defaults.js';
@@ -964,6 +969,10 @@ export function createGevActionRunner({
};
}
if (name === 'next_satellite_pass') {
return nextSatellitePass(viewer, dataManager, args);
}
if (name === 'next_iss_pass') {
return nextIssPass(viewer, dataManager, args);
}
@@ -2655,6 +2664,64 @@ function nextIssPass(viewer, dataManager, args) {
durationMin: Math.max(1, Math.round((pass.setMs - pass.riseMs) / 60000)),
peakElevationDeg: Math.round(pass.maxElevDeg),
riseDirection: compassDir(pass.riseAzDeg),
visible: typeof pass.visible === 'boolean' ? pass.visible : null,
visibilityNote:
'Geometric illumination estimate only; weather, brightness and orbital-element age affect actual visibility.',
setIso: new Date(pass.setMs).toISOString(),
peakIso: new Date(pass.maxElevMs).toISOString(),
};
}
function nextSatellitePass(viewer, dataManager, args) {
const layer = dataManager?.layers?.get('satellites')?.module;
const identity = layer?.resolveSatelliteForPass?.(args.target) || {
status: 'not-found',
};
if (identity.status !== 'ok')
return {
ok: false,
action: 'next_satellite_pass',
...identity,
error:
identity.status === 'ambiguous'
? 'Several loaded satellites match. Choose a NORAD ID from candidates.'
: 'No loaded satellite matches. Enable satellites and use an exact name or NORAD ID.',
};
// Reuse the legacy location fallback and result formatting, substituting only
// this explicitly resolved catalog identity and the optional visibility filter.
const adapter = {
layers: new Map([
[
'satellites',
{
module: {
getNextIssPass: (options) =>
layer.getNextSatellitePass(identity.noradId, {
...options,
requireVisible: args.visibleOnly === true,
}),
},
},
],
]),
};
const result = nextIssPass(viewer, adapter, args);
if (result.error) {
result.error = result.error.replace(
/ISS/g,
identity.name || String(identity.noradId),
);
if (args.visibleOnly === true)
result.error +=
' Search required estimated illumination under a dark sky.';
}
return {
...result,
action: 'next_satellite_pass',
noradId: identity.noradId,
name: identity.name,
visibleOnly: args.visibleOnly === true,
horizonHours: 24,
};
}
@@ -3025,7 +3092,13 @@ function getCurrentViewState(
enabled: layer.enabled,
count: layer.stats?.count || 0,
error: layer.stats?.error || null,
feedState: layerSnapshot(layer).feedState,
source: layerSnapshot(layer).source,
lastUpdate: layerSnapshot(layer).lastUpdate,
})),
feedProvenance: feedProvenanceEnvelope(
layerSnapshots(dataManager.getAll()).filter((layer) => layer.enabled),
),
};
}
@@ -4152,6 +4225,29 @@ function analystProviders(
? mod.getAnalystRecords(requestedLimit) || []
: mod.getAnalystRecords() || [];
},
getLayerSnapshot(layerKey) {
const row = dataManager.getAll?.().find((layer) => layer.id === layerKey);
if (row) return layerSnapshot(row);
const module = dataManager.layers?.get(layerKey)?.module;
return layerSnapshot({
id: layerKey,
enabled: dataManager.isEnabled?.(layerKey),
stats: module?.getStats?.() || {},
});
},
getRecordCoverage(layerKey, rows) {
if (!['satellites', 'local-datacenters', 'local-dams'].includes(layerKey))
return null;
const module = dataManager.layers.get(layerKey)?.module;
const loaded = module?.getStats?.().count;
return {
basis: 'bounded-loaded-records',
recordsExamined: rows.length,
loadedCount: Number.isFinite(loaded) ? loaded : null,
sourceTruncated: Number.isFinite(loaded) ? loaded > rows.length : null,
note: 'Counts and ranks apply only to these examined loaded records, not all satellites or infrastructure; distance is ground great-circle distance.',
};
},
resolveRegionRing,
/**
* The active Contacts subject, when there is one — the centre the operator
@@ -4243,6 +4339,12 @@ async function runAnalystQuery(
'distanceKm',
'confidence',
'place',
'noradId',
'satelliteClass',
'group',
'river',
'output',
'capacity',
]) {
if (r[k] !== null && r[k] !== undefined) compact[k] = r[k];
}
@@ -4282,7 +4384,18 @@ async function runAnalystQuery(
args,
result,
);
if (entityWindow) return entityWindow;
if (entityWindow) {
const provenance = feedProvenanceEnvelope(
layerSnapshots(dataManager.getAll?.() || []).filter(
(layer) => layer.enabled && ['flights', 'military'].includes(layer.id),
),
);
return {
...entityWindow,
feedProvenance: provenance,
feedState: provenance.overall,
};
}
const contactsWindow = activeContactsWindow(dataManager);
const aircraftQueried = (result.coverage?.layersQueried || []).some(
@@ -4321,6 +4434,8 @@ async function runAnalystQuery(
items,
summary: result.summary,
coverage: result.coverage,
feedProvenance: result.coverage?.feedProvenance || null,
feedState: result.coverage?.feedProvenance?.overall || null,
// The panel's own numbers, carried so the answer can match what the
// operator is looking at regardless of how the model reads the note.
// Flattened alongside the object so the count and its subject cannot be
+88
View File
@@ -3182,3 +3182,91 @@ test('ISS voice lookup uses the registered satellite instance', async () => {
assert.deepEqual(calls, [{ latDeg: 30, lonDeg: -97, minElevDeg: 15 }]);
assert.match(result.error, /No ISS pass above 15/);
});
test('analyst_query and get_current_view_state carry stale feed provenance', async () => {
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
const now = Date.now();
const flights = {
id: 'flights',
source: 'OpenSky Network',
getStats: () => ({
source: 'OpenSky Network',
stale: true,
count: 12,
lastUpdate: now - 240_000,
}),
getAnalystRecords: () => ([
{ id: 'SWA1', icao24: 'aaa001', lat: 30.27, lon: -97.74, altitudeM: 11000, onGround: false },
]),
};
const viewer = {
clock: { onTick: { addEventListener: () => () => {} } },
scene: { canvas: { addEventListener() {}, removeEventListener() {} } },
camera: {
moveEnd: { addEventListener() {} },
positionWC: Cesium.Cartesian3.fromDegrees(-97.7, 30.2, 1000),
positionCartographic: { height: 300_000, latitude: 0.52, longitude: -1.71 },
},
};
const dataManager = {
layers: new Map([['flights', { module: flights }]]),
isEnabled: (id) => id === 'flights',
getAll: () => [{
id: 'flights',
name: 'Live Flights',
enabled: true,
source: 'OpenSky Network',
stats: flights.getStats(),
}],
};
const runner = createGevActionRunner({
viewer,
styleManager: {
activeStyle: 'normal',
getContextModeState: () => ({ mode: null, active: false }),
getCockpitState: () => ({ active: false }),
getControlState: () => null,
},
dataManager,
});
const view = await runner('get_current_view_state');
assert.equal(view.layers[0].feedState, 'stale');
assert.equal(view.feedProvenance.overall, 'stale');
assert.match(view.feedProvenance.note, /STALE/);
const query = await runner('analyst_query', {
layers: ['flights'],
scope: { kind: 'view' },
limit: 5,
});
assert.equal(query.ok, true);
assert.equal(query.feedState, 'stale');
assert.equal(query.feedProvenance.overall, 'stale');
assert.equal(query.coverage.layersQueried[0].feedState, 'stale');
assert.match(query.feedProvenance.note, /do not describe this as live/i);
});
test('general satellite pass resolves once, refuses ambiguity, and preserves ISS call semantics', async () => {
const calls = [];
const pass = { riseMs: Date.now() + 60000, setMs: Date.now() + 360000, maxElevMs: Date.now() + 180000, maxElevDeg: 30, riseAzDeg: 90, visible: false };
const layer = {
resolveSatelliteForPass(target) { return target === 'starlink' ? { status: 'ambiguous', candidates: [{ noradId: 1 }, { noradId: 2 }] } : { status: 'ok', noradId: 25544, name: 'ISS' }; },
getNextSatellitePass(id, options) { calls.push({ id, ...options }); return { status: 'ok', pass }; },
getNextIssPass(options) { calls.push(options); return { status: 'ok', pass }; },
};
const viewer = { clock: { onTick: { addEventListener: () => () => {} } }, scene: { canvas: { addEventListener() {}, removeEventListener() {} } }, camera: { moveEnd: { addEventListener() {} } } };
const runner = createGevActionRunner({ viewer, styleManager: {}, dataManager: { layers: new Map([['satellites', { module: layer }]]) } });
const ambiguous = await runner('next_satellite_pass', { target: 'starlink' });
assert.equal(ambiguous.status, 'ambiguous');
assert.equal(calls.length, 0);
const args = { latitude: 30, longitude: -97, minElevationDeg: 15 };
const general = await runner('next_satellite_pass', { target: '25544', visibleOnly: true, ...args });
assert.equal(general.action, 'next_satellite_pass');
assert.deepEqual(calls[0], { id: 25544, latDeg: 30, lonDeg: -97, minElevDeg: 15, requireVisible: true });
const iss = await runner('next_iss_pass', args);
assert.deepEqual(calls[1], { latDeg: 30, lonDeg: -97, minElevDeg: 15 });
for (const key of ['observer', 'riseIso', 'minutesFromNow', 'durationMin', 'peakElevationDeg', 'riseDirection']) assert.deepEqual(iss[key], general[key]);
assert.equal(iss.visible, false);
assert.equal(iss.action, 'next_iss_pass');
});