import { resolveCatalogSources } from './sourceComposition.js'; 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 { createApplicationOsmPlaces } from './layers/osmPlaces.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 { createApplicationAirQuality } from './layers/airQuality.js'; import { createApplicationFirePerimeters } from './layers/perimeters.js'; import { createApplicationGnssIntegrity } from './layers/gnss.js'; import { createApplicationGhostMaps } from './layers/ghostMaps.js'; import { createApplicationDroneAirspace } from './layers/droneAirspace.js'; import { createApplicationCables } from './layers/submarineCables.js'; import { createOsmOverlayLayers } from '../data/osmOverlays.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'; /** * 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' }), // Voice search results belong to this session, like a receiver to this machine. Object.freeze({ id: 'osm-places', 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, streetLevelProviders, }) { 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'); const sourceComposition = resolveCatalogSources(sources); sources = sourceComposition.sources; const militaryRegistry = createMilitaryRegistry(); const weatherClock = createWeatherClock(); const dispose = () => { signal.removeEventListener('abort', dispose); militaryRegistry.dispose(); weatherClock.destroy(); }; signal.addEventListener('abort', dispose, { once: true }); try { if (sourceComposition.isConfigured('military')) 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 }), createApplicationAirQuality({ source: sources['air-quality'] }), createApplicationFirePerimeters({ source: sources['fire-perimeters'], }), createApplicationGnssIntegrity({ source: sources['gnss-integrity'], windowMs: sources['gnss-integrity'].windowMs, }), createApplicationGhostMaps({ source: sources['s2-ghostmaps'] }), createApplicationDroneAirspace({ source: sources['drone-airspace'], }), createApplicationAlpr({ surface, source: sources.alpr }), createApplicationStreetLevel({ surface, sources: { mapillary: sources.mapillary }, providers: streetLevelProviders, }), 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({ source: sources.directions }), createApplicationRecentImagery({ source: sources['recent-imagery'] }), createApplicationOsmPlaces(), 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 }), ...createOsmOverlayLayers(), ...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, getSourceAvailability: sourceComposition.createAvailabilityLookup( catalog.layers, ), militaryRegistry, surface, weatherClock, }); } catch (error) { dispose(); throw error; } }