mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
- No pointer cursor over coverage lines (no other layer has one); a click test replaces the hover tests. - No legend: one source, one colour. - The on-globe credit lives in index.js; credits.js is gone. - Billboard glyphs are SVG data URLs with the same geometry, so tests need no canvas stub. - The tile cache keeps the 120 most recently used tiles, same 24 h TTL, instead of a byte budget; listTileLayers was test-only. - providers/mapillary/ is flattened into the layer and its policy merged into one policy.js; the source's method list is a literal in the catalog. - One fakeCesiumViewer for the layer tests. - The fixture gate keeps the steps the unit tests do not cover: coverage and credit, a rejected key, line -> cone -> photo, close, a keyless page. Co-Authored-By: Claude Fable 5.1 <[email protected]>
214 lines
7.8 KiB
JavaScript
214 lines
7.8 KiB
JavaScript
import { createWeatherClock } from '../layers/weather/clock.js';
|
|
import { createWeatherLayer } from '../layers/weather/index.js';
|
|
import { createCyclonesLayer } from '../layers/cyclones/index.js';
|
|
import { createWindLayer } from '../layers/wind/index.js';
|
|
import { createLayerCatalog } from './catalog.js';
|
|
import { LAYER_STATE_REGISTRY } from '../data/layerState.js';
|
|
import { createMilitaryRegistry } from '../layers/aircraft/classification.js';
|
|
import { createApplicationFlights } from './layers/flights.js';
|
|
import { createApplicationMilitary } from './layers/militaryFlights.js';
|
|
import { createApplicationVessels } from './layers/aisLiveVessels.js';
|
|
import { createApplicationCctv } from './layers/cctv.js';
|
|
import { createApplicationRadio } from './layers/radio.js';
|
|
import { createApplicationTraffic } from './layers/traffic.js';
|
|
import { createApplicationBikeshare } from './layers/bikeshare.js';
|
|
import { createApplicationDirections } from './layers/directions.js';
|
|
import { createApplicationRecentImagery } from './layers/recentImagery.js';
|
|
import { createApplicationTransit } from './layers/transit.js';
|
|
import { createApplicationInstallations } from './layers/militaryInstallations.js';
|
|
import { createApplicationSatellites } from './layers/satellites.js';
|
|
import { createApplicationLaunches } from './layers/rocketLaunches.js';
|
|
import { createApplicationAlpr } from './layers/alprCameras.js';
|
|
import { createApplicationLocalAdsb } from './layers/localAdsb.js';
|
|
import { createApplicationAwareness } from './layers/militaryAwareness.js';
|
|
import { createApplicationFirms } from './layers/firms.js';
|
|
import { createApplicationEarthquakes } from './layers/earthquakes.js';
|
|
import { createApplicationFirePerimeters } from './layers/perimeters.js';
|
|
import { createApplicationCables } from './layers/submarineCables.js';
|
|
import { createApplicationStreetLevel } from './layers/streetLevel.js';
|
|
import { createInfrastructureLayers } from '../data/infrastructure.js';
|
|
import { localGeoJsonServices } from './localGeojsonServices.js';
|
|
import { createBhoteKoshiEventLayer } from '../data/bhoteKoshiEvent.js';
|
|
import { createBhoteKoshiLocatorLayer } from '../data/bhoteKoshiLocator.js';
|
|
|
|
const SOURCE_METHODS = Object.freeze({
|
|
flights: ['getSnapshot'],
|
|
military: ['getSnapshot'],
|
|
vessels: ['getSnapshot'],
|
|
cctv: ['getCatalog', 'getHealth', 'getFrameUrl', 'getMediaUrl'],
|
|
radio: ['getDirectory', 'recordClick'],
|
|
traffic: [
|
|
'requestRoads',
|
|
'getStatus',
|
|
'fetchFlowForBounds',
|
|
'getFlowSessionStats',
|
|
'resetFlowTileCache',
|
|
],
|
|
bikeshare: ['getStations'],
|
|
installations: ['getMappedSites', 'searchNearby'],
|
|
satellites: ['readGroup'],
|
|
launches: ['getLaunches', 'getActiveTle'],
|
|
alpr: ['fetch'],
|
|
firms: ['getSnapshot'],
|
|
wind: ['getSnapshot'],
|
|
weather: ['getSnapshot'],
|
|
cyclones: ['getSnapshot'],
|
|
earthquakes: ['getSnapshot'],
|
|
'fire-perimeters': ['getSnapshot'],
|
|
cables: ['fetch'],
|
|
mapillary: ['getStatus', 'getTile', 'getSequenceImages'],
|
|
});
|
|
|
|
/**
|
|
* Hardware-local layers are registered like any other but never enter share
|
|
* links or stored layer state: another browser cannot have this receiver.
|
|
*/
|
|
export const LOCAL_ONLY_LAYER_METADATA = Object.freeze([
|
|
Object.freeze({ id: 'local-adsb', disposition: 'local-only' }),
|
|
]);
|
|
|
|
/** Serialization metadata for every layer the application catalog constructs. */
|
|
export const APPLICATION_LAYER_METADATA = Object.freeze([
|
|
...LAYER_STATE_REGISTRY,
|
|
...LOCAL_ONLY_LAYER_METADATA,
|
|
]);
|
|
|
|
/** Construct the current catalog without choosing any source provider.
|
|
* Scene engines remain page-owned; layers and classification have this app's lifetime.
|
|
* The manager owns layer destruction, while abort releases classification even if startup fails.
|
|
*/
|
|
export function createApplicationCatalog({
|
|
surface,
|
|
sources,
|
|
signal,
|
|
metadata = APPLICATION_LAYER_METADATA,
|
|
vesselOptions,
|
|
resolveAsset,
|
|
nepalBoundaryResolver,
|
|
}) {
|
|
if (!signal?.addEventListener)
|
|
throw new TypeError('An application lifetime signal is required');
|
|
signal.throwIfAborted();
|
|
if (!surface?.groundFloor || !surface?.terrain)
|
|
throw new TypeError('Application surface services are required');
|
|
|
|
for (const [name, methods] of Object.entries(SOURCE_METHODS)) {
|
|
if (
|
|
methods.some((method) => typeof sources?.[name]?.[method] !== 'function')
|
|
)
|
|
throw new TypeError(`Invalid catalog source: ${name}`);
|
|
}
|
|
const militaryRegistry = createMilitaryRegistry();
|
|
const weatherClock = createWeatherClock();
|
|
const dispose = () => {
|
|
signal.removeEventListener('abort', dispose);
|
|
militaryRegistry.dispose();
|
|
weatherClock.destroy();
|
|
};
|
|
signal.addEventListener('abort', dispose, { once: true });
|
|
try {
|
|
militaryRegistry.configureSource(sources.military, { signal });
|
|
const flights = createApplicationFlights({
|
|
surface,
|
|
source: sources.flights,
|
|
militaryRegistry,
|
|
resolveAsset,
|
|
});
|
|
const military = createApplicationMilitary({
|
|
surface,
|
|
source: sources.military,
|
|
militaryRegistry,
|
|
resolveAsset,
|
|
});
|
|
const vessels = createApplicationVessels({
|
|
source: sources.vessels,
|
|
options: vesselOptions,
|
|
});
|
|
const installations = createApplicationInstallations({
|
|
surface,
|
|
source: sources.installations,
|
|
});
|
|
const satellites = createApplicationSatellites({
|
|
source: sources.satellites,
|
|
});
|
|
const catalog = createLayerCatalog(
|
|
[
|
|
createBhoteKoshiEventLayer(),
|
|
createBhoteKoshiLocatorLayer({
|
|
boundaryResolver: nepalBoundaryResolver,
|
|
}),
|
|
flights,
|
|
military,
|
|
createApplicationLocalAdsb({
|
|
surface,
|
|
enrichment: sources.flights,
|
|
displayParams: () => flights.getParams(),
|
|
...(resolveAsset ? { resolveAsset } : {}),
|
|
}),
|
|
createApplicationEarthquakes({ source: sources.earthquakes }),
|
|
createApplicationFirePerimeters({
|
|
source: sources['fire-perimeters'],
|
|
}),
|
|
createApplicationAlpr({ surface, source: sources.alpr }),
|
|
createApplicationStreetLevel({
|
|
sources: { mapillary: sources.mapillary },
|
|
}),
|
|
satellites,
|
|
createApplicationLaunches({ source: sources.launches, satellites }),
|
|
createApplicationTraffic({ source: sources.traffic, surface }),
|
|
createApplicationCctv({ surface, source: sources.cctv }),
|
|
createApplicationRadio({ surface, source: sources.radio }),
|
|
createApplicationTransit({ surface, source: sources.transit }),
|
|
createApplicationBikeshare({ source: sources.bikeshare }),
|
|
createApplicationDirections(),
|
|
createApplicationRecentImagery(),
|
|
vessels,
|
|
installations,
|
|
createApplicationAwareness({
|
|
flights,
|
|
military,
|
|
vessels,
|
|
installations,
|
|
}),
|
|
createWindLayer({ feed: sources.wind, clock: weatherClock }),
|
|
createWeatherLayer({
|
|
feed: sources.weather,
|
|
id: 'weather-radar',
|
|
clock: weatherClock,
|
|
}),
|
|
createWeatherLayer({
|
|
feed: sources.weather,
|
|
id: 'weather-satellite',
|
|
clock: weatherClock,
|
|
}),
|
|
createWeatherLayer({
|
|
feed: sources.weather,
|
|
id: 'weather-lightning',
|
|
clock: weatherClock,
|
|
}),
|
|
createCyclonesLayer({ feed: sources.cyclones }),
|
|
...createInfrastructureLayers(localGeoJsonServices),
|
|
createApplicationCables({ source: sources.cables }),
|
|
createApplicationFirms({
|
|
surface,
|
|
id: 'local-firms',
|
|
name: 'FIRMS Active Fires',
|
|
icon: '▲',
|
|
source: 'NASA FIRMS · LIVE',
|
|
feed: sources.firms,
|
|
}),
|
|
],
|
|
metadata,
|
|
);
|
|
return Object.freeze({
|
|
...catalog,
|
|
militaryRegistry,
|
|
surface,
|
|
weatherClock,
|
|
});
|
|
} catch (error) {
|
|
dispose();
|
|
throw error;
|
|
}
|
|
}
|