From 8d7f05557223c7506ed98e38742d016bc0bcb718 Mon Sep 17 00:00:00 2001 From: German Khokhlov Date: Tue, 3 Nov 2026 23:02:04 +0400 Subject: [PATCH] Format the files this branch wrote or broke with Prettier --- scripts/fixtures/w2/radio.mjs | 131 ++++---- src/app/constructCatalog.js | 289 ++++++++++++------ src/app/layers/directions.js | 6 +- src/app/layers/gnss.js | 7 +- src/app/layers/perimeters.js | 13 +- src/app/layers/renderers.js | 3 +- src/app/layers/submarineCables.js | 20 +- src/app/layers/traffic.js | 7 +- src/app/viewerlessTools.test.mjs | 24 +- src/data/aircraftEmergencyAnalyst.test.mjs | 2 +- src/data/bhoteKoshiEmbeddedMedia.js | 17 +- src/data/bhoteKoshiEvent.js | 3 +- src/data/bhoteKoshiLocator.js | 29 +- src/data/directions.js | 6 +- src/data/infrastructure.js | 5 +- src/data/layerStateCoordinator.js | 36 ++- src/data/localGeojsonCore.js | 7 +- src/layers/airQuality/index.js | 12 +- src/layers/airQuality/ownership.test.mjs | 10 +- src/layers/airQuality/render/navara.js | 13 +- src/layers/alpr/index.js | 8 +- src/layers/awareness/focus.js | 12 +- src/layers/awareness/lifecycle.js | 76 ++--- src/layers/bikeshare/index.js | 3 +- .../bikeshare/render/cesium/lifecycle.js | 4 +- src/layers/cctv/lifecycle.js | 27 +- src/layers/cctv/projection.js | 24 +- src/layers/cyclones/index.js | 36 ++- src/layers/cyclones/index.test.mjs | 18 +- src/layers/directions/index.js | 47 ++- src/layers/earthquakes/index.js | 17 +- src/layers/earthquakes/ownership.test.mjs | 15 +- src/layers/earthquakes/render/navara.js | 7 +- src/layers/firms/lifecycle.js | 8 +- src/layers/flights/lifecycle.js | 8 +- src/layers/flights/snapshotRenderer.js | 3 +- src/layers/ghostMaps/lifecycle.test.mjs | 11 +- src/layers/ghostMaps/render/navara.js | 21 +- src/layers/gnss/index.js | 8 +- src/layers/gnss/ownership.test.mjs | 10 +- src/layers/installations/index.js | 11 +- .../installations/render/cesium/rendering.js | 3 +- src/layers/installations/viewport.js | 5 +- src/layers/launches/rendering.js | 8 +- src/layers/military/lifecycle.js | 5 +- src/layers/military/queries.js | 3 +- src/layers/military/renderer.test.mjs | 23 +- src/layers/military/snapshotRenderer.js | 9 +- src/layers/military/tracking.js | 5 +- src/layers/perimeters/index.js | 39 ++- src/layers/perimeters/ownership.test.mjs | 32 +- src/layers/radio/rendering.js | 5 +- src/layers/recentImagery/index.js | 2 +- src/layers/satellites/controls.js | 3 +- src/layers/satellites/lifecycle.js | 65 +++- src/layers/satellites/tracking.js | 5 +- src/layers/shared/render/navaraGroundCells.js | 34 ++- src/layers/streetLevel/cameraFraming.js | 27 +- src/layers/streetLevel/credits.js | 5 +- src/layers/streetLevel/groundClick.js | 4 +- src/layers/streetLevel/index.js | 56 ++-- src/layers/streetLevel/lineCoverage.js | 32 +- .../streetLevel/providers/panoramax/index.js | 4 +- src/layers/streetLevel/selection.js | 37 +-- src/layers/streetLevel/sequenceCones.js | 3 +- src/layers/streetLevel/view.js | 27 +- src/layers/submarineCables/overlay.js | 10 +- src/layers/submarineCables/policy.js | 8 +- src/layers/traffic/animation.js | 9 +- src/layers/traffic/flow.js | 16 +- src/layers/traffic/footprint.js | 4 +- src/layers/traffic/ingestion.js | 12 +- src/layers/traffic/lifecycle.js | 154 +++++++--- src/layers/traffic/policy.js | 3 +- src/layers/traffic/rendering.js | 36 ++- src/layers/traffic/retention.js | 13 +- src/layers/traffic/state.js | 4 +- src/layers/traffic/surface.js | 9 +- src/layers/traffic/viewport.js | 39 ++- src/layers/transit/lifecycle.js | 19 +- src/layers/transit/viewport.js | 3 +- src/layers/vessels/renderer.test.mjs | 33 +- src/layers/weather/index.js | 46 ++- src/layers/weather/render/navaraSurface.js | 8 +- src/layers/weather/shellRendering.js | 7 +- src/layers/wind/index.js | 34 ++- src/layers/wind/inspectionMarker.js | 16 +- src/scenes/dataPacks/presentation.js | 79 ++--- src/ui/contextControls.js | 10 +- src/ui/shareRestoration.js | 21 +- 90 files changed, 1408 insertions(+), 640 deletions(-) diff --git a/scripts/fixtures/w2/radio.mjs b/scripts/fixtures/w2/radio.mjs index aa0499398..38b4c4eed 100644 --- a/scripts/fixtures/w2/radio.mjs +++ b/scripts/fixtures/w2/radio.mjs @@ -38,71 +38,72 @@ export const stations = Array.from({ length: 750 }, (_, index) => { }); export async function installRadioFixture(page) { -await page.evaluateOnNewDocument((stations) => { - localStorage.clear(); - localStorage.setItem('gev:first-run-mission:v1', 'suppressed'); - window.__qaRadio = { - stations, - generation: 1, - plays: [], - delay: 0, - response: 'normal', - pending: null, - }; - HTMLMediaElement.prototype.play = function () { - window.qaPlayer = this; - window.__qaRadio.plays.push(this.src); - if (window.__qaRadio.delayPlay) { - window.__qaRadio.delayPlay = false; - return new Promise((resolve) => { - window.__qaRadio.releasePlay = () => { - this.dispatchEvent(new Event('playing')); - resolve(); - }; - }); - } - queueMicrotask(() => this.dispatchEvent(new Event('playing'))); - return Promise.resolve(); - }; - const fetch = window.fetch.bind(window); - window.fetch = async (input, init) => { - const address = String( - typeof input === 'string' || input instanceof URL ? input : input.url, - ); - if (address.includes('/api/radio/stations')) { - const qa = window.__qaRadio; - qa.reads = (qa.reads || 0) + 1; - const response = { - stations: qa.response === 'empty' ? [] : qa.stations, - updatedAt: new Date().toISOString(), - stale: false, - degraded: false, - acceptedGeneration: qa.generation, - catalogInstance: 'w2-radio-fixture', - }; - const mode = qa.response; - const delay = qa.delay; - qa.delay = 0; - if (delay) - await new Promise((resolve) => { - qa.pending = resolve; + await page.evaluateOnNewDocument((stations) => { + localStorage.clear(); + localStorage.setItem('gev:first-run-mission:v1', 'suppressed'); + window.__qaRadio = { + stations, + generation: 1, + plays: [], + delay: 0, + response: 'normal', + pending: null, + }; + HTMLMediaElement.prototype.play = function () { + window.qaPlayer = this; + window.__qaRadio.plays.push(this.src); + if (window.__qaRadio.delayPlay) { + window.__qaRadio.delayPlay = false; + return new Promise((resolve) => { + window.__qaRadio.releasePlay = () => { + this.dispatchEvent(new Event('playing')); + resolve(); + }; }); - init?.signal?.throwIfAborted(); - return new Response( - mode === 'failure' ? '{}' : JSON.stringify(response), - { - status: mode === 'failure' ? 503 : 200, - headers: { 'Content-Type': 'application/json' }, - }, + } + queueMicrotask(() => this.dispatchEvent(new Event('playing'))); + return Promise.resolve(); + }; + const fetch = window.fetch.bind(window); + window.fetch = async (input, init) => { + const address = String( + typeof input === 'string' || input instanceof URL ? input : input.url, ); - } - if (address.includes('/api/radio/click/')) - return new Response(null, { status: 204 }); - if (address.includes('/api/flights')) return new Response('{"states":[]}'); - if (address.includes('/api/military')) return new Response('{"ac":[]}'); - if (address.includes('/api/vessels')) - return new Response('{"connected":false,"vessels":[]}'); - return fetch(input, init); - }; -}, stations); + if (address.includes('/api/radio/stations')) { + const qa = window.__qaRadio; + qa.reads = (qa.reads || 0) + 1; + const response = { + stations: qa.response === 'empty' ? [] : qa.stations, + updatedAt: new Date().toISOString(), + stale: false, + degraded: false, + acceptedGeneration: qa.generation, + catalogInstance: 'w2-radio-fixture', + }; + const mode = qa.response; + const delay = qa.delay; + qa.delay = 0; + if (delay) + await new Promise((resolve) => { + qa.pending = resolve; + }); + init?.signal?.throwIfAborted(); + return new Response( + mode === 'failure' ? '{}' : JSON.stringify(response), + { + status: mode === 'failure' ? 503 : 200, + headers: { 'Content-Type': 'application/json' }, + }, + ); + } + if (address.includes('/api/radio/click/')) + return new Response(null, { status: 204 }); + if (address.includes('/api/flights')) + return new Response('{"states":[]}'); + if (address.includes('/api/military')) return new Response('{"ac":[]}'); + if (address.includes('/api/vessels')) + return new Response('{"connected":false,"vessels":[]}'); + return fetch(input, init); + }; + }, stations); } diff --git a/src/app/constructCatalog.js b/src/app/constructCatalog.js index cfafac6e0..0f5b4689f 100644 --- a/src/app/constructCatalog.js +++ b/src/app/constructCatalog.js @@ -86,7 +86,11 @@ export function createApplicationCatalog({ const weatherClock = layerSession?.weatherClock ?? createWeatherClock(); if (layerSession && !layerSession.weatherClock) { layerSession.weatherClock = weatherClock; - layerSession.lifetime.signal.addEventListener('abort', weatherClock.destroy, { once: true }); + layerSession.lifetime.signal.addEventListener( + 'abort', + weatherClock.destroy, + { once: true }, + ); } const dispose = () => { signal.removeEventListener('abort', dispose); @@ -98,7 +102,9 @@ export function createApplicationCatalog({ if (!layerSession?.militaryRegistry) { const lifetime = layerSession?.lifetime.signal ?? signal; if (sourceComposition.isConfigured('military')) - militaryRegistry.configureSource(sources.military, { signal: lifetime }); + militaryRegistry.configureSource(sources.military, { + signal: lifetime, + }); if (layerSession) { layerSession.militaryRegistry = militaryRegistry; lifetime.addEventListener('abort', militaryRegistry.dispose, { @@ -129,12 +135,14 @@ export function createApplicationCatalog({ ), ); const military = once(() => - sessionModel('military', () => createApplicationMilitary({ - surface, - source: sources.military, - militaryRegistry, - resolveAsset, - })), + sessionModel('military', () => + createApplicationMilitary({ + surface, + source: sources.military, + militaryRegistry, + resolveAsset, + }), + ), ); const vessels = once(() => sessionModel('ais-live-vessels', () => @@ -153,39 +161,53 @@ export function createApplicationCatalog({ ), ); const satellites = once(() => - sessionModel('satellites', () => createApplicationSatellites({ - source: sources.satellites, - })), + sessionModel('satellites', () => + createApplicationSatellites({ + source: sources.satellites, + }), + ), ); // Catalog order: [layer id(s), construction]. Only the layers the // engine can show are constructed (renderers.js). const entries = [ - ['bhote-koshi-2026', () => sessionModel('bhote-koshi-2026', () => createBhoteKoshiEventLayer({ - createNativeRenderer: (engine) => rendererFor('bhote-koshi-2026', engine), - }))], + [ + 'bhote-koshi-2026', + () => + sessionModel('bhote-koshi-2026', () => + createBhoteKoshiEventLayer({ + createNativeRenderer: (engine) => + rendererFor('bhote-koshi-2026', engine), + }), + ), + ], [ 'bhote-koshi-locator', () => - sessionModel('bhote-koshi-locator', () => createBhoteKoshiLocatorLayer({ - boundaryResolver: nepalBoundaryResolver, - createNativeRenderer: (engine, data) => rendererFor('bhote-koshi-locator', engine, { data }), - })), + sessionModel('bhote-koshi-locator', () => + createBhoteKoshiLocatorLayer({ + boundaryResolver: nepalBoundaryResolver, + createNativeRenderer: (engine, data) => + rendererFor('bhote-koshi-locator', engine, { data }), + }), + ), ], ['flights', flights], ['military', military], [ 'local-adsb', () => - sessionModel('local-adsb', () => createApplicationLocalAdsb({ - surface, - // QA fixture receivers (?qaFeed=local-adsb:fixture, dev only). - ...(sources.localAdsbFeeds - ? { feeds: sources.localAdsbFeeds } - : {}), - enrichment: sources.flights, - displayParams: () => flights().getParams(), - ...(resolveAsset ? { resolveAsset } : {}), - })), + sessionModel('local-adsb', () => + createApplicationLocalAdsb({ + surface, + // QA fixture receivers (?qaFeed=local-adsb:fixture, dev only). + ...(sources.localAdsbFeeds + ? { feeds: sources.localAdsbFeeds } + : {}), + enrichment: sources.flights, + displayParams: () => flights().getParams(), + ...(resolveAsset ? { resolveAsset } : {}), + }), + ), ], [ 'earthquakes', @@ -196,144 +218,215 @@ export function createApplicationCatalog({ ], [ 'air-quality', - () => sessionModel('air-quality', () => createApplicationAirQuality({ source: sources['air-quality'] })), + () => + sessionModel('air-quality', () => + createApplicationAirQuality({ source: sources['air-quality'] }), + ), ], [ 'fire-perimeters', () => - sessionModel('fire-perimeters', () => createApplicationFirePerimeters({ - source: sources['fire-perimeters'], - })), + sessionModel('fire-perimeters', () => + createApplicationFirePerimeters({ + source: sources['fire-perimeters'], + }), + ), ], [ 'gnss-integrity', () => - sessionModel('gnss-integrity', () => createApplicationGnssIntegrity({ - source: sources['gnss-integrity'], - windowMs: sources['gnss-integrity'].windowMs, - })), + sessionModel('gnss-integrity', () => + createApplicationGnssIntegrity({ + source: sources['gnss-integrity'], + windowMs: sources['gnss-integrity'].windowMs, + }), + ), ], [ 's2-ghostmaps', - () => sessionModel('s2-ghostmaps', () => createApplicationGhostMaps({ source: sources['s2-ghostmaps'] })), + () => + sessionModel('s2-ghostmaps', () => + createApplicationGhostMaps({ source: sources['s2-ghostmaps'] }), + ), ], [ 'alpr-cameras', - () => sessionModel('alpr-cameras', () => createApplicationAlpr({ surface, source: sources.alpr })), + () => + sessionModel('alpr-cameras', () => + createApplicationAlpr({ surface, source: sources.alpr }), + ), ], [ 'street-level', () => - sessionModel('street-level', () => createApplicationStreetLevel({ - surface, - nativeRenderer:engine=>rendererFor('street-level',engine), - sources: { mapillary: sources.mapillary }, - providers: streetLevelProviders, - })), + sessionModel('street-level', () => + createApplicationStreetLevel({ + surface, + nativeRenderer: (engine) => rendererFor('street-level', engine), + sources: { mapillary: sources.mapillary }, + providers: streetLevelProviders, + }), + ), ], ['satellites', satellites], [ 'rocket-launches', () => - sessionModel('rocket-launches', () => createApplicationLaunches({ - source: sources.launches, - satellites: satellites(), - })), + sessionModel('rocket-launches', () => + createApplicationLaunches({ + source: sources.launches, + satellites: satellites(), + }), + ), ], [ 'traffic', - () => sessionModel('traffic', () => createApplicationTraffic({ - source: sources.traffic, surface, nativeRenderer: engine => rendererFor('traffic',engine), - })), + () => + sessionModel('traffic', () => + createApplicationTraffic({ + source: sources.traffic, + surface, + nativeRenderer: (engine) => rendererFor('traffic', engine), + }), + ), + ], + [ + 'cctv', + () => + sessionModel('cctv', () => + createApplicationCctv({ surface, source: sources.cctv }), + ), ], - ['cctv', () => sessionModel('cctv', () => createApplicationCctv({ surface, source: sources.cctv }))], [ 'radio', - () => sessionModel('radio', () => createApplicationRadio({ surface, source: sources.radio })), + () => + sessionModel('radio', () => + createApplicationRadio({ surface, source: sources.radio }), + ), ], [ 'transit', - () => sessionModel('transit', () => createApplicationTransit({ surface, source: sources.transit })), + () => + sessionModel('transit', () => + createApplicationTransit({ surface, source: sources.transit }), + ), ], [ 'bikeshare', - () => sessionModel('bikeshare', () => createApplicationBikeshare({ source: sources.bikeshare })), + () => + sessionModel('bikeshare', () => + createApplicationBikeshare({ source: sources.bikeshare }), + ), ], [ 'directions', - () => sessionModel('directions', () => createApplicationDirections({ source: sources.directions, surface })), + () => + sessionModel('directions', () => + createApplicationDirections({ + source: sources.directions, + surface, + }), + ), ], [ 'recent-imagery', () => - sessionModel('recent-imagery', () => createApplicationRecentImagery({ - source: sources['recent-imagery'], - })), + sessionModel('recent-imagery', () => + createApplicationRecentImagery({ + source: sources['recent-imagery'], + }), + ), ], ['ais-live-vessels', vessels], ['military-installations', installations], [ 'military-awareness', - () => sessionModel('military-awareness', () => - createApplicationAwareness({ - flights: flights(), - military: military(), - vessels: vessels(), - installations: installations(), - }), - ), + () => + sessionModel('military-awareness', () => + createApplicationAwareness({ + flights: flights(), + military: military(), + vessels: vessels(), + installations: installations(), + }), + ), ], [ 'wind', - () => sessionModel('wind', () => createWindLayer({ - feed: sources.wind, - clock: weatherClock, - createNativeRendering: (engine) => rendererFor('wind', engine), - })), + () => + sessionModel('wind', () => + createWindLayer({ + feed: sources.wind, + clock: weatherClock, + createNativeRendering: (engine) => rendererFor('wind', engine), + }), + ), ], ...['weather-radar', 'weather-satellite', 'weather-lightning'].map( (id) => [ id, - () => sessionModel(id, () => - createWeatherLayer({ - feed: sources.weather, - id, - clock: weatherClock, - createNativeRendering: (engine, options) => rendererFor(id, engine, options), - }), - ), + () => + sessionModel(id, () => + createWeatherLayer({ + feed: sources.weather, + id, + clock: weatherClock, + createNativeRendering: (engine, options) => + rendererFor(id, engine, options), + }), + ), ], ), [ 'weather-cyclones', - () => sessionModel('weather-cyclones', () => createCyclonesLayer({ - feed: sources.cyclones, - createNativeRendering: (engine, options) => rendererFor('weather-cyclones', engine, options), - })), + () => + sessionModel('weather-cyclones', () => + createCyclonesLayer({ + feed: sources.cyclones, + createNativeRendering: (engine, options) => + rendererFor('weather-cyclones', engine, options), + }), + ), ], [ ['local-datacenters', 'local-dams'], - () => sessionModel('local-infrastructure', () => - createInfrastructureLayers(localGeoJsonServices, (config, services) => - createInfrastructureSessionLayer(config, services, (engine, descriptor, ops) => - rendererFor(config.id,engine,{config:descriptor,services:ops}))), - ), + () => + sessionModel('local-infrastructure', () => + createInfrastructureLayers( + localGeoJsonServices, + (config, services) => + createInfrastructureSessionLayer( + config, + services, + (engine, descriptor, ops) => + rendererFor(config.id, engine, { + config: descriptor, + services: ops, + }), + ), + ), + ), ], [ 'telegeography-submarine-cables', - () => sessionModel('telegeography-submarine-cables', () => createApplicationCables({ source: sources.cables, session: true })), + () => + sessionModel('telegeography-submarine-cables', () => + createApplicationCables({ source: sources.cables, session: true }), + ), ], [ 'local-firms', () => - sessionModel('local-firms', () => createApplicationFirms({ - surface, - id: 'local-firms', - name: 'FIRMS Active Fires', - icon: '▲', - source: 'NASA FIRMS · LIVE', - feed: sources.firms, - })), + sessionModel('local-firms', () => + createApplicationFirms({ + surface, + id: 'local-firms', + name: 'FIRMS Active Fires', + icon: '▲', + source: 'NASA FIRMS · LIVE', + feed: sources.firms, + }), + ), ], ]; const available = (id) => availability.isAvailable(id); diff --git a/src/app/layers/directions.js b/src/app/layers/directions.js index f6a73fd0d..abcc8cf3d 100644 --- a/src/app/layers/directions.js +++ b/src/app/layers/directions.js @@ -4,5 +4,9 @@ import { rendererFor } from './renderers.js'; /** Construct Directions with application scene owners and an explicit route source. */ export function createApplicationDirections({ source, surface }) { - return createApplicationDirectionsLayer({ source, groundFloor: surface?.groundFloor, createNativeRenderer: (engine) => rendererFor('directions', engine) }); + return createApplicationDirectionsLayer({ + source, + groundFloor: surface?.groundFloor, + createNativeRenderer: (engine) => rendererFor('directions', engine), + }); } diff --git a/src/app/layers/gnss.js b/src/app/layers/gnss.js index 7bb6443e7..0b0bb435d 100644 --- a/src/app/layers/gnss.js +++ b/src/app/layers/gnss.js @@ -5,9 +5,10 @@ import { createGnssIntegrityLayer } from '../../layers/gnss/index.js'; /** Wire the adsb.lol GNSS navigation-integrity layer into the application catalog. */ export function createApplicationGnssIntegrity(options) { return createGnssIntegrityLayer({ - createRenderer: ({ viewer, engine, viewAnchor }) => engine - ? rendererFor('gnss-integrity', engine, { viewer, viewAnchor }) - : createGnssCesiumRenderer({ viewer, viewAnchor }), + createRenderer: ({ viewer, engine, viewAnchor }) => + engine + ? rendererFor('gnss-integrity', engine, { viewer, viewAnchor }) + : createGnssCesiumRenderer({ viewer, viewAnchor }), ...options, }); } diff --git a/src/app/layers/perimeters.js b/src/app/layers/perimeters.js index 8b18e39e1..9ca24571f 100644 --- a/src/app/layers/perimeters.js +++ b/src/app/layers/perimeters.js @@ -24,9 +24,16 @@ export function createApplicationFirePerimeters(options) { openExternal: (url) => window.open(url, '_blank', 'noopener,noreferrer'), screenSpaceEventHandlerFactory: (viewer) => new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas), - createRenderer: ({viewer, engine, screenSpaceEventHandlerFactory}) => engine - ? rendererFor('fire-perimeters', engine, {viewer, screenSpaceEventHandlerFactory}) - : createPerimetersCesiumRenderer({viewer, screenSpaceEventHandlerFactory}), + createRenderer: ({ viewer, engine, screenSpaceEventHandlerFactory }) => + engine + ? rendererFor('fire-perimeters', engine, { + viewer, + screenSpaceEventHandlerFactory, + }) + : createPerimetersCesiumRenderer({ + viewer, + screenSpaceEventHandlerFactory, + }), picking, pointer: { isPointerFree }, ...options, diff --git a/src/app/layers/renderers.js b/src/app/layers/renderers.js index 7bbe026b3..d94d6c3cd 100644 --- a/src/app/layers/renderers.js +++ b/src/app/layers/renderers.js @@ -97,7 +97,8 @@ export const CESIUM_LAYER_RENDERERS = Object.freeze({ earthquakes: createEarthquakesCesiumRenderer, 'air-quality': (_engine, options) => createAirQualityCesiumRenderer(options), 'gnss-integrity': (_engine, options) => createGnssCesiumRenderer(options), - 'fire-perimeters': (_engine, options) => createPerimetersCesiumRenderer(options), + 'fire-perimeters': (_engine, options) => + createPerimetersCesiumRenderer(options), 'local-firms': createFirmsCesiumRenderer, 'rocket-launches': createLaunchesCesiumRenderer, satellites: createSatellitesCesiumRenderer, diff --git a/src/app/layers/submarineCables.js b/src/app/layers/submarineCables.js index c90926a02..6e1e60956 100644 --- a/src/app/layers/submarineCables.js +++ b/src/app/layers/submarineCables.js @@ -5,18 +5,20 @@ import { rendererFor } from './renderers.js'; import { overlayHost } from './overlayHost.js'; /** Wire cable source geometry to the application overlay host. */ export function createApplicationCables({ session = false, ...options }) { - const createCesium = (source) => createSubmarineCableLayer({ - overlayHost, - screenSpaceEventHandlerFactory: (canvas) => - new Cesium.ScreenSpaceEventHandler(canvas), - mapStackEventTarget: typeof window !== 'undefined' ? window : null, - ...options, - source, - }); + const createCesium = (source) => + createSubmarineCableLayer({ + overlayHost, + screenSpaceEventHandlerFactory: (canvas) => + new Cesium.ScreenSpaceEventHandler(canvas), + mapStackEventTarget: typeof window !== 'undefined' ? window : null, + ...options, + source, + }); if (!session) return createCesium(options.source); return createCableSessionLayer({ source: options.source, createCesium, - createNavara: (engine) => rendererFor('telegeography-submarine-cables', engine, { overlayHost }), + createNavara: (engine) => + rendererFor('telegeography-submarine-cables', engine, { overlayHost }), }); } diff --git a/src/app/layers/traffic.js b/src/app/layers/traffic.js index f4d8d04cc..9ed2b2feb 100644 --- a/src/app/layers/traffic.js +++ b/src/app/layers/traffic.js @@ -7,6 +7,11 @@ import * as render from '../../renderGovernor.js'; export function createApplicationTraffic({ source, surface, nativeRenderer }) { return createTrafficLayer({ source, - services: { credits, render, nativeRenderer, ground: surface?.groundFloor || ground }, + services: { + credits, + render, + nativeRenderer, + ground: surface?.groundFloor || ground, + }, }); } diff --git a/src/app/viewerlessTools.test.mjs b/src/app/viewerlessTools.test.mjs index 3ab52138e..2e5587566 100644 --- a/src/app/viewerlessTools.test.mjs +++ b/src/app/viewerlessTools.test.mjs @@ -17,15 +17,33 @@ import { // The tools without a viewer (the experimental Navara engine); they load // from src/app/navaraMode.js. test('without a viewer the tools run the governor on the engine, show the notice, create no viewer tools and run voice on the engine', async (t) => { - const saved = { window: globalThis.window, document: globalThis.document, localStorage:globalThis.localStorage }; + const saved = { + window: globalThis.window, + document: globalThis.document, + localStorage: globalThis.localStorage, + }; const listeners = new Map(); globalThis.window = {}; - globalThis.localStorage = { getItem:()=>null,setItem(){},removeItem(){} }; + globalThis.localStorage = { + getItem: () => null, + setItem() {}, + removeItem() {}, + }; globalThis.document = { hidden: false, getElementById: () => null, querySelector: () => null, - body: {classList:{add(){},remove(){},toggle(){},contains(){return false;}},appendChild(){}}, + body: { + classList: { + add() {}, + remove() {}, + toggle() {}, + contains() { + return false; + }, + }, + appendChild() {}, + }, addEventListener: (type, fn) => listeners.set(type, fn), removeEventListener: (type, fn) => { if (listeners.get(type) === fn) listeners.delete(type); diff --git a/src/data/aircraftEmergencyAnalyst.test.mjs b/src/data/aircraftEmergencyAnalyst.test.mjs index 9abbd12da..6c558dc05 100644 --- a/src/data/aircraftEmergencyAnalyst.test.mjs +++ b/src/data/aircraftEmergencyAnalyst.test.mjs @@ -483,7 +483,7 @@ test('GERMAN: an existing untracked military billboard repaints each poll withou }); flightState.records = records; flightState._renderer = { view: {}, surfaceExclusions: () => [] }; - const {applySnapshot: apply} = createMilitarySnapshotRenderer({ + const { applySnapshot: apply } = createMilitarySnapshotRenderer({ flightState, records, groundFloor: { warmGroundFloor() {}, cachedGroundFloor: () => null }, diff --git a/src/data/bhoteKoshiEmbeddedMedia.js b/src/data/bhoteKoshiEmbeddedMedia.js index 621f5c1a6..d75bf8612 100644 --- a/src/data/bhoteKoshiEmbeddedMedia.js +++ b/src/data/bhoteKoshiEmbeddedMedia.js @@ -550,12 +550,13 @@ function anchorOnScreen(viewer, engine, anchor, root) { ); if (Cesium.Cartesian3.dot(delta, camera.directionWC) <= 0) return null; } - const pixel = engine ? engine.projector.worldToWindow(anchor, {}) : Cesium.SceneTransforms.worldToWindowCoordinates( - viewer.scene, - anchor, - ); + const pixel = engine + ? engine.projector.worldToWindow(anchor, {}) + : Cesium.SceneTransforms.worldToWindowCoordinates(viewer.scene, anchor); if (!pixel) return null; - const canvasBounds = (engine?.host.canvas ?? viewer?.canvas)?.getBoundingClientRect?.(); + const canvasBounds = ( + engine?.host.canvas ?? viewer?.canvas + )?.getBoundingClientRect?.(); const rootBounds = root.getBoundingClientRect?.(); if (!canvasBounds || !rootBounds) return null; const x = pixel.x + canvasBounds.left - rootBounds.left; @@ -638,7 +639,11 @@ function createRoot(documentRef, viewer) { const root = documentRef.createElement('div'); root.id = 'bhote-koshi-embedded-media-root'; root.setAttribute('aria-live', 'polite'); - (documentRef.getElementById?.('cesiumContainer') || viewer?.container || documentRef.body)?.append(root); + ( + documentRef.getElementById?.('cesiumContainer') || + viewer?.container || + documentRef.body + )?.append(root); return root; } diff --git a/src/data/bhoteKoshiEvent.js b/src/data/bhoteKoshiEvent.js index 2bd89f80b..0595f3fb4 100644 --- a/src/data/bhoteKoshiEvent.js +++ b/src/data/bhoteKoshiEvent.js @@ -47,7 +47,8 @@ const SCENE_PATH_RENDER_HOLD = 'bhote-koshi-scene-path-travel'; const SCENE_MEDIA_MIN_PLAYBACK_SECONDS = 6; const SCENE_MEDIA_EXIT_SECONDS = 0.65; const BHOTE_EVENT_ACCENT = WORLD_OVERLAY_STYLE.accent; -const FLOOD_COLOR = () => Cesium.Color.fromCssColorString('#d8954e').withAlpha(0.68); +const FLOOD_COLOR = () => + Cesium.Color.fromCssColorString('#d8954e').withAlpha(0.68); const SURGE_COLOR = () => Cesium.Color.fromCssColorString('#e8b47c'); const CINEMATIC_INPUT_EVENTS = Object.freeze(['pointerdown', 'wheel']); const CINEMATIC_VIEW_SPECS = Object.freeze([ diff --git a/src/data/bhoteKoshiLocator.js b/src/data/bhoteKoshiLocator.js index 1282748d8..3faa7eeb7 100644 --- a/src/data/bhoteKoshiLocator.js +++ b/src/data/bhoteKoshiLocator.js @@ -291,7 +291,10 @@ export function createBhoteKoshiLocatorLayer({ let _pausedApproach = null; const cameraForScene = () => _engine?.camera ?? _viewer?.camera; const creditTarget = () => _engine ?? _viewer; - const requestRender = reason => _engine?.kind === 'navara' ? _engine.render.request(reason) : requestSceneRender(reason); + const requestRender = (reason) => + _engine?.kind === 'navara' + ? _engine.render.request(reason) + : requestSceneRender(reason); let _dataSource = null; let _enabled = false; let _boundaryReady = false; @@ -651,10 +654,17 @@ export function createBhoteKoshiLocatorLayer({ roll: Cesium.Math.toRadians(pose.roll), }, duration, - easingFunction: orbit ? Cesium.EasingFunction.SINUSOIDAL_IN_OUT : Cesium.EasingFunction.CUBIC_IN_OUT, + easingFunction: orbit + ? Cesium.EasingFunction.SINUSOIDAL_IN_OUT + : Cesium.EasingFunction.CUBIC_IN_OUT, complete: () => { if (_triggerApproachAttempt !== attempt) return; - if (orbit || !_enabled || _presentation !== BHOTE_KOSHI_TRIGGER_RECORD_PRESENTATION) finish(); + if ( + orbit || + !_enabled || + _presentation !== BHOTE_KOSHI_TRIGGER_RECORD_PRESENTATION + ) + finish(); else flyTriggerStage(attempt, 'orbiting', TRIGGER_ORBIT_DURATION_SEC); }, cancel: finish, @@ -1042,14 +1052,14 @@ export function createBhoteKoshiLocatorLayer({ showOsmCredit(creditTarget(), BHOTE_KOSHI_LOCATOR_LAYER_ID); } - function attachRenderer(viewer, {engine = null} = {}) { + function attachRenderer(viewer, { engine = null } = {}) { _viewer = viewer; _engine = engine; if (!_enabled) return true; connectDisplay(); - publishOverlayEntries({moving: _placeRevealMoving}); + publishOverlayEntries({ moving: _placeRevealMoving }); if (_pausedApproach) { - const {phase, remaining} = _pausedApproach; + const { phase, remaining } = _pausedApproach; _pausedApproach = null; if (phase === 'armed') armTriggerApproach(); else flyTriggerStage((_triggerApproachAttempt = {}), phase, remaining); @@ -1060,8 +1070,11 @@ export function createBhoteKoshiLocatorLayer({ function detachRenderer() { const phase = _triggerApproachAttempt?.phase; if (phase && phase !== 'finished') { - _pausedApproach = {phase, remaining: Math.max(0, (_triggerApproachAttempt.endsAt - now()) / 1000)}; - clearTriggerApproach({preserveCallout: true}); + _pausedApproach = { + phase, + remaining: Math.max(0, (_triggerApproachAttempt.endsAt - now()) / 1000), + }; + clearTriggerApproach({ preserveCallout: true }); } _native?.destroy(); _native = null; diff --git a/src/data/directions.js b/src/data/directions.js index 40046bc19..2bda66af1 100644 --- a/src/data/directions.js +++ b/src/data/directions.js @@ -43,7 +43,11 @@ export function createApplicationDirectionsLayer({ createNativeRenderer, groundFloor = ground, } = {}) { - return createDirectionsLayer({ services: { ...directionsServices, ground: groundFloor }, source, createNativeRenderer }); + return createDirectionsLayer({ + services: { ...directionsServices, ground: groundFloor }, + source, + createNativeRenderer, + }); } export { diff --git a/src/data/infrastructure.js b/src/data/infrastructure.js index 27b8ae93e..0adc9b5c8 100644 --- a/src/data/infrastructure.js +++ b/src/data/infrastructure.js @@ -6,7 +6,10 @@ import { INFRASTRUCTURE_DATA_URLS } from '../sources/infrastructureData.js'; * @param {object} services Caller-owned context, overlay and render operations. * @returns {object[]} Datacenters then dams, with stable standalone identities. */ -export function createInfrastructureLayers(services, createLayer = createLocalGeoJsonLayer) { +export function createInfrastructureLayers( + services, + createLayer = createLocalGeoJsonLayer, +) { const datacentersUrl = INFRASTRUCTURE_DATA_URLS['local-datacenters']; const damsUrl = INFRASTRUCTURE_DATA_URLS['local-dams']; const datacenters = createLayer( diff --git a/src/data/layerStateCoordinator.js b/src/data/layerStateCoordinator.js index 2af5963f9..9081b093f 100644 --- a/src/data/layerStateCoordinator.js +++ b/src/data/layerStateCoordinator.js @@ -406,12 +406,21 @@ export class LayerStateCoordinator { ); // A same-document engine choice retains operational layer intents. // The share still owns durable preferences and later history visits. - if (live && this.dataManager.layers.get(entry.id) === live.entry && - !live.entry.lifecycleUncertain && live.entry.enabled === targetEnabled) { + if ( + live && + this.dataManager.layers.get(entry.id) === live.entry && + !live.entry.lifecycleUncertain && + live.entry.enabled === targetEnabled + ) { return { - layerId: entry.id, targetEnabled, origin, phase: 'retained', + layerId: entry.id, + targetEnabled, + origin, + phase: 'retained', ...currentLayerOutcome(this.dataManager, entry.id), - appliedOptions: {}, persistenceWrite: false, succeeded: true, + appliedOptions: {}, + persistenceWrite: false, + succeeded: true, }; } const options = layerOptionsForRestore(this._durableState, entry.id); @@ -512,24 +521,33 @@ export class LayerStateCoordinator { const model = this.dataManager.layers.get(selected.layerId)?.module; if ( model?.attachRenderer && - String(model.getParams?.()[selected.optionKey]) === String(selected.targetId) && + String(model.getParams?.()[selected.optionKey]) === + String(selected.targetId) && model.hasContact?.(selected.targetId) === true - ) return selected; + ) + return selected; return null; } resumeRetainedTracking(previous) { const selected = this.retainedTrackingTarget(); if ( - !selected || selected.layerId !== previous.layerId || + !selected || + selected.layerId !== previous.layerId || selected.targetId !== previous.targetId - ) return null; + ) + return null; const model = this.dataManager.layers.get(selected.layerId).module; const followed = model.trackById(selected.targetId, { origin: LAYER_RESTORE_ORIGINS.share, }); if (!followed) return null; - const result = { ...selected, status: 'found', classification: 'followed', cleared: false }; + const result = { + ...selected, + status: 'found', + classification: 'followed', + cleared: false, + }; this._publishTrackingRestoreStatus(result); return result; } diff --git a/src/data/localGeojsonCore.js b/src/data/localGeojsonCore.js index dc473ce5e..865cd1062 100644 --- a/src/data/localGeojsonCore.js +++ b/src/data/localGeojsonCore.js @@ -585,7 +585,8 @@ export function createLocalGeoJsonLayer( // windows (before vs after the add settles) need different cleanup. let addedToScene = false; try { - let features = _cachedFeatures ?? (readFeatures ? await readFeatures() : null); + let features = + _cachedFeatures ?? (readFeatures ? await readFeatures() : null); if (_destroyed) return; if (!features) { const response = await fetch(url, { @@ -1065,7 +1066,9 @@ export function createLocalGeoJsonLayer( }, restoreFeatureSelection(viewer, featureId) { - const entity = _dataSource?.entities.values.find((item) => String(item.id) === featureId); + const entity = _dataSource?.entities.values.find( + (item) => String(item.id) === featureId, + ); if (!entity) return false; viewer.selectedEntity = entity; selectEntityContext(entity); diff --git a/src/layers/airQuality/index.js b/src/layers/airQuality/index.js index a5e9d7ba1..44fb3bff8 100644 --- a/src/layers/airQuality/index.js +++ b/src/layers/airQuality/index.js @@ -25,7 +25,11 @@ const STATION_MARKER_RADIUS_M = 6_000; const READOUT_LIMIT = 5; /** Own one air-quality display, its refresh lifecycle, and its readout card. */ -export function createAirQualityLayer({ source, overlayHost, createRenderer = createAirQualityCesiumRenderer } = {}) { +export function createAirQualityLayer({ + source, + overlayHost, + createRenderer = createAirQualityCesiumRenderer, +} = {}) { if (typeof source?.getSnapshot !== 'function') throw new TypeError('Air quality requires a snapshot source'); if (!overlayHost) throw new TypeError('Air quality requires an overlay host'); @@ -61,7 +65,8 @@ export function createAirQualityLayer({ source, overlayHost, createRenderer = cr updateInterval: 1_200_000, init(viewer, { engine = null } = {}) { - if (initialized) throw new Error('Air quality layer is already initialized'); + if (initialized) + throw new Error('Air quality layer is already initialized'); initialized = true; try { if (viewer || engine) this.attachRenderer(viewer, { engine }); @@ -72,7 +77,8 @@ export function createAirQualityLayer({ source, overlayHost, createRenderer = cr }, attachRenderer(viewer, { engine = null } = {}) { - if (!initialized || renderer) throw new Error('Invalid air quality scene attachment'); + if (!initialized || renderer) + throw new Error('Invalid air quality scene attachment'); renderer = createRenderer({ viewer, engine }); renderer.setVisible(_enabled); overlayHost.setVisible(AIR_QUALITY_OVERLAY_SOURCE_ID, _enabled); diff --git a/src/layers/airQuality/ownership.test.mjs b/src/layers/airQuality/ownership.test.mjs index 2a0895caa..74d69b757 100644 --- a/src/layers/airQuality/ownership.test.mjs +++ b/src/layers/airQuality/ownership.test.mjs @@ -276,10 +276,12 @@ test('a failed refresh over good readings reports degraded, not nominal', async test('station data stays queryable and attaches to a fresh scene without polling', async () => { let polls = 0; - const h = harness({ getSnapshot: async () => { - polls++; - return [reading('Toronto', 7)]; - } }); + const h = harness({ + getSnapshot: async () => { + polls++; + return [reading('Toronto', 7)]; + }, + }); try { h.layer.detachRenderer(); assert.equal(h.sources.length, 0); diff --git a/src/layers/airQuality/render/navara.js b/src/layers/airQuality/render/navara.js index 3518c0db9..6d598a5b1 100644 --- a/src/layers/airQuality/render/navara.js +++ b/src/layers/airQuality/render/navara.js @@ -35,8 +35,17 @@ function stationFeature(reading, radius) { /** One native GeoJSON layer per AQHI colour, independent of station count. */ export function createAirQualityNavaraRenderer(engine, { view }) { const display = createNavaraPolygonDisplay(engine, view, (color, show) => ({ - polygon: {color, show, transparent: true, opacity: 0.35, clampToGround: true, - outline: true, outlineShow: true, outlineColor: color, outlineWidth: 2}, + polygon: { + color, + show, + transparent: true, + opacity: 0.35, + clampToGround: true, + outline: true, + outlineShow: true, + outlineColor: color, + outlineWidth: 2, + }, })); return { viewRectangle: () => engine.camera.viewRectangle(), diff --git a/src/layers/alpr/index.js b/src/layers/alpr/index.js index 127ae48a4..fd6350c12 100644 --- a/src/layers/alpr/index.js +++ b/src/layers/alpr/index.js @@ -232,7 +232,13 @@ export function createAlprCamerasLayer({ // Stale and saturated are independent facts; a cached response that also // hit the cap must still warn about coverage, not just about freshness. if (!restoringView) renderer?.renderRecords(); - setAlprStatus((renderer?.count() ?? records.length) ? (stale ? 'stale' : 'ready') : 'empty'); + setAlprStatus( + (renderer?.count() ?? records.length) + ? stale + ? 'stale' + : 'ready' + : 'empty', + ); } catch (error) { if ( error?.name === 'AbortError' || diff --git a/src/layers/awareness/focus.js b/src/layers/awareness/focus.js index 0dc189f6f..8553b0e83 100644 --- a/src/layers/awareness/focus.js +++ b/src/layers/awareness/focus.js @@ -45,7 +45,11 @@ export function createFocus({ state: layerState, services, parts, source }) { ) return true; announceNavigationAuthority('context-vessel-focus'); - layerState.display.frameContact(vessel.position, VESSEL_FOCUS_RADIUS_M, 1.4); + layerState.display.frameContact( + vessel.position, + VESSEL_FOCUS_RADIUS_M, + 1.4, + ); return true; } @@ -177,7 +181,11 @@ export function createFocus({ state: layerState, services, parts, source }) { announceNavigationAuthority('context-vessel-autofocus', { cancelPendingSelection: false, }); - layerState.display.frameContact(vessel.position, VESSEL_FOCUS_RADIUS_M, 1.6); + layerState.display.frameContact( + vessel.position, + VESSEL_FOCUS_RADIUS_M, + 1.6, + ); return true; } return { diff --git a/src/layers/awareness/lifecycle.js b/src/layers/awareness/lifecycle.js index 6cf31e89a..8158be483 100644 --- a/src/layers/awareness/lifecycle.js +++ b/src/layers/awareness/lifecycle.js @@ -51,43 +51,42 @@ export function createLifecycle({ 'gev:awareness-subject-cleared', layerState.subjectClearListener, ); - layerState.preRenderRemover = - layerState.display.onFrame(() => { - if (!layerState.enabled) return; - const now = Date.now(); - // "Did the camera move" uses the SAME quantized pose signature the fleet - // rotation pass gates on (iconOrientation.cameraPoseSignature) rather than - // camera.changed, whose granularity is globally degraded by other layers - // mutating camera.percentageChanged. - const poseSig = cameraMotionSignature(layerState.display.camera); - if (poseSig !== layerState.lastCameraPoseSig) { - layerState.lastCameraPoseSig = poseSig; - layerState.lastCameraPoseChangeMs = now; - } - const decision = parts.model.awarenessRefreshDecision({ - nowMs: now, - lastRefreshMs: layerState.lastSubjectRefreshMs, - lastPoseChangeMs: layerState.lastCameraPoseChangeMs, - wasMoving: layerState.cameraMoving, - }); - layerState.cameraMoving = decision.moving; - syncAwarenessRenderHold(); - if (!decision.refresh) return; - layerState.lastSubjectRefreshMs = now; - if ( - layerState.subject && - ['flights', 'military', 'ais-live-vessels'].includes( - layerState.subject.layerId, - ) - ) { - parts.subject.refreshSelectedSubject(); - } else if (!layerState.passive && layerState.autoFocusRetryPending) { - layerState.autoFocusRetryPending = false; - parts.focus.focusAttentionTarget(); - } else { - parts.rendering.scheduleDirectionOverlayUpdate(); - } + layerState.preRenderRemover = layerState.display.onFrame(() => { + if (!layerState.enabled) return; + const now = Date.now(); + // "Did the camera move" uses the SAME quantized pose signature the fleet + // rotation pass gates on (iconOrientation.cameraPoseSignature) rather than + // camera.changed, whose granularity is globally degraded by other layers + // mutating camera.percentageChanged. + const poseSig = cameraMotionSignature(layerState.display.camera); + if (poseSig !== layerState.lastCameraPoseSig) { + layerState.lastCameraPoseSig = poseSig; + layerState.lastCameraPoseChangeMs = now; + } + const decision = parts.model.awarenessRefreshDecision({ + nowMs: now, + lastRefreshMs: layerState.lastSubjectRefreshMs, + lastPoseChangeMs: layerState.lastCameraPoseChangeMs, + wasMoving: layerState.cameraMoving, }); + layerState.cameraMoving = decision.moving; + syncAwarenessRenderHold(); + if (!decision.refresh) return; + layerState.lastSubjectRefreshMs = now; + if ( + layerState.subject && + ['flights', 'military', 'ais-live-vessels'].includes( + layerState.subject.layerId, + ) + ) { + parts.subject.refreshSelectedSubject(); + } else if (!layerState.passive && layerState.autoFocusRetryPending) { + layerState.autoFocusRetryPending = false; + parts.focus.focusAttentionTarget(); + } else { + parts.rendering.scheduleDirectionOverlayUpdate(); + } + }); layerState.runtimeListenersAttached = true; } @@ -184,10 +183,11 @@ export function createLifecycle({ layerState.display?.destroy?.(); layerState.display = viewer ? createAwarenessCesiumRenderer(viewer) - : services.renderer?.(engine) ?? null; + : (services.renderer?.(engine) ?? null); if (layerState.enabled) { attachRuntimeListeners(); - if (layerState.subject) parts.rendering.renderVisual(layerState.subject); + if (layerState.subject) + parts.rendering.renderVisual(layerState.subject); parts.rendering.scheduleDirectionOverlayUpdate(true); } }, diff --git a/src/layers/bikeshare/index.js b/src/layers/bikeshare/index.js index 94bee7237..34b6e8d06 100644 --- a/src/layers/bikeshare/index.js +++ b/src/layers/bikeshare/index.js @@ -75,7 +75,8 @@ export function createBikeshareLayer({ services, source, rendererFactory }) { state._viewer = state._engine = null; renderer = null; initialized = false; - for (const key of ['rendering', 'selection', 'viewport', 'lifecycle']) delete parts[key]; + for (const key of ['rendering', 'selection', 'viewport', 'lifecycle']) + delete parts[key]; }, enable(...args) { return renderer?.lifecycle.methods.enable(...args); diff --git a/src/layers/bikeshare/render/cesium/lifecycle.js b/src/layers/bikeshare/render/cesium/lifecycle.js index 2571dbe8c..723d1db1d 100644 --- a/src/layers/bikeshare/render/cesium/lifecycle.js +++ b/src/layers/bikeshare/render/cesium/lifecycle.js @@ -137,7 +137,9 @@ export function createLifecycle({ document.removeEventListener('keydown', parts.selection._onKeyDown); unregisterPickOwner('bikeshare'); if (layerState._cameraChangedAttached) { - viewer.camera.changed.removeEventListener(parts.viewport.onCameraChanged); + viewer.camera.changed.removeEventListener( + parts.viewport.onCameraChanged, + ); releaseCameraSensitivity(viewer.camera, 'bikeshare'); layerState._cameraChangedAttached = false; } diff --git a/src/layers/cctv/lifecycle.js b/src/layers/cctv/lifecycle.js index 5b377f1de..2c47dc762 100644 --- a/src/layers/cctv/lifecycle.js +++ b/src/layers/cctv/lifecycle.js @@ -133,8 +133,7 @@ export function createLifecycle({ // swaps (bing→osm) no-op. layerState._lastAppliedRegime = parts.ground.currentSurfaceRegime(); if (!layerState._mapStackListener && typeof window !== 'undefined') { - layerState._mapStackListener = () => - parts.ground.handleMapStackChanged(); + layerState._mapStackListener = () => parts.ground.handleMapStackChanged(); window.addEventListener( 'gev:map-stack-changed', layerState._mapStackListener, @@ -222,7 +221,6 @@ export function createLifecycle({ parts.hover.handleHoverMove(movement?.endPosition), }, ); - } function createRenderer(viewer, options) { if (parts.renderer) throw new Error('CCTV layer is already initialized'); @@ -431,13 +429,17 @@ export function createLifecycle({ layerState._restoringViewport = Boolean(options.waitForCameraRestore); renderer.createMarkers(); for (const record of layerState._records) { - record.billboard = renderer.createMarker(record.camera, record.position); + record.billboard = renderer.createMarker( + record.camera, + record.position, + ); record.groundResolved = {}; record.groundSamples = {}; record.probeClampRangeM = null; } bindScene(viewer, options); - if (!layerState._restoringViewport && layerState._enabled) this.resumeDisplay(); + if (!layerState._restoringViewport && layerState._enabled) + this.resumeDisplay(); }, resumeViewport() { @@ -462,7 +464,10 @@ export function createLifecycle({ parts.renderer.hideCredit(); unregisterPickOwner('cctv'); if (layerState._mapStackListener) { - window.removeEventListener('gev:map-stack-changed', layerState._mapStackListener); + window.removeEventListener( + 'gev:map-stack-changed', + layerState._mapStackListener, + ); layerState._mapStackListener = null; } layerState._viewResizeObserver?.disconnect(); @@ -470,7 +475,10 @@ export function createLifecycle({ layerState._horizonCullListener?.(); layerState._moveStartListener?.(); layerState._removeFocusAppearListener?.(); - layerState._horizonCullListener = layerState._moveStartListener = layerState._removeFocusAppearListener = null; + layerState._horizonCullListener = + layerState._moveStartListener = + layerState._removeFocusAppearListener = + null; layerState._gizmo?.destroy(); layerState._gizmo = null; releaseContinuousRender('cctv-adjust'); @@ -483,7 +491,7 @@ export function createLifecycle({ parts.hover.clearHoverCard(); parts.projection.clearProjectionOverlay(); layerState._cctvOverlayHost.clearSource(CCTV_OVERLAY_SOURCE_ID); - parts.geometry.destroyCoverageEntities({retainMedia:true}); + parts.geometry.destroyCoverageEntities({ retainMedia: true }); for (const record of layerState._records) { record.billboard = null; record.coverageEntities = []; @@ -536,7 +544,8 @@ export function createLifecycle({ const activeRecord = parts.selection.getActiveRecord(); if (activeRecord) { parts.projection.ensureProjectionRuntime(activeRecord); - if (refreshImage) parts.frames.refreshProjectionImage(activeRecord, true); + if (refreshImage) + parts.frames.refreshProjectionImage(activeRecord, true); } parts.rendering.refreshHorizonCulling(); // Re-enabled mid-flight: moveStart has already fired. diff --git a/src/layers/cctv/projection.js b/src/layers/cctv/projection.js index 034c5d3fb..7849904a5 100644 --- a/src/layers/cctv/projection.js +++ b/src/layers/cctv/projection.js @@ -103,7 +103,8 @@ export function createProjection({ function setPlaneVisible(runtime, visible) { if (!runtime) return; runtime.planeVisible = !!visible; - if (runtime.planeEntity) parts.renderer?.setPlaneVisible(runtime, !!visible); + if (runtime.planeEntity) + parts.renderer?.setPlaneVisible(runtime, !!visible); if (visible && runtime.overlayEntry && runtime.cameraId) { if (layerState._projectionOverlayOwnerId !== runtime.cameraId) { layerState._cctvOverlayHost.setEntries( @@ -256,7 +257,8 @@ export function createProjection({ runtime.imageLoading = false; runtime.imageReady = false; }; - if (runtime.planeMaterial) parts.renderer?.setPlaneImage(runtime, runtime.canvas); + if (runtime.planeMaterial) + parts.renderer?.setPlaneImage(runtime, runtime.canvas); parts.frames.refreshProjectionImage(record, true); parts.presentation.notifyListeners(); }, @@ -314,11 +316,21 @@ export function createProjection({ if (record.projection && !record.projection.disposed) { const runtime = record.projection; if (runtime.needsPlane && parts.renderer?.ready) { - const geometry = record.frustumGeometry || parts.geometry.computeFrustumGeometry(record.camera, parts.ground.groundAltFor(record), record.probeClampRangeM); - const positions = record.frustumPositions || parts.geometry.frustumCartesians(geometry); - runtime.planeMaterial = parts.renderer.createPlaneMaterial(runtime.canvas); + const geometry = + record.frustumGeometry || + parts.geometry.computeFrustumGeometry( + record.camera, + parts.ground.groundAltFor(record), + record.probeClampRangeM, + ); + const positions = + record.frustumPositions || parts.geometry.frustumCartesians(geometry); + runtime.planeMaterial = parts.renderer.createPlaneMaterial( + runtime.canvas, + ); createProjectionPlane(record, runtime, geometry, positions); - if (runtime.video?.videoWidth) parts.renderer.setPlaneImage(runtime, runtime.video); + if (runtime.video?.videoWidth) + parts.renderer.setPlaneImage(runtime, runtime.video); runtime.needsPlane = false; runtime.lastSwappedCanvasStamp = -1; } diff --git a/src/layers/cyclones/index.js b/src/layers/cyclones/index.js index 3b70fe113..7a1193f25 100644 --- a/src/layers/cyclones/index.js +++ b/src/layers/cyclones/index.js @@ -110,7 +110,12 @@ export function createCyclonesLayer({ function installSelection() { const canvas = engine?.host.canvas ?? viewer?.scene?.canvas; const native = engine?.kind === 'navara'; - if (clickHandler || !canvas || (!native && typeof cesium.ScreenSpaceEventHandler !== 'function')) return; + if ( + clickHandler || + !canvas || + (!native && typeof cesium.ScreenSpaceEventHandler !== 'function') + ) + return; const owner = native ? {} : new cesium.ScreenSpaceEventHandler(canvas); clickHandler = owner; let capturedHit = null; @@ -185,7 +190,8 @@ export function createCyclonesLayer({ if (id) layer.setParams({ stormId: id }); else if (isSurfacePick(picked)) layer.setParams({ clear: true }); }; - if (native) owner.destroy = engine.input.on('click', onClick, { pick: true }); + if (native) + owner.destroy = engine.input.on('click', onClick, { pick: true }); else owner.setInputAction(onClick, cesium.ScreenSpaceEventType.LEFT_CLICK); } function removeSelection() { @@ -202,14 +208,16 @@ export function createCyclonesLayer({ source: 'NOAA NHC / CPHC', updateInterval: 300_000, init(nextViewer, options = {}) { - if (nextViewer || options.engine) return layer.attachRenderer(nextViewer, options); + if (nextViewer || options.engine) + return layer.attachRenderer(nextViewer, options); }, attachRenderer(nextViewer, options = {}) { viewer = nextViewer; engine = options.engine ?? null; - rendering = engine?.kind === 'navara' - ? createNativeRendering(engine, { overlayHost }) - : createRendering({ viewer, cesium, overlayHost }); + rendering = + engine?.kind === 'navara' + ? createNativeRendering(engine, { overlayHost }) + : createRendering({ viewer, cesium, overlayHost }); if (!enabled) return; installSelection(); if (snapshot && !snapshot.unavailable) { @@ -287,8 +295,15 @@ export function createCyclonesLayer({ let display; do { display = rendering; - applied = display ? await display.setSnapshot(next, { signal: controller.signal }) : true; - } while (display !== rendering && enabled && !controller.signal.aborted && request === controller); + applied = display + ? await display.setSnapshot(next, { signal: controller.signal }) + : true; + } while ( + display !== rendering && + enabled && + !controller.signal.aborted && + request === controller + ); if ( !applied || !enabled || @@ -351,8 +366,9 @@ export function createCyclonesLayer({ selectedId !== storm.id ) return; - const fly = engine ? engine.camera.flyToSphere.bind(engine.camera) : - viewer.camera.flyToBoundingSphere.bind(viewer.camera); + const fly = engine + ? engine.camera.flyToSphere.bind(engine.camera) + : viewer.camera.flyToBoundingSphere.bind(viewer.camera); return fly(sphere, { duration: matchMedia?.('(prefers-reduced-motion: reduce)') ?.matches diff --git a/src/layers/cyclones/index.test.mjs b/src/layers/cyclones/index.test.mjs index c77fa8195..57ab94fe6 100644 --- a/src/layers/cyclones/index.test.mjs +++ b/src/layers/cyclones/index.test.mjs @@ -680,10 +680,18 @@ test('advisories delivered between scenes keep a deliberate selection without an let reads = 0; const drawn = []; const layer = createCyclonesLayer({ - feed: { getSnapshot: () => ++reads === 1 ? snapshot([storm()]) : pending.promise }, + feed: { + getSnapshot: () => + ++reads === 1 ? snapshot([storm()]) : pending.promise, + }, createRendering: () => ({ - setSnapshot(next) { drawn.push(next.storms.map(s=>s.advisoryNumber)); return true; }, - setSelection() {}, clear() {}, destroy() {}, + setSnapshot(next) { + drawn.push(next.storms.map((s) => s.advisoryNumber)); + return true; + }, + setSelection() {}, + clear() {}, + destroy() {}, }), }); layer.init({}); @@ -692,11 +700,11 @@ test('advisories delivered between scenes keep a deliberate selection without an layer.setParams({ stormId: 'ep152026' }); const refresh = layer.update(); layer.detachRenderer(); - pending.resolve(snapshot([{...storm(), advisoryNumber: '11'}])); + pending.resolve(snapshot([{ ...storm(), advisoryNumber: '11' }])); await refresh; assert.equal(layer.getDiagnostics().selectedId, 'ep152026'); await layer.attachRenderer({}); - assert.deepEqual(drawn, [['10'],['11']]); + assert.deepEqual(drawn, [['10'], ['11']]); assert.equal(reads, 2); layer.destroy(); }); diff --git a/src/layers/directions/index.js b/src/layers/directions/index.js index 537bcdafd..20bc20540 100644 --- a/src/layers/directions/index.js +++ b/src/layers/directions/index.js @@ -71,7 +71,8 @@ export const DIRECTIONS_ROUTE_COLOR = '#39d0ff'; const MARKER_A_COLOR = () => Cesium.Color.fromCssColorString('#5dff9f'); const MARKER_B_COLOR = () => Cesium.Color.fromCssColorString('#ff6b6b'); const STEP_COLOR = () => Cesium.Color.WHITE.withAlpha(0.95); -const STEP_OUTLINE = () => Cesium.Color.fromCssColorString(DIRECTIONS_ROUTE_COLOR); +const STEP_OUTLINE = () => + Cesium.Color.fromCssColorString(DIRECTIONS_ROUTE_COLOR); const STEP_PIXEL_SIZE = 8; const STEP_SELECTED_PIXEL_SIZE = 13; /** Route request timeout (ms) — the proxy itself gives OSRM 12 s. */ @@ -859,7 +860,11 @@ export function normalizeRoutePayload(payload, mode) { * @param {{services: object, source: {getRoute: Function}}} options * @returns {object} The layer module the data manager registers. */ -export function createDirectionsLayer({ services, source, createNativeRenderer }) { +export function createDirectionsLayer({ + services, + source, + createNativeRenderer, +}) { if (typeof source?.getRoute !== 'function') throw new TypeError('Directions require a route source'); /** @@ -970,10 +975,9 @@ export function createDirectionsLayer({ services, source, createNativeRenderer } } function pickGround(screenPosition) { - const cartesian = _native ? _engine.picker.pickGlobe(screenPosition) : services.scenePick.pickGroundPosition( - _viewer, - screenPosition, - ); + const cartesian = _native + ? _engine.picker.pickGlobe(screenPosition) + : services.scenePick.pickGroundPosition(_viewer, screenPosition); if (!cartesian) return null; const carto = Cesium.Cartographic.fromCartesian(cartesian); if (!carto) return null; @@ -1011,7 +1015,10 @@ export function createDirectionsLayer({ services, source, createNativeRenderer } } function removeEntity(entity) { - if (_native) { entity?.destroy(); return; } + if (_native) { + entity?.destroy(); + return; + } if (entity && _viewer && !_viewer.isDestroyed?.()) _viewer.entities.remove(entity); } @@ -1099,10 +1106,13 @@ export function createDirectionsLayer({ services, source, createNativeRenderer } function drawRoute(route) { clearRouteGraphics(); if (!_viewer && !_native) return; - if (_native) _routeEntity = _native.route(route.geometry, DIRECTIONS_ROUTE_COLOR); + if (_native) + _routeEntity = _native.route(route.geometry, DIRECTIONS_ROUTE_COLOR); else { services.annotations.ensureFlowFabricRegistered(); - const positions = Cesium.Cartesian3.fromDegreesArray(route.geometry.flat()); + const positions = Cesium.Cartesian3.fromDegreesArray( + route.geometry.flat(), + ); _routeEntity = _viewer.entities.add({ id: 'directions:route', polyline: { @@ -1563,11 +1573,18 @@ export function createDirectionsLayer({ services, source, createNativeRenderer } if (_selectedStep !== null) _clearStepSelection(); }; if (_native) { - const off = _engine.input.on('click', event => handle({ position: event.position, picked: event.picked }), { pick: true }); + const off = _engine.input.on( + 'click', + (event) => handle({ position: event.position, picked: event.picked }), + { pick: true }, + ); _clickHandler = { destroy: off }; } else { _clickHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); - _clickHandler.setInputAction(handle, Cesium.ScreenSpaceEventType.LEFT_CLICK); + _clickHandler.setInputAction( + handle, + Cesium.ScreenSpaceEventType.LEFT_CLICK, + ); } if (typeof document !== 'undefined') document.addEventListener('keydown', _onKeyDown); @@ -1768,8 +1785,12 @@ export function createDirectionsLayer({ services, source, createNativeRenderer } if (_shellSeams && _suspendedFlight && _engine) { _suspendedFlight.floorFn = _shellSeams.floorFn; _suspendedFlight.warmFn = _shellSeams.warmFn; - if (services.camera.resumeRouteFlight(_suspendedFlight, _engine)) trackFlightProgress(); - else { _flightId = 0; _flightStep = null; } + if (services.camera.resumeRouteFlight(_suspendedFlight, _engine)) + trackFlightProgress(); + else { + _flightId = 0; + _flightStep = null; + } _suspendedFlight = null; } }, diff --git a/src/layers/earthquakes/index.js b/src/layers/earthquakes/index.js index 6d3301208..88a188fbb 100644 --- a/src/layers/earthquakes/index.js +++ b/src/layers/earthquakes/index.js @@ -53,7 +53,8 @@ export function createEarthquakesLayer({ source, overlayHost, renderer } = {}) { updateInterval: 60000, init(viewer, { engine = null } = {}) { - if (initialized) throw new Error('Earthquake layer is already initialized'); + if (initialized) + throw new Error('Earthquake layer is already initialized'); initialized = true; try { if (viewer || engine) this.attachRenderer(viewer, { engine }); @@ -65,7 +66,8 @@ export function createEarthquakesLayer({ source, overlayHost, renderer } = {}) { }, attachRenderer(viewer, { engine = null } = {}) { - if (!initialized || _renderer) throw new Error('Invalid earthquake scene attachment'); + if (!initialized || _renderer) + throw new Error('Invalid earthquake scene attachment'); _renderer = renderer.create({ viewer, engine }); if (!_renderer) throw new Error('Earthquake renderer unavailable'); _renderer.setVisible(_enabled); @@ -208,9 +210,14 @@ export function createEarthquakesLayer({ source, overlayHost, renderer } = {}) { const limit = Number.isFinite(maxCount) ? Math.max(1, Math.floor(maxCount)) : 2000; - return observations.slice(0, limit).map((row, index) => - mapAnalystRecord({ ...row, id: row.usgsId, depth: row.depthKm }, index), - ); + return observations + .slice(0, limit) + .map((row, index) => + mapAnalystRecord( + { ...row, id: row.usgsId, depth: row.depthKm }, + index, + ), + ); }, getStats() { diff --git a/src/layers/earthquakes/ownership.test.mjs b/src/layers/earthquakes/ownership.test.mjs index ad3f39d45..4079f7406 100644 --- a/src/layers/earthquakes/ownership.test.mjs +++ b/src/layers/earthquakes/ownership.test.mjs @@ -94,11 +94,16 @@ test('USGS body completion honors cancellation even with an uncooperative transp }); test('an observation received between scenes is queryable and draws without a second fetch', async () => { - let resolve, polls = 0; - const h = harness({ getSnapshot() { - polls++; - return new Promise(done => { resolve = done; }); - } }); + let resolve, + polls = 0; + const h = harness({ + getSnapshot() { + polls++; + return new Promise((done) => { + resolve = done; + }); + }, + }); try { const pending = h.layer.update(); h.layer.detachRenderer(); diff --git a/src/layers/earthquakes/render/navara.js b/src/layers/earthquakes/render/navara.js index e7848a2c9..3e28ddc82 100644 --- a/src/layers/earthquakes/render/navara.js +++ b/src/layers/earthquakes/render/navara.js @@ -199,7 +199,12 @@ function buildState(discs) { function groundEffectClass(EffectDesc, NavaraEffect) { return class GroundEffectDesc extends EffectDesc { static key = KEY; - static insertAfter = ['gevObservedWeather', 'gevNepalEvent', 'gevRecentImagery', 'toneMapping']; + static insertAfter = [ + 'gevObservedWeather', + 'gevNepalEvent', + 'gevRecentImagery', + 'toneMapping', + ]; static insertBefore = [ 'gevFirmsGround', 'gevPreSharpenOverlay', diff --git a/src/layers/firms/lifecycle.js b/src/layers/firms/lifecycle.js index d25af749c..0f03961d7 100644 --- a/src/layers/firms/lifecycle.js +++ b/src/layers/firms/lifecycle.js @@ -15,7 +15,10 @@ export function createLifecycle({ init(viewer, options) { this.attachRenderer(viewer, options); }, - attachRenderer(viewer, { engine = null, waitForCameraRestore = false } = {}) { + attachRenderer( + viewer, + { engine = null, waitForCameraRestore = false } = {}, + ) { if (layerState._destroyed) return; if (layerState._renderer) return; layerState._renderer = config.renderer.create({ @@ -87,7 +90,8 @@ export function createLifecycle({ this.showRenderer(); if (!layerState._fires.length && !layerState._loading) await components.ingestion.loadHeatmap(); - if (layerState._enabled && layerState._renderer === renderer) renderer.onEnable(); + if (layerState._enabled && layerState._renderer === renderer) + renderer.onEnable(); }, disable() { diff --git a/src/layers/flights/lifecycle.js b/src/layers/flights/lifecycle.js index 4ef66df3d..56a157c65 100644 --- a/src/layers/flights/lifecycle.js +++ b/src/layers/flights/lifecycle.js @@ -15,7 +15,10 @@ export function createLifecycle({ const { registerPickOwner } = services.picking; const binding = createAircraftSceneBinding({ - id: 'flights', flightState, services, parts, + id: 'flights', + flightState, + services, + parts, resolveModel: () => resolveAsset(PLANE_MODEL_URL), enable: () => methods.enable(), }); @@ -44,7 +47,8 @@ export function createLifecycle({ * @param {{ engine?: object }} [context] - LayerLifecycle's context. */ init(viewer, { engine = null } = {}) { - if (flightState.initialized) throw new Error('Flight layer is already initialized'); + if (flightState.initialized) + throw new Error('Flight layer is already initialized'); if (typeof flightState.feed._source?.getSnapshot !== 'function') throw new TypeError('A snapshot source is required'); if (flightState.lifetime.signal.aborted) diff --git a/src/layers/flights/snapshotRenderer.js b/src/layers/flights/snapshotRenderer.js index 0c08ded31..e7cb9b186 100644 --- a/src/layers/flights/snapshotRenderer.js +++ b/src/layers/flights/snapshotRenderer.js @@ -112,8 +112,7 @@ export function createFlightSnapshotRenderer({ // subpoint + collected floor cells for the viewer-proximate low-contact // clamp below — one carto read per poll, one batch warm after the loop. const viewerCarto = - flightState._renderer?.view.camera?.positionCartographic || - null; + flightState._renderer?.view.camera?.positionCartographic || null; const viewerLatDeg = viewerCarto ? geo.math.toDegrees(viewerCarto.latitude) : null; diff --git a/src/layers/ghostMaps/lifecycle.test.mjs b/src/layers/ghostMaps/lifecycle.test.mjs index 4d5c43c21..0cc79c8d0 100644 --- a/src/layers/ghostMaps/lifecycle.test.mjs +++ b/src/layers/ghostMaps/lifecycle.test.mjs @@ -157,10 +157,17 @@ test('cancellation during parsing releases every packed-image URL', async (t) => test('a KMZ delivered between planets is reused by the new scene and releases its icon on disable', async (t) => { let layer; - const h = fixture(t, { beforeFileReturns() { layer.detachRenderer(); } }); + const h = fixture(t, { + beforeFileReturns() { + layer.detachRenderer(); + }, + }); layer = h.layer; await layer.update(); - assert.deepEqual(layer.getAnalystRecords().map(r => r.name), ['Test report']); + assert.deepEqual( + layer.getAnalystRecords().map((r) => r.name), + ['Test report'], + ); assert.equal(h.mounted.size, 0); await layer.attachRenderer(h.viewer); assert.equal(h.mounted.size, 1); diff --git a/src/layers/ghostMaps/render/navara.js b/src/layers/ghostMaps/render/navara.js index 65619c543..21c735ac8 100644 --- a/src/layers/ghostMaps/render/navara.js +++ b/src/layers/ghostMaps/render/navara.js @@ -32,13 +32,11 @@ export function createGhostMapsNavaraRenderer(engine, view) { function add(style, geom, id) { const key = JSON.stringify(style); if (!groups.has(key)) groups.set(key, { style, features: [] }); - groups - .get(key) - .features.push({ - type: 'Feature', - geometry: geom, - properties: { gevId: id }, - }); + groups.get(key).features.push({ + type: 'Feature', + geometry: geom, + properties: { gevId: id }, + }); } for (const [index, feature] of features.entries()) { const { style } = feature; @@ -51,7 +49,14 @@ export function createGhostMapsNavaraRenderer(engine, view) { }; for (const [part, geometry] of feature.geometries.entries()) { const id = `ghostmaps:${productId}:${index}:${part}`; - shapes.push({ id, geometry, style: { ...style, pointRadius: 10 * clamp((style.iconScale ?? 1) * 0.7, 0.35, 1.5) } }); + shapes.push({ + id, + geometry, + style: { + ...style, + pointRadius: 10 * clamp((style.iconScale ?? 1) * 0.7, 0.35, 1.5), + }, + }); if (geometry.type === 'point') { const color = style.iconColor && style.icon ? style.iconColor : null; diff --git a/src/layers/gnss/index.js b/src/layers/gnss/index.js index 37938ce6c..0146cc5cc 100644 --- a/src/layers/gnss/index.js +++ b/src/layers/gnss/index.js @@ -171,8 +171,12 @@ export function createGnssIntegrityLayer({ if (!initialized || renderer) throw new Error('Invalid GNSS scene attachment'); renderer = createRenderer({ viewer, engine, viewAnchor }); - _removeCameraEnd = renderer.camera?.moveEnd?.addEventListener(scheduleCameraRefresh); - _removeCameraChanged = renderer.camera?.changed?.addEventListener(scheduleCameraRefresh); + _removeCameraEnd = renderer.camera?.moveEnd?.addEventListener( + scheduleCameraRefresh, + ); + _removeCameraChanged = renderer.camera?.changed?.addEventListener( + scheduleCameraRefresh, + ); _signature = null; if (_enabled) expire(); renderer.setVisible(_enabled); diff --git a/src/layers/gnss/ownership.test.mjs b/src/layers/gnss/ownership.test.mjs index 0ec9abf7c..97c0c3369 100644 --- a/src/layers/gnss/ownership.test.mjs +++ b/src/layers/gnss/ownership.test.mjs @@ -700,8 +700,14 @@ test('view anchor falls back to rectangle, then nadir, then no camera', () => { }); test('an in-flight regional response supplies the next renderer without a new request', async () => { - let resolve, calls = 0; - const h = harness(() => { calls++; return new Promise(done => { resolve = done; }); }); + let resolve, + calls = 0; + const h = harness(() => { + calls++; + return new Promise((done) => { + resolve = done; + }); + }); try { h.layer.enable(); const pending = h.layer.update(); diff --git a/src/layers/installations/index.js b/src/layers/installations/index.js index 5f7c8ba24..a21e3111b 100644 --- a/src/layers/installations/index.js +++ b/src/layers/installations/index.js @@ -28,7 +28,8 @@ export function createInstallationsLayer({ let renderer = null; const lifecycle = { init(viewer, options = {}) { - if (viewer || options.engine) return lifecycle.attachRenderer(viewer, options); + if (viewer || options.engine) + return lifecycle.attachRenderer(viewer, options); }, attachRenderer(viewer, options = {}) { if (renderer) @@ -41,7 +42,9 @@ export function createInstallationsLayer({ const ready = renderer.lifecycle.methods.init(viewer, options); if (state.enabled) { renderer.lifecycle.methods.enable(); - renderer.rendering.renderRecords({ claimSelection: Boolean(state.selectedId) }); + renderer.rendering.renderRecords({ + claimSelection: Boolean(state.selectedId), + }); } return ready; }, @@ -63,7 +66,9 @@ export function createInstallationsLayer({ state.coverage = { kind: 'viewport' }; clearTimeout(state.timer); parts.viewport.clearUnavailableRetry(); - services.context.clearSelectedEntityContextForLayer('military-installations'); + services.context.clearSelectedEntityContextForLayer( + 'military-installations', + ); parts.ingestion.setInstallationStatus('idle'); }, detachRenderer(viewer) { diff --git a/src/layers/installations/render/cesium/rendering.js b/src/layers/installations/render/cesium/rendering.js index 7f391a563..7d8528d1d 100644 --- a/src/layers/installations/render/cesium/rendering.js +++ b/src/layers/installations/render/cesium/rendering.js @@ -355,7 +355,8 @@ export function createRendering({ if (!cold.length) return; const owner = layerState.dataSource; warmFireAnchorFloors(cold).then(() => { - if (!layerState.enabled || !owner || layerState.dataSource !== owner) return; + if (!layerState.enabled || !owner || layerState.dataSource !== owner) + return; if ( !cold.some((point) => cachedGroundFloor(point.lat, point.lon) != null) ) diff --git a/src/layers/installations/viewport.js b/src/layers/installations/viewport.js index 15277c3f2..c99f1497d 100644 --- a/src/layers/installations/viewport.js +++ b/src/layers/installations/viewport.js @@ -21,7 +21,10 @@ export function createViewport({ state: layerState, services, parts, source }) { } if (viewer || layerState.engine) layerState.lastViewBox = viewportBox(viewer); - return { box: layerState.lastViewBox ?? null, coverage: { kind: 'viewport' } }; + return { + box: layerState.lastViewBox ?? null, + coverage: { kind: 'viewport' }, + }; } function viewportBox(viewer) { diff --git a/src/layers/launches/rendering.js b/src/layers/launches/rendering.js index c826e043a..b3266db12 100644 --- a/src/layers/launches/rendering.js +++ b/src/layers/launches/rendering.js @@ -100,7 +100,7 @@ export function createRendering({ : satelliteTrack?.orbitPath?.length > 1 ? satelliteTrack.orbitPath : parts.paths.approximateOrbitPath(launch); - const solution = {activeTleText, satelliteTrack, orbitPath}; + const solution = { activeTleText, satelliteTrack, orbitPath }; orbitSolutions.set(launch, solution); return solution; } @@ -117,7 +117,11 @@ export function createRendering({ const orbitAllowed = parts.policyHelpers.launchStatusAllowsOrbit( launch.status, ); - const { satelliteTrack, orbitPath } = orbitForLaunch(launch, activeTleText, orbitAllowed); + const { satelliteTrack, orbitPath } = orbitForLaunch( + launch, + activeTleText, + orbitAllowed, + ); launch.recoveryStages.forEach((stage) => { stage.endpoint = parts.paths.landingEndpoint( stage, diff --git a/src/layers/military/lifecycle.js b/src/layers/military/lifecycle.js index dbc239490..c5a312ff2 100644 --- a/src/layers/military/lifecycle.js +++ b/src/layers/military/lifecycle.js @@ -15,7 +15,10 @@ export function createLifecycle({ const { setMilitaryLayerActive } = services.militaryRegistry; const binding = createAircraftSceneBinding({ - id: 'military', flightState, services, parts, + id: 'military', + flightState, + services, + parts, resolveModel: () => resolveAsset(JET_MODEL_URL), enable: () => methods.enable(), }); diff --git a/src/layers/military/queries.js b/src/layers/military/queries.js index de175718f..69bab83d2 100644 --- a/src/layers/military/queries.js +++ b/src/layers/military/queries.js @@ -383,7 +383,8 @@ export function createQueries({ const trackedPos = isTracked ? parts.motion._trackedDisplayCached() : null; // cached, no recompute (anti-jitter) - const pos = trackedPos || bb?.position || parts.motion._deadReckon(icao24); + const pos = + trackedPos || bb?.position || parts.motion._deadReckon(icao24); if (!pos) continue; const distance = geo.cartesian3.distance(center, pos); diff --git a/src/layers/military/renderer.test.mjs b/src/layers/military/renderer.test.mjs index bf2824b96..eedf33f3a 100644 --- a/src/layers/military/renderer.test.mjs +++ b/src/layers/military/renderer.test.mjs @@ -294,12 +294,20 @@ test('the military logic draws and follows on the Navara renderer; the follow pa }); test('military classification and the selected aircraft survive an update between scenes', async () => { - const first = createFakeEngine(), second = createFakeEngine(); - let polls = 0, binding; + const first = createFakeEngine(), + second = createFakeEngine(); + let polls = 0, + binding; const layer = createApplicationMilitary({ - surface: defaultSurface, militaryRegistry, - source: { getSnapshot: async () => militarySnapshotOf(['ae0001'], 1700000000000 + (++polls)*1000) }, - renderer: {create: ({engine}) => (binding = recordingAircraftRenderer(engine))}, + surface: defaultSurface, + militaryRegistry, + source: { + getSnapshot: async () => + militarySnapshotOf(['ae0001'], 1700000000000 + ++polls * 1000), + }, + renderer: { + create: ({ engine }) => (binding = recordingAircraftRenderer(engine)), + }, }); try { layer.init(null); @@ -314,7 +322,7 @@ test('military classification and the selected aircraft survive an update betwee assert.equal(militaryRegistry.isMilitaryLayerActive(), true); assert.equal(layer.getTrackedInfo().icao24, 'ae0001'); await layer.update(null); - layer.attachRenderer(null, {engine: second}); + layer.attachRenderer(null, { engine: second }); assert.ok(binding.follow.current()); assert.equal(layer.getStats().lastUpdate, 1700000002000); assert.equal(polls, 2); @@ -323,6 +331,7 @@ test('military classification and the selected aircraft survive an update betwee assert.equal(layer.hasContact('ae0001'), null); } finally { layer.destroy(null); - first.destroy(); second.destroy(); + first.destroy(); + second.destroy(); } }); diff --git a/src/layers/military/snapshotRenderer.js b/src/layers/military/snapshotRenderer.js index ff06290d1..662eac375 100644 --- a/src/layers/military/snapshotRenderer.js +++ b/src/layers/military/snapshotRenderer.js @@ -93,7 +93,11 @@ export function createMilitarySnapshotRenderer({ function presentRecords() { for (const [id, meta] of records.data) { - const position = geo.cartesian3.fromDegrees(meta.rawLon, meta.rawLat, meta.renderAltitudeM); + const position = geo.cartesian3.fromDegrees( + meta.rawLon, + meta.rawLat, + meta.renderAltitudeM, + ); presentContact(id, meta, position, null, false); } } @@ -195,7 +199,8 @@ export function createMilitarySnapshotRenderer({ } } - if (flightState._renderer) presentContact(icao24, meta, position, prevMeta, groundFlipped); + if (flightState._renderer) + presentContact(icao24, meta, position, prevMeta, groundFlipped); // Takeoff while TRACKED: ground traffic drew no trail, so start one // from the fresh airborne history (touchdown needs no action — the // append gate above simply freezes the existing trail). diff --git a/src/layers/military/tracking.js b/src/layers/military/tracking.js index 7608ec61d..77cab5257 100644 --- a/src/layers/military/tracking.js +++ b/src/layers/military/tracking.js @@ -840,7 +840,10 @@ export function createTracking({ * @param {string} icao24 - ICAO hex identifier of the aircraft to track */ - function _trackFlight(icao24, { origin = 'programmatic', resume = false, follow = true } = {}) { + function _trackFlight( + icao24, + { origin = 'programmatic', resume = false, follow = true } = {}, + ) { if (!resume) _clearTracking(false, { origin }); // switching planes — the new follow-camera takes over const bb = flightState._billboards.get(icao24); diff --git a/src/layers/perimeters/index.js b/src/layers/perimeters/index.js index 52dd57e91..40677feb0 100644 --- a/src/layers/perimeters/index.js +++ b/src/layers/perimeters/index.js @@ -31,10 +31,20 @@ const INCIWEB_INDEX_TTL_MS = 6 * 3600000; function samePolygons(left, right) { if (left === right) return true; - return left?.length === right.length && right.every((rings, i) => - left[i].length === rings.length && rings.every((ring, j) => - left[i][j].length === ring.length && ring.every(([x, y], k) => - left[i][j][k][0] === x && left[i][j][k][1] === y))); + return ( + left?.length === right.length && + right.every( + (rings, i) => + left[i].length === rings.length && + rings.every( + (ring, j) => + left[i][j].length === ring.length && + ring.every( + ([x, y], k) => left[i][j][k][0] === x && left[i][j][k][1] === y, + ), + ), + ) + ); } /** Own one fire-perimeter display, its refresh lifecycle, and click selection. */ @@ -184,7 +194,10 @@ export function createFirePerimetersLayer({ const pickId = picking.resolvePickId(picked); if (typeof pickId !== 'string' || !pickId.startsWith(PICK_PREFIX)) return null; - const incidentId = pickId.slice(PICK_PREFIX.length, pickId.lastIndexOf(':')); + const incidentId = pickId.slice( + PICK_PREFIX.length, + pickId.lastIndexOf(':'), + ); return _rowById.has(incidentId) ? incidentId : null; } @@ -256,7 +269,8 @@ export function createFirePerimetersLayer({ updateInterval: 300000, init(viewer, { engine = null } = {}) { - if (initialized) throw new Error('Fire perimeter layer is already initialized'); + if (initialized) + throw new Error('Fire perimeter layer is already initialized'); initialized = true; try { if (viewer || engine) this.attachRenderer(viewer, { engine }); @@ -267,8 +281,13 @@ export function createFirePerimetersLayer({ }, attachRenderer(viewer, { engine = null } = {}) { - if (!initialized || renderer) throw new Error('Invalid fire perimeter scene attachment'); - renderer = createRenderer({ viewer, engine, screenSpaceEventHandlerFactory }); + if (!initialized || renderer) + throw new Error('Invalid fire perimeter scene attachment'); + renderer = createRenderer({ + viewer, + engine, + screenSpaceEventHandlerFactory, + }); renderer.setVisible(_enabled); if (geometry.size) renderer.replace([...geometry.values()]); if (_enabled) { @@ -333,7 +352,9 @@ export function createFirePerimetersLayer({ ); if ( signature === _snapshotSignature && - rows.every(row => samePolygons(geometry.get(row.stableId)?.polygons, row.polygons)) + rows.every((row) => + samePolygons(geometry.get(row.stableId)?.polygons, row.polygons), + ) ) { if (_selectedId) publishSelectedCard(); _lastUpdate = updatedAt; diff --git a/src/layers/perimeters/ownership.test.mjs b/src/layers/perimeters/ownership.test.mjs index 100ff4729..b855dcf8f 100644 --- a/src/layers/perimeters/ownership.test.mjs +++ b/src/layers/perimeters/ownership.test.mjs @@ -590,22 +590,38 @@ test('a selected fire retains its card and changed polygon through a scene hando const fire = { ...row, stableId: 'incident:one' }; let geometry = fire.polygons; let polls = 0; - const h = harness({getSnapshot: async () => { polls++; return [{...fire, polygons: geometry}]; }}, { - pick: () => ({id: 'fire-perimeter:incident:one:0'}), - }); + const h = harness( + { + getSnapshot: async () => { + polls++; + return [{ ...fire, polygons: geometry }]; + }, + }, + { + pick: () => ({ id: 'fire-perimeter:incident:one:0' }), + }, + ); try { await h.layer.update(); - h.clicks.handler({position: {x: 0, y: 0}}); - assert.equal(h.overlay.entries.get('fire-perimeters')[0].id, 'fire-perimeter-card:incident:one'); + h.clicks.handler({ position: { x: 0, y: 0 } }); + assert.equal( + h.overlay.entries.get('fire-perimeters')[0].id, + 'fire-perimeter-card:incident:one', + ); h.layer.detachRenderer(); assert.equal(h.sources.length, 0); - geometry = [[ring.map(([lon, lat]) => [lon+0.1,lat])]]; + geometry = [[ring.map(([lon, lat]) => [lon + 0.1, lat])]]; assert.equal(await h.layer.update(), true); const record = h.layer.getAnalystRecords()[0]; assert.equal(record.id, 'incident:one'); h.layer.attachRenderer(h.viewer); assert.equal(h.sources[0].entities.values.length, 1); - assert.equal(h.overlay.entries.get('fire-perimeters')[0].id, 'fire-perimeter-card:incident:one'); + assert.equal( + h.overlay.entries.get('fire-perimeters')[0].id, + 'fire-perimeter-card:incident:one', + ); assert.equal(polls, 2); - } finally { h.layer.destroy(); } + } finally { + h.layer.destroy(); + } }); diff --git a/src/layers/radio/rendering.js b/src/layers/radio/rendering.js index e5b06b091..f7e733d93 100644 --- a/src/layers/radio/rendering.js +++ b/src/layers/radio/rendering.js @@ -46,7 +46,10 @@ export function createRendering({ function publishRadioOverlayEntries() { layerState._overlayPublishTimer = null; - if (!parts.renderer?.ready || !parts.interaction.radioPresentationAllowed()) { + if ( + !parts.renderer?.ready || + !parts.interaction.radioPresentationAllowed() + ) { parts.clustering.resetRadioClusterOverlayIdentities(); layerState._overlayDiagnostics = parts.clustering.emptyRadioOverlayDiagnostics(); diff --git a/src/layers/recentImagery/index.js b/src/layers/recentImagery/index.js index 3e7949588..f4d28b368 100644 --- a/src/layers/recentImagery/index.js +++ b/src/layers/recentImagery/index.js @@ -830,7 +830,7 @@ export function createRecentImageryLayer({ forgetLease(); renderer?.destroy(); renderer = null; - _hostInfo = {collection: null, kind: 'none'}; + _hostInfo = { collection: null, kind: 'none' }; _viewer = _engine = _tileset = null; _keptGeneration = null; await Promise.all([lease?.release(), _releasing]); diff --git a/src/layers/satellites/controls.js b/src/layers/satellites/controls.js index d4c906969..02a7687b2 100644 --- a/src/layers/satellites/controls.js +++ b/src/layers/satellites/controls.js @@ -266,7 +266,8 @@ export function createControls({ state: layerState, services, parts, source }) { !layerState._points.has(id) ) return false; - if (layerState._restoringViewport && layerState._trackedNorad === id) return true; + if (layerState._restoringViewport && layerState._trackedNorad === id) + return true; parts.tracking._cancelPendingTrackingRestore(); parts.tracking._trackSatellite(id, { origin }); return layerState._trackedNorad === id; diff --git a/src/layers/satellites/lifecycle.js b/src/layers/satellites/lifecycle.js index ed84eaf64..64242501b 100644 --- a/src/layers/satellites/lifecycle.js +++ b/src/layers/satellites/lifecycle.js @@ -73,22 +73,47 @@ export function createLifecycle({ detachRenderer() { const renderer = layerState._renderer; if (!renderer) return; - layerState._waitForRenderer ??= new Promise(resolve => { layerState._resumeRenderer = resolve; }); + layerState._waitForRenderer ??= new Promise((resolve) => { + layerState._resumeRenderer = resolve; + }); layerState._clickHandler?.destroy(); layerState._trackedEntityChangedRemove?.(); layerState._preRenderListener?.(); - layerState._clickHandler = layerState._trackedEntityChangedRemove = layerState._preRenderListener = null; + layerState._clickHandler = + layerState._trackedEntityChangedRemove = + layerState._preRenderListener = + null; document.removeEventListener('keydown', parts.interaction._onKeyDown); unregisterPickOwner('satellites'); if (layerState._trackedEntity) { - layerState._handoffOrigin = layerState._trackedEntity.gevSelectionOrigin; + layerState._handoffOrigin = + layerState._trackedEntity.gevSelectionOrigin; renderer.follow.release(); renderer.removeTracked(layerState._trackedEntity); layerState._trackedEntity = null; } for (const [id, point] of layerState._points) { - const {position, show, pixelSize, color, outlineColor, outlineWidth, scaleByDistance, disableDepthTestDistance} = point; - layerState._points.set(id, {id, position: {...position}, show, pixelSize, color, outlineColor, outlineWidth, scaleByDistance, disableDepthTestDistance}); + const { + position, + show, + pixelSize, + color, + outlineColor, + outlineWidth, + scaleByDistance, + disableDepthTestDistance, + } = point; + layerState._points.set(id, { + id, + position: { ...position }, + show, + pixelSize, + color, + outlineColor, + outlineWidth, + scaleByDistance, + disableDepthTestDistance, + }); } for (const path of layerState._orbitPaths.values()) { renderer.removeOrbit(path.primitive); @@ -97,23 +122,36 @@ export function createLifecycle({ layerState._detectionObjects.clear(); renderer.removePoints(layerState._pointCollection); renderer.destroy(); - layerState._engine = layerState._viewer = layerState._renderer = layerState._pointCollection = null; + layerState._engine = + layerState._viewer = + layerState._renderer = + layerState._pointCollection = + null; layerState._overlayHost.clearSource(ISS_OVERLAY_SOURCE_ID); if (layerState._enabled) releaseContinuousRender('satellites'); }, - attachRenderer(viewer, {engine = null, waitForCameraRestore = false} = {}) { + attachRenderer( + viewer, + { engine = null, waitForCameraRestore = false } = {}, + ) { layerState._engine = engine; - const renderer = services.renderer.create({viewer,engine}); + const renderer = services.renderer.create({ viewer, engine }); layerState._renderer = renderer; layerState._viewer = renderer.view; layerState._pointCollection = renderer.createPoints(); for (const [id, spec] of layerState._points) layerState._points.set(id, layerState._pointCollection.add(spec)); - layerState._pointCollection.show = layerState._enabled && layerState._params.showPoints; + layerState._pointCollection.show = + layerState._enabled && layerState._params.showPoints; for (const [id, path] of layerState._orbitPaths) { - path.primitive = renderer.createOrbit({ positions: path.basePositions, width: id === ISS_NORAD ? 2.5 : 2, color: path.color }); - path.primitive.show = layerState._enabled && layerState._params.showOrbits; + path.primitive = renderer.createOrbit({ + positions: path.basePositions, + width: id === ISS_NORAD ? 2.5 : 2, + color: path.color, + }); + path.primitive.show = + layerState._enabled && layerState._params.showOrbits; } // Source work can publish as soon as GPU collections exist. Restoring // share-selected camera state may itself wait for that publication. @@ -129,7 +167,10 @@ export function createLifecycle({ layerState._restoringViewport = false; if (layerState._enabled) { if (layerState._trackedNorad != null && !layerState._trackedEntity) - parts.tracking._trackSatellite(layerState._trackedNorad, {origin: layerState._handoffOrigin, resuming: true}); + parts.tracking._trackSatellite(layerState._trackedNorad, { + origin: layerState._handoffOrigin, + resuming: true, + }); this.enable(layerState._viewer); } layerState._resumeRenderer?.(); diff --git a/src/layers/satellites/tracking.js b/src/layers/satellites/tracking.js index 78630c2e5..65face939 100644 --- a/src/layers/satellites/tracking.js +++ b/src/layers/satellites/tracking.js @@ -412,7 +412,10 @@ export function createTracking({ refreshTrackedReadout(layerState._trackedEntity); } - function _trackSatellite(noradId, { origin = 'programmatic', resuming = false } = {}) { + function _trackSatellite( + noradId, + { origin = 'programmatic', resuming = false } = {}, + ) { if (!resuming) _clearTracking(false, { origin }); const point = layerState._points.get(noradId); diff --git a/src/layers/shared/render/navaraGroundCells.js b/src/layers/shared/render/navaraGroundCells.js index c2a28b0bb..005789456 100644 --- a/src/layers/shared/render/navaraGroundCells.js +++ b/src/layers/shared/render/navaraGroundCells.js @@ -2,15 +2,8 @@ import { Effect, EffectAttribute } from 'postprocessing'; import { Matrix4, Uniform } from 'three'; import { rectangleFrame } from './navaraCellGeometry.js'; -import { - viewFrame, - groundClipBounds, -} from './navaraGroundGeometry.js'; -import { - createGroundGrid, - GRID_COLS, - GRID_ROWS, -} from './navaraGroundGrid.js'; +import { viewFrame, groundClipBounds } from './navaraGroundGeometry.js'; +import { createGroundGrid, GRID_COLS, GRID_ROWS } from './navaraGroundGrid.js'; import { createGroundTexture, createGroundStorage, @@ -269,26 +262,39 @@ function groundEffectClass(EffectDesc, NavaraEffect) { // The SDK allows one active effect per key. Compose enabled datasets in // catalog order so both can be drawn without replacing one another. const views = new WeakMap(); -export function createGroundCellDisplay(engine, { view, EffectDesc, NavaraEffect }, order) { +export function createGroundCellDisplay( + engine, + { view, EffectDesc, NavaraEffect }, + order, +) { if (!registeredViews.has(view)) { view.registerEffect(KEY, groundEffectClass(EffectDesc, NavaraEffect)); registeredViews.add(view); } let pass = views.get(view); if (!pass) { - pass = { inputs: new Set(), owner: { state: buildState([]) }, handle: null }; + pass = { + inputs: new Set(), + owner: { state: buildState([]) }, + handle: null, + }; views.set(view, pass); } const input = { cells: [], visible: false, order }; pass.inputs.add(input); function sync() { - const cells = [...pass.inputs].filter(item => item.visible) - .sort((a,b) => a.order - b.order).flatMap(item => item.cells); + const cells = [...pass.inputs] + .filter((item) => item.visible) + .sort((a, b) => a.order - b.order) + .flatMap((item) => item.cells); const previous = pass.owner.state; pass.owner.state = buildState(cells); try { if (!pass.handle && cells.length) { - pass.handle = view.addEffect({ [KEY]: { owner: pass.owner }, visible: true }); + pass.handle = view.addEffect({ + [KEY]: { owner: pass.owner }, + visible: true, + }); } else { pass.handle?.update({ visible: cells.length > 0 }); } diff --git a/src/layers/streetLevel/cameraFraming.js b/src/layers/streetLevel/cameraFraming.js index 572eea5e3..642ecd108 100644 --- a/src/layers/streetLevel/cameraFraming.js +++ b/src/layers/streetLevel/cameraFraming.js @@ -30,7 +30,10 @@ export function createCameraFraming({ state }) { const scene = state.viewer?.scene; const carto = Cesium.Cartographic.fromDegrees(lon, lat); const engine = state.viewer?.engine; - let height = engine ? engine.surface.sampleHeight(carto) ?? engine.surface.terrainHeight(carto) : sampledSurfaceHeight(scene, carto); + let height = engine + ? (engine.surface.sampleHeight(carto) ?? + engine.surface.terrainHeight(carto)) + : sampledSurfaceHeight(scene, carto); if (height === null) height = scene?.globe?.getHeight?.(carto); if (!plausibleSurfaceHeight(height)) height = fallback; return plausibleSurfaceHeight(height) ? height : 0; @@ -55,10 +58,24 @@ export function createCameraFraming({ state }) { if (flight === current) flight = null; }; if (state.viewer.engine) { - state.viewer.engine.camera.flyToSphere({ - center:Cesium.Cartesian3.fromDegrees(position.lon,position.lat,ground+2),radius:4 - }, {heading:Cesium.Math.toRadians(bearing || 0),pitch:Cesium.Math.toRadians(-32),range:140,duration:1.6, - complete:release,cancel:release}); + state.viewer.engine.camera.flyToSphere( + { + center: Cesium.Cartesian3.fromDegrees( + position.lon, + position.lat, + ground + 2, + ), + radius: 4, + }, + { + heading: Cesium.Math.toRadians(bearing || 0), + pitch: Cesium.Math.toRadians(-32), + range: 140, + duration: 1.6, + complete: release, + cancel: release, + }, + ); return; } state.viewer.camera.flyToBoundingSphere( diff --git a/src/layers/streetLevel/credits.js b/src/layers/streetLevel/credits.js index e8189468d..66be7dcb6 100644 --- a/src/layers/streetLevel/credits.js +++ b/src/layers/streetLevel/credits.js @@ -11,7 +11,10 @@ export function createCredits() { function show(viewer, def) { if (shown.has(def.id)) return; if (viewer?.engine) { - shown.set(def.id, viewer.engine.credits.addStatic(def.credit.html, { onScreen: true })); + shown.set( + def.id, + viewer.engine.credits.addStatic(def.credit.html, { onScreen: true }), + ); return; } if (!viewer?.creditDisplay) return; diff --git a/src/layers/streetLevel/groundClick.js b/src/layers/streetLevel/groundClick.js index 7c5e75ae2..4331d21e7 100644 --- a/src/layers/streetLevel/groundClick.js +++ b/src/layers/streetLevel/groundClick.js @@ -32,7 +32,9 @@ export function createGroundClick({ state, openNearest, isAvailable }) { ) .map((entry) => entry.def.id); if (!providerIds.length) return false; - const position = state.viewer.engine ? state.viewer.engine.picker.pickGlobe(screenPosition) : scenePick.pickGroundPosition(state.viewer, screenPosition); + const position = state.viewer.engine + ? state.viewer.engine.picker.pickGlobe(screenPosition) + : scenePick.pickGroundPosition(state.viewer, screenPosition); const carto = position && geo.cartographic.fromCartesian(position); if (!carto) return false; return openNearest( diff --git a/src/layers/streetLevel/index.js b/src/layers/streetLevel/index.js index bb69ff043..b11ed92ff 100644 --- a/src/layers/streetLevel/index.js +++ b/src/layers/streetLevel/index.js @@ -195,7 +195,8 @@ export function createStreetLevelLayer({ cancelGroundRetry(); const ready = state.enabled && groundClickHeightOk(state.viewer); if ( - state.enabled && state.viewer && + state.enabled && + state.viewer && !ready && groundRetries < GROUND_RETRY_LIMIT && groundUnderCamera(state.viewer) === null @@ -266,12 +267,15 @@ export function createStreetLevelLayer({ /** Ground under the screen centre; null for sky, undefined when it cannot aim. */ function screenCentreGround() { - const canvas = state.viewer?.engine?.host.canvas ?? state.viewer?.scene?.canvas; + const canvas = + state.viewer?.engine?.host.canvas ?? state.viewer?.scene?.canvas; const pick = state.services.scenePick?.pickGroundPosition; if (!pick || !canvas?.clientWidth || !canvas?.clientHeight) return undefined; - const center = { x:canvas.clientWidth/2, y:canvas.clientHeight/2 }; - const position = state.viewer.engine ? state.viewer.engine.picker.pickGlobe(center) : pick(state.viewer,center); + const center = { x: canvas.clientWidth / 2, y: canvas.clientHeight / 2 }; + const position = state.viewer.engine + ? state.viewer.engine.picker.pickGlobe(center) + : pick(state.viewer, center); const carto = position && geo.cartographic.fromCartesian(position); return carto ? { @@ -485,10 +489,13 @@ export function createStreetLevelLayer({ }, init(viewer, options) { - this.attachRenderer(viewer,options); + this.attachRenderer(viewer, options); for (const entry of state.providers.values()) checkStatus(entry); }, - attachRenderer(viewer, {engine = null, waitForCameraRestore = false} = {}) { + attachRenderer( + viewer, + { engine = null, waitForCameraRestore = false } = {}, + ) { const map = viewer ?? services.nativeRenderer?.(engine); state.viewer = map; waitingForView = waitForCameraRestore; @@ -497,45 +504,50 @@ export function createStreetLevelLayer({ parts.marker.setVisible(state.enabled); for (const entry of state.providers.values()) { entry.instance.init(map); - if(state.enabled && entry.on){ + if (state.enabled && entry.on) { entry.instance.activate(map); entry.instance.repaintMap?.(); - if(providerAvailable(entry)) parts.credits.show(map,entry.def); + if (providerAvailable(entry)) parts.credits.show(map, entry.def); } } if (state.enabled) { parts.selection.install(map); watchCamera(map); - parts.marker.set(state.street.position,state.street.bearing,state.providers.get(state.street.providerId)?.def.marker); + parts.marker.set( + state.street.position, + state.street.bearing, + state.providers.get(state.street.providerId)?.def.marker, + ); remeasureGround(); } }, resumeViewport() { if (!waitingForView) return; - waitingForView=false; - if(state.enabled && state.viewer) { - for(const entry of activeEntries())entry.instance.refreshCoverage?.(); + waitingForView = false; + if (state.enabled && state.viewer) { + for (const entry of activeEntries()) entry.instance.refreshCoverage?.(); remeasureGround(); } }, detachRenderer() { - const map=state.viewer; - if(!map)return; - waitingForView=true; - removeCameraListener?.();removeCameraListener=null; + const map = state.viewer; + if (!map) return; + waitingForView = true; + removeCameraListener?.(); + removeCameraListener = null; cancelGroundRetry(); parts.framing.cancel(); parts.selection.uninstall(); - for(const entry of state.providers.values()){ - if(entry.instance.detachMap)entry.instance.detachMap(); - else if(entry.on)entry.instance.deactivate(); + for (const entry of state.providers.values()) { + if (entry.instance.detachMap) entry.instance.detachMap(); + else if (entry.on) entry.instance.deactivate(); } parts.marker.destroy(map); parts.credits.hideAll(map); unsubscribeMapStack?.(); - unsubscribeMapStack=null; - mapStack=null; - state.viewer=null; + unsubscribeMapStack = null; + mapStack = null; + state.viewer = null; }, enable(viewer) { state.enabled = true; diff --git a/src/layers/streetLevel/lineCoverage.js b/src/layers/streetLevel/lineCoverage.js index c3a8b9c71..619a09219 100644 --- a/src/layers/streetLevel/lineCoverage.js +++ b/src/layers/streetLevel/lineCoverage.js @@ -246,8 +246,13 @@ export function createLineCoverage({ const sequence = entry.sequenceList.find(({ id }) => id === sequenceId); if (sequence && passes(sequence, current)) parts.push(...sequence.parts); } - if(state.viewer?.engine){ - state.coverage.highlight=state.viewer.lines([{id:sequenceId,parts}],colors.selected,tuning.lineWidthPx+1,pickPrefix); + if (state.viewer?.engine) { + state.coverage.highlight = state.viewer.lines( + [{ id: sequenceId, parts }], + colors.selected, + tuning.lineWidthPx + 1, + pickPrefix, + ); requestRender(); return; } @@ -270,8 +275,13 @@ export function createLineCoverage({ // Lookup, picking and counts follow what is drawn, not the whole tile. const sequences = drawnSequences(entry); entry.sequences = new Map(sequences.map((s) => [s.id, s])); - if(state.viewer?.engine){ - const group = state.viewer.lines(sequences,colors.coverage,tuning.lineWidthPx,pickPrefix); + if (state.viewer?.engine) { + const group = state.viewer.lines( + sequences, + colors.coverage, + tuning.lineWidthPx, + pickPrefix, + ); entry.primitives = group ? [group] : []; return; } @@ -409,7 +419,9 @@ export function createLineCoverage({ */ function wantedTiles(viewer) { const ground = groundUnderCamera(viewer); - const cameraHeight = viewer.engine?.camera.pose().cartographic.height ?? viewer.camera?.positionCartographic?.height; + const cameraHeight = + viewer.engine?.camera.pose().cartographic.height ?? + viewer.camera?.positionCartographic?.height; const height = Number.isFinite(cameraHeight) ? cameraHeight - (ground ?? 0) : null; @@ -438,7 +450,13 @@ export function createLineCoverage({ /** Recompute the tile set for the current camera and reconcile primitives. */ function refresh() { const viewer = state.viewer; - if (!viewer || state.context.waitingForView?.() || !state.context.isActive() || state.coverage.blocked) return; + if ( + !viewer || + state.context.waitingForView?.() || + !state.context.isActive() || + state.coverage.blocked + ) + return; const { zoom, tiles, hint } = wantedTiles(viewer); if (hint) { state.coverage.hint = hint; @@ -550,7 +568,7 @@ export function createLineCoverage({ releaseGraphics() { detach(); highlight(null); - for(const entry of state.coverage.tiles.values()) detachPrimitive(entry); + for (const entry of state.coverage.tiles.values()) detachPrimitive(entry); }, refresh, clear, diff --git a/src/layers/streetLevel/providers/panoramax/index.js b/src/layers/streetLevel/providers/panoramax/index.js index 368d3f751..de71595fc 100644 --- a/src/layers/streetLevel/providers/panoramax/index.js +++ b/src/layers/streetLevel/providers/panoramax/index.js @@ -256,7 +256,9 @@ export function createPanoramaxProvider({ * from high up the click may land tens of metres off the line. */ function lineClickRadius() { - const height = state.viewer?.engine?.camera.pose().cartographic.height ?? state.viewer?.camera?.positionCartographic?.height; + const height = + state.viewer?.engine?.camera.pose().cartographic.height ?? + state.viewer?.camera?.positionCartographic?.height; if (!Number.isFinite(height)) return NEAREST_RADIUS_M; return Math.min( LINE_CLICK_RADIUS_MAX_M, diff --git a/src/layers/streetLevel/selection.js b/src/layers/streetLevel/selection.js index 1d98ebbed..5798053a0 100644 --- a/src/layers/streetLevel/selection.js +++ b/src/layers/streetLevel/selection.js @@ -10,11 +10,13 @@ export function createSelection({ state, parts }) { const viewer = state.viewer; if (!viewer || !state.enabled) return; if (input?.isPointerFree && !input.isPointerFree()) return; - const picked = viewer.engine ? click.picked : viewer.scene.pick( - click.position, - CLICK_PICK_WINDOW_PX, - CLICK_PICK_WINDOW_PX, - ); + const picked = viewer.engine + ? click.picked + : viewer.scene.pick( + click.position, + CLICK_PICK_WINDOW_PX, + CLICK_PICK_WINDOW_PX, + ); const id = picking?.resolvePickId ? picking.resolvePickId(picked) : picked?.id; @@ -33,10 +35,9 @@ export function createSelection({ state, parts }) { /** Lon/lat of the ground under a screen point, or null (sky, no picker). */ function groundPointAt(viewer, screenPosition) { - const position = viewer.engine ? viewer.engine.picker.pickGlobe(screenPosition) : state.services.scenePick?.pickGroundPosition?.( - viewer, - screenPosition, - ); + const position = viewer.engine + ? viewer.engine.picker.pickGlobe(screenPosition) + : state.services.scenePick?.pickGroundPosition?.(viewer, screenPosition); const carto = position && Cesium.Cartographic.fromCartesian(position); return carto ? { @@ -69,16 +70,16 @@ export function createSelection({ state, parts }) { function install(viewer) { if (state.clickHandler) return; if (viewer.engine) { - const remove = viewer.engine.input.on('click',onClick,{pick:true}); - state.clickHandler = { isDestroyed:()=>false,destroy:remove }; + const remove = viewer.engine.input.on('click', onClick, { pick: true }); + state.clickHandler = { isDestroyed: () => false, destroy: remove }; } else { - state.clickHandler = new Cesium.ScreenSpaceEventHandler( - viewer.scene.canvas, - ); - state.clickHandler.setInputAction( - onClick, - Cesium.ScreenSpaceEventType.LEFT_CLICK, - ); + state.clickHandler = new Cesium.ScreenSpaceEventHandler( + viewer.scene.canvas, + ); + state.clickHandler.setInputAction( + onClick, + Cesium.ScreenSpaceEventType.LEFT_CLICK, + ); } document.addEventListener('keydown', onKeyDown); picking?.registerPickOwner?.(STREET_LEVEL_LAYER_ID, parts.router.ownsPick); diff --git a/src/layers/streetLevel/sequenceCones.js b/src/layers/streetLevel/sequenceCones.js index d4c2b7c86..00eb2a502 100644 --- a/src/layers/streetLevel/sequenceCones.js +++ b/src/layers/streetLevel/sequenceCones.js @@ -142,7 +142,8 @@ export function createSequenceCones({ * reloads a selected one (a provider that loads part of a sequence moved on). */ async function select(sequenceId, { force = false } = {}) { - if (!sequenceId || (!state.viewer && !state.context.waitingForView?.())) return; + if (!sequenceId || (!state.viewer && !state.context.waitingForView?.())) + return; // Already on screen, or already on its way. if ( !force && diff --git a/src/layers/streetLevel/view.js b/src/layers/streetLevel/view.js index c573c138e..66f1408b0 100644 --- a/src/layers/streetLevel/view.js +++ b/src/layers/streetLevel/view.js @@ -2,7 +2,8 @@ import * as Cesium from 'cesium'; import { wrapLon } from './tileMath.js'; export const cameraLocation = (view) => - view?.engine?.camera.pose().cartographic ?? view?.camera?.positionCartographic; + view?.engine?.camera.pose().cartographic ?? + view?.camera?.positionCartographic; const canvasOf = (view) => view?.engine?.host.canvas ?? view?.scene?.canvas; /** Equirectangular metres between {lon, lat} points, the short way round ±180°; street scale. */ export function metresBetween(a, b) { @@ -70,7 +71,9 @@ function raisedEllipsoid(ellipsoid, height) { function groundHit(camera, point, ellipsoid, maxRange, engine) { let cartesian = null; try { - cartesian = engine ? engine.picker.pickGlobe(point) : camera.pickEllipsoid(point, ellipsoid); + cartesian = engine + ? engine.picker.pickGlobe(point) + : camera.pickEllipsoid(point, ellipsoid); } catch { cartesian = null; } @@ -78,10 +81,18 @@ function groundHit(camera, point, ellipsoid, maxRange, engine) { if ( Number.isFinite(maxRange) && (engine?.camera.livePosition || camera.positionWC) && - Cesium.Cartesian3.distance(cartesian, engine?.camera.livePosition ?? camera.positionWC) > maxRange + Cesium.Cartesian3.distance( + cartesian, + engine?.camera.livePosition ?? camera.positionWC, + ) > maxRange ) return null; - return Cesium.Cartographic.fromCartesian(cartesian, engine ? Cesium.Ellipsoid.WGS84 : ellipsoid) || null; + return ( + Cesium.Cartographic.fromCartesian( + cartesian, + engine ? Cesium.Ellipsoid.WGS84 : ellipsoid, + ) || null + ); } /** Camera height (m) above the ground under it. */ @@ -120,7 +131,13 @@ export function visibleBbox( for (let j = 0; j <= BBOX_GRID; j++) { point.x = (width * i) / BBOX_GRID; point.y = (height * j) / BBOX_GRID; - const carto = groundHit(camera, point, ellipsoid, maxRange, viewer.engine); + const carto = groundHit( + camera, + point, + ellipsoid, + maxRange, + viewer.engine, + ); if (carto) hits.push(carto); } } diff --git a/src/layers/submarineCables/overlay.js b/src/layers/submarineCables/overlay.js index 04eea35ee..9c9b5dbc1 100644 --- a/src/layers/submarineCables/overlay.js +++ b/src/layers/submarineCables/overlay.js @@ -292,8 +292,14 @@ export function updateCableReferenceTip( return true; } -export function updateCableReferenceStem(record, cameraPositionWC, canvasHeight, fov) { - if (!updateCableReferenceTip(record, cameraPositionWC, canvasHeight, fov)) return false; +export function updateCableReferenceStem( + record, + cameraPositionWC, + canvasHeight, + fov, +) { + if (!updateCableReferenceTip(record, cameraPositionWC, canvasHeight, fov)) + return false; record.stemPositionBufferIndex = 1 - record.stemPositionBufferIndex; const stemPositions = record.stemPositionBuffers[record.stemPositionBufferIndex]; diff --git a/src/layers/submarineCables/policy.js b/src/layers/submarineCables/policy.js index dbf454249..9fb0c4341 100644 --- a/src/layers/submarineCables/policy.js +++ b/src/layers/submarineCables/policy.js @@ -86,11 +86,15 @@ export const CABLE_GLOBE_STACK_IDS = Object.freeze( export const MARKER_COLLECTION_EXPECTATIONS = Object.freeze([ Object.freeze({ key: '_billboardCollection', - get type() { return Cesium.BillboardCollection; }, + get type() { + return Cesium.BillboardCollection; + }, }), Object.freeze({ key: '_pointCollection', - get type() { return Cesium.PointPrimitiveCollection; }, + get type() { + return Cesium.PointPrimitiveCollection; + }, }), ]); diff --git a/src/layers/traffic/animation.js b/src/layers/traffic/animation.js index 91cad5f1a..5ab522262 100644 --- a/src/layers/traffic/animation.js +++ b/src/layers/traffic/animation.js @@ -320,7 +320,9 @@ export function createAnimation({ // Jam heat-lines throb (~1.6 s period) — one shared material uniform for // the whole jam batch, no geometry rebuild. Null outside live heatline mode. if (layerState._native && layerState._heatJamPrim) { - layerState._heatJamPrim.setAlpha(HEAT_JAM_BASE_ALPHA + HEAT_JAM_PULSE_ALPHA * Math.sin(now / 260)); + layerState._heatJamPrim.setAlpha( + HEAT_JAM_BASE_ALPHA + HEAT_JAM_PULSE_ALPHA * Math.sin(now / 260), + ); } if (layerState._heatJamPrim?.appearance) { layerState._heatJamPrim.appearance.material.uniforms.color.alpha = @@ -353,7 +355,10 @@ export function createAnimation({ /** Remove all point primitives and reset dot/road arrays and counters. */ function clearDots() { - services.credits?.hideOsmCredit?.(layerState._engine ?? layerState._viewer, 'traffic'); + services.credits?.hideOsmCredit?.( + layerState._engine ?? layerState._viewer, + 'traffic', + ); parts.retention.clear(); if (layerState._dots.length) layerState._motion.rebuilds++; if (layerState._pointCollection) layerState._pointCollection.removeAll(); diff --git a/src/layers/traffic/flow.js b/src/layers/traffic/flow.js index 76504bb51..5d8ab27a9 100644 --- a/src/layers/traffic/flow.js +++ b/src/layers/traffic/flow.js @@ -87,7 +87,10 @@ export function createFlow({ state: layerState, services, parts, source }) { layerState._flowStatusKnown = true; if (layerState._liveMode) { console.log('[Data:Traffic] TomTom key present — live flow mode'); - registerDynamicCredit(layerState._engine ?? layerState._viewer, TOMTOM_CREDIT); + registerDynamicCredit( + layerState._engine ?? layerState._viewer, + TOMTOM_CREDIT, + ); } }) .catch((e) => { @@ -236,7 +239,8 @@ export function createFlow({ state: layerState, services, parts, source }) { return false; if (layerState._waitForScene) { await layerState._waitForScene; - if (generation !== layerState._loadGeneration || signal?.aborted) return false; + if (generation !== layerState._loadGeneration || signal?.aborted) + return false; } const revision = (layerState._surfaceRevision || 0) + 1; layerState._surfaceRevision = revision; @@ -384,7 +388,9 @@ export function createFlow({ state: layerState, services, parts, source }) { maxX = -Infinity, maxY = -Infinity; for (const point of road.waypoints) { - const screen = scene.project ? scene.project(point, {}) : Cesium.SceneTransforms.worldToWindowCoordinates(scene, point); + const screen = scene.project + ? scene.project(point, {}) + : Cesium.SceneTransforms.worldToWindowCoordinates(scene, point); if (!screen) continue; minX = Math.min(minX, screen.x); maxX = Math.max(maxX, screen.x); @@ -411,7 +417,9 @@ export function createFlow({ state: layerState, services, parts, source }) { roadDotBudget(altitude), ); const admitted = selected.filter((r, i) => budgets[i] > 0); - const camera = (layerState._engine?.camera.livePosition ?? layerState._viewer?.camera?.positionWC); + const camera = + layerState._engine?.camera.livePosition ?? + layerState._viewer?.camera?.positionWC; if (camera) admitted.sort( (a, b) => diff --git a/src/layers/traffic/footprint.js b/src/layers/traffic/footprint.js index 9179d09a0..1beb629fc 100644 --- a/src/layers/traffic/footprint.js +++ b/src/layers/traffic/footprint.js @@ -102,7 +102,9 @@ export function createReticleFootprint() { dlon -= 360 * Math.round(dlon / 360); dx = dlon * 111.195 * Math.cos(nadir.latitude); } else { - const picked = engine ? camera.pickRay(p) : camera.getPickRay(p, (ray ??= new Cesium.Ray())) || ray; + const picked = engine + ? camera.pickRay(p) + : camera.getPickRay(p, (ray ??= new Cesium.Ray())) || ray; if (!picked) continue; const d = picked.direction; dx = diff --git a/src/layers/traffic/ingestion.js b/src/layers/traffic/ingestion.js index 27aa0107e..bf5168e2d 100644 --- a/src/layers/traffic/ingestion.js +++ b/src/layers/traffic/ingestion.js @@ -275,11 +275,13 @@ export function createIngestion({ let estimated = services.ground?.cachedGroundFloor?.(centerLat, centerLon); if (!Number.isFinite(estimated)) { try { - estimated = scene?.engine ? scene.sampleHeight(location) : scene?.globe?.show - ? scene.globe.getHeight?.(location) - : scene?.sampleHeightSupported - ? scene.sampleHeight(location, [layerState._pointCollection]) - : undefined; + estimated = scene?.engine + ? scene.sampleHeight(location) + : scene?.globe?.show + ? scene.globe.getHeight?.(location) + : scene?.sampleHeightSupported + ? scene.sampleHeight(location, [layerState._pointCollection]) + : undefined; } catch { /* the local mesh can still be loading */ } diff --git a/src/layers/traffic/lifecycle.js b/src/layers/traffic/lifecycle.js index 186151a36..9d34033a5 100644 --- a/src/layers/traffic/lifecycle.js +++ b/src/layers/traffic/lifecycle.js @@ -21,10 +21,12 @@ export function createLifecycle({ } function bindScene() { - const viewer = layerState._viewer, engine = layerState._engine; + const viewer = layerState._viewer, + engine = layerState._engine; const camera = engine?.camera ?? viewer.camera; const frames = engine?.frames ?? viewer.scene; - if (viewer) layerState._surfaceFrameRemover = observeTrafficSurface(viewer.scene); + if (viewer) + layerState._surfaceFrameRemover = observeTrafficSurface(viewer.scene); layerState._pointCollection.show = true; layerState._lastAnimTime = 0; parts.animation.syncRenderDemand(); @@ -36,11 +38,17 @@ export function createLifecycle({ parts.timing.clearTrafficTimingEntries(); layerState._trafficTimingCurrentAnchor = null; layerState._trafficTimingPostRenderRemovers = new Set(); - layerState._trafficTimingMoveEndRemover = camera.moveEnd.addEventListener(parts.timing.markTrafficTimingMoveEnd); + layerState._trafficTimingMoveEndRemover = camera.moveEnd.addEventListener( + parts.timing.markTrafficTimingMoveEnd, + ); } - layerState._cameraRemover = camera.changed.addEventListener(parts.viewport.onCameraChanged); - layerState._arrivalRemover = camera.moveEnd.addEventListener(() => parts.viewport.onCameraChanged({immediate:true})); - claimCameraSensitivity(engine ?? camera,'traffic',0.05); + layerState._cameraRemover = camera.changed.addEventListener( + parts.viewport.onCameraChanged, + ); + layerState._arrivalRemover = camera.moveEnd.addEventListener(() => + parts.viewport.onCameraChanged({ immediate: true }), + ); + claimCameraSensitivity(engine ?? camera, 'traffic', 0.05); } function unbindScene() { @@ -48,15 +56,22 @@ export function createLifecycle({ layerState._surfaceFrameRemover?.(); layerState._preRenderRemover?.(); layerState._trafficTimingMoveEndRemover?.(); - for (const remove of layerState._trafficTimingPostRenderRemovers || []) remove(); + for (const remove of layerState._trafficTimingPostRenderRemovers || []) + remove(); layerState._trafficTimingPostRenderRemovers = null; layerState._trafficTimingCurrentAnchor = null; layerState._cameraRemover?.(); // Some legacy camera events do not return a remover. - if (!layerState._cameraRemover) camera?.changed?.removeEventListener?.(parts.viewport.onCameraChanged); + if (!layerState._cameraRemover) + camera?.changed?.removeEventListener?.(parts.viewport.onCameraChanged); layerState._arrivalRemover?.(); - layerState._surfaceFrameRemover = layerState._preRenderRemover = layerState._cameraRemover = layerState._arrivalRemover = layerState._trafficTimingMoveEndRemover = null; - releaseCameraSensitivity(layerState._engine ?? camera,'traffic'); + layerState._surfaceFrameRemover = + layerState._preRenderRemover = + layerState._cameraRemover = + layerState._arrivalRemover = + layerState._trafficTimingMoveEndRemover = + null; + releaseCameraSensitivity(layerState._engine ?? camera, 'traffic'); } const methods = { @@ -99,8 +114,12 @@ export function createLifecycle({ layerState._stylePreset = document?.documentElement?.dataset?.gevStyle || 'normal'; if (!layerState._styleListenerBound) { - layerState._styleListener = (e) => parts.style.setStylePreset(e?.detail?.style); - window.addEventListener('gev:style-change', layerState._styleListener); + layerState._styleListener = (e) => + parts.style.setStylePreset(e?.detail?.style); + window.addEventListener( + 'gev:style-change', + layerState._styleListener, + ); layerState._styleListenerBound = true; } } @@ -108,25 +127,42 @@ export function createLifecycle({ console.log('[Data:Traffic] Initialized'); }, - attachRenderer(viewer, {engine=null,waitForCameraRestore=false}={}) { + attachRenderer( + viewer, + { engine = null, waitForCameraRestore = false } = {}, + ) { layerState._restoringViewport = waitForCameraRestore; layerState._viewer = viewer; layerState._engine = engine; - layerState._native = !viewer && engine ? services.nativeRenderer(engine) : null; + layerState._native = + !viewer && engine ? services.nativeRenderer(engine) : null; layerState._heatSupported = null; - const points = layerState._native?.points ?? new Cesium.PointPrimitiveCollection({blendOption: Cesium.BlendOption.TRANSLUCENT}); + const points = + layerState._native?.points ?? + new Cesium.PointPrimitiveCollection({ + blendOption: Cesium.BlendOption.TRANSLUCENT, + }); if (viewer) viewer.scene.primitives.add(points); layerState._pointCollection = points; - for(const dot of layerState._dots) dot.point = points.add(dot.point); + for (const dot of layerState._dots) dot.point = points.add(dot.point); points.show = layerState._enabled; if (!waitForCameraRestore) readyForSurfaceWork(); if (layerState._enabled) { bindScene(); parts.rendering.rebuildHeatLines(layerState._roads); - if (layerState._dots.length && layerState._roads.some(r=>!r.directFlow)) - services.credits?.showOsmCredit?.(engine ?? viewer,'traffic',{openMapTiles:true}); - if (layerState._liveMode) services.credits?.registerDynamicCredit?.(engine ?? viewer, services.credits.TOMTOM_CREDIT); - parts.viewport.onCameraChanged({immediate:true}); + if ( + layerState._dots.length && + layerState._roads.some((r) => !r.directFlow) + ) + services.credits?.showOsmCredit?.(engine ?? viewer, 'traffic', { + openMapTiles: true, + }); + if (layerState._liveMode) + services.credits?.registerDynamicCredit?.( + engine ?? viewer, + services.credits.TOMTOM_CREDIT, + ); + parts.viewport.onCameraChanged({ immediate: true }); } }, resumeViewport() { @@ -136,40 +172,70 @@ export function createLifecycle({ if (!layerState._enabled) return; if (layerState._populationPending) parts.retention.resume(); const previous = layerState._surfaceInput; - if (previous && !layerState._fetching && layerState._lastBounds === previous.bounds) { + if ( + previous && + !layerState._fetching && + layerState._lastBounds === previous.bounds + ) { // A completed pass, not a refinement: the incoming globe often still // shows coarse tiles, so it samples provisionally and refines once its // surface has loaded ('Handoff refined'). A refinement here never // refined again and left vehicles on those coarse heights. - void parts.flow.applyFlowThenRender(previous.roads, previous.bounds, layerState._loadGeneration, - parts.viewport.getCameraAltitude(), 'Handoff').catch(error => { - if (error?.name !== 'AbortError') console.warn('[Data:Traffic] Surface refresh failed', error); + void parts.flow + .applyFlowThenRender( + previous.roads, + previous.bounds, + layerState._loadGeneration, + parts.viewport.getCameraAltitude(), + 'Handoff', + ) + .catch((error) => { + if (error?.name !== 'AbortError') + console.warn('[Data:Traffic] Surface refresh failed', error); }); } - parts.viewport.onCameraChanged({immediate:true}); + parts.viewport.onCameraChanged({ immediate: true }); }, detachRenderer() { - if(!layerState._pointCollection)return; + if (!layerState._pointCollection) return; unbindScene(); clearTimeout(layerState._fetchTimeout); layerState._surfaceRefineRemove?.(); layerState._surfaceRefineRemove = null; - layerState._surfaceRevision = (layerState._surfaceRevision || 0)+1; + layerState._surfaceRevision = (layerState._surfaceRevision || 0) + 1; layerState._preparedRoads?.clear(); parts.rendering.removeHeatLines(); - services.credits?.hideOsmCredit?.(layerState._engine ?? layerState._viewer,'traffic'); - for(const dot of layerState._dots) { - const p=dot.point; - dot.point={position:Cesium.Cartesian3.clone(p.position),pixelSize:p.pixelSize,color:p.color,show:p.show, - outlineColor:p.outlineColor,outlineWidth:p.outlineWidth,scaleByDistance:p.scaleByDistance, - translucencyByDistance:p.translucencyByDistance,disableDepthTestDistance:0}; + services.credits?.hideOsmCredit?.( + layerState._engine ?? layerState._viewer, + 'traffic', + ); + for (const dot of layerState._dots) { + const p = dot.point; + dot.point = { + position: Cesium.Cartesian3.clone(p.position), + pixelSize: p.pixelSize, + color: p.color, + show: p.show, + outlineColor: p.outlineColor, + outlineWidth: p.outlineWidth, + scaleByDistance: p.scaleByDistance, + translucencyByDistance: p.translucencyByDistance, + disableDepthTestDistance: 0, + }; } layerState._viewer?.scene.primitives.remove(layerState._pointCollection); layerState._native?.destroy(); - if(layerState._renderHeld)services.render.releaseContinuousRender('traffic'); - layerState._renderHeld=false; - layerState._pointCollection=layerState._viewer=layerState._engine=layerState._native=null; - layerState._waitForScene ??= new Promise(resolve=>{layerState._sceneReady=resolve;}); + if (layerState._renderHeld) + services.render.releaseContinuousRender('traffic'); + layerState._renderHeld = false; + layerState._pointCollection = + layerState._viewer = + layerState._engine = + layerState._native = + null; + layerState._waitForScene ??= new Promise((resolve) => { + layerState._sceneReady = resolve; + }); }, /** @@ -220,7 +286,10 @@ export function createLifecycle({ disable(viewer) { layerState._enabled = false; layerState._surfaceInput = null; - services.credits?.hideOsmCredit?.(layerState._engine ?? layerState._viewer, 'traffic'); + services.credits?.hideOsmCredit?.( + layerState._engine ?? layerState._viewer, + 'traffic', + ); parts.animation.syncRenderDemand(); clearTimeout(layerState._fetchTimeout); clearInterval(layerState._enableKickTimer); @@ -255,7 +324,9 @@ export function createLifecycle({ destroy(viewer) { this.disable(viewer); if (layerState._pointCollection) { - layerState._viewer?.scene.primitives.remove(layerState._pointCollection); + layerState._viewer?.scene.primitives.remove( + layerState._pointCollection, + ); layerState._native?.destroy(); layerState._native = null; layerState._pointCollection = null; @@ -263,7 +334,10 @@ export function createLifecycle({ parts.rendering.removeHeatLines(); layerState._tileCache.clear(); if (layerState._styleListenerBound && typeof window !== 'undefined') { - window.removeEventListener('gev:style-change', layerState._styleListener); + window.removeEventListener( + 'gev:style-change', + layerState._styleListener, + ); layerState._styleListenerBound = false; } layerState._sceneReady?.(); diff --git a/src/layers/traffic/policy.js b/src/layers/traffic/policy.js index f355b2819..fd99d3b85 100644 --- a/src/layers/traffic/policy.js +++ b/src/layers/traffic/policy.js @@ -144,8 +144,7 @@ export const HEAT_JAM_COLOR = rgbaFromCss('#e05252'); /** @const {Cesium.Color} Slow corridor color (bucket amber, faint + static). */ -export const HEAT_SLOW_COLOR = - rgbaFromCss('#f0b23e').withAlpha(0.2); +export const HEAT_SLOW_COLOR = rgbaFromCss('#f0b23e').withAlpha(0.2); /** @const {number} Far-distance scale floor for jam dots (shipped: 0.3). */ diff --git a/src/layers/traffic/rendering.js b/src/layers/traffic/rendering.js index 4296299d8..b38452060 100644 --- a/src/layers/traffic/rendering.js +++ b/src/layers/traffic/rendering.js @@ -129,9 +129,20 @@ export function createRendering({ : HEAT_SLOW_COLOR; if (layerState._native) { - const roadsFor = bucket => kept.filter(item=>item.bucket===bucket).map(item=>item.road); - layerState._heatJamPrim = layerState._native.makeHeat(roadsFor('jam'), jamLineColor, HEAT_LINE_JAM_WIDTH, HEAT_JAM_BASE_ALPHA); - layerState._heatSlowPrim = layerState._native.makeHeat(roadsFor('slow'), slowLineColor, HEAT_LINE_SLOW_WIDTH, slowLineColor.alpha); + const roadsFor = (bucket) => + kept.filter((item) => item.bucket === bucket).map((item) => item.road); + layerState._heatJamPrim = layerState._native.makeHeat( + roadsFor('jam'), + jamLineColor, + HEAT_LINE_JAM_WIDTH, + HEAT_JAM_BASE_ALPHA, + ); + layerState._heatSlowPrim = layerState._native.makeHeat( + roadsFor('slow'), + slowLineColor, + HEAT_LINE_SLOW_WIDTH, + slowLineColor.alpha, + ); layerState._heatLineCount = kept.length; parts.animation.syncRenderDemand(); return; @@ -228,7 +239,8 @@ export function createRendering({ areaDist = Math.max( areaDist, Cesium.Cartesian3.distance( - layerState._engine?.camera.livePosition ?? layerState._viewer.camera.positionWC, + layerState._engine?.camera.livePosition ?? + layerState._viewer.camera.positionWC, wp, ), ); @@ -247,10 +259,18 @@ export function createRendering({ : null; parts.retention.reconcile(filteredRoads, altitude, replace); if (layerState._dots.length && filteredRoads.some((r) => !r.directFlow)) - services.credits?.showOsmCredit?.(layerState._engine ?? layerState._viewer, 'traffic', { - openMapTiles: true, - }); - else services.credits?.hideOsmCredit?.(layerState._engine ?? layerState._viewer, 'traffic'); + services.credits?.showOsmCredit?.( + layerState._engine ?? layerState._viewer, + 'traffic', + { + openMapTiles: true, + }, + ); + else + services.credits?.hideOsmCredit?.( + layerState._engine ?? layerState._viewer, + 'traffic', + ); parts.model.recolorDotsInPlace(label); const renderMetrics = state diff --git a/src/layers/traffic/retention.js b/src/layers/traffic/retention.js index 3e548d56c..c6cb3a519 100644 --- a/src/layers/traffic/retention.js +++ b/src/layers/traffic/retention.js @@ -217,7 +217,8 @@ export function createRetention({ state, parts }) { const blend = Math.min(1, dt * 12); const scene = state._trafficScene?.() ?? state._viewer?.scene; const canProject = - scene?.canvas?.clientHeight && (scene.project || (scene.camera && scene.frameState)); + scene?.canvas?.clientHeight && + (scene.project || (scene.camera && scene.frameState)); state._heightPending = false; for (let r = 0; r < state._roads.length; r++) { const road = state._roads[r]; @@ -300,5 +301,13 @@ export function createRetention({ state, parts }) { state._populationPending = false; state._heightPending = false; } - return { reconcile, retarget, easeHeights, finishHeights, clear, refreshCounts, resume: schedule }; + return { + reconcile, + retarget, + easeHeights, + finishHeights, + clear, + refreshCounts, + resume: schedule, + }; } diff --git a/src/layers/traffic/state.js b/src/layers/traffic/state.js index 76e5082e3..50b19c535 100644 --- a/src/layers/traffic/state.js +++ b/src/layers/traffic/state.js @@ -18,8 +18,8 @@ export function createState({ services }) { layerState._viewer = null; layerState._engine = null; layerState._native = null; - layerState._trafficScene = () => layerState._native?.surface ?? layerState._viewer?.scene; - + layerState._trafficScene = () => + layerState._native?.surface ?? layerState._viewer?.scene; /** @type {Cesium.PointPrimitiveCollection|null} */ diff --git a/src/layers/traffic/surface.js b/src/layers/traffic/surface.js index 8f9b901f9..9e91ae7ef 100644 --- a/src/layers/traffic/surface.js +++ b/src/layers/traffic/surface.js @@ -50,7 +50,7 @@ export function observeTrafficSurface(scene) { } function currentRenderedFrame(scene) { - if (scene.engine) return {ready:scene.ready}; + if (scene.engine) return { ready: scene.ready }; if (!scene.postRender?.addEventListener || !scene.camera?.viewMatrix) return { ready: surfaceTilesReady(scene) }; const frame = renderedFrames.get(scene); @@ -280,7 +280,9 @@ export async function prepareRoadSurfaces( cache = { surface, heights: new Map() }; sceneCaches.set(scene, cache); } - const cameraCarto = scene.engine?.camera.pose().cartographic ?? scene.camera?.positionCartographic; + const cameraCarto = + scene.engine?.camera.pose().cartographic ?? + scene.camera?.positionCartographic; const cameraLon = cameraCarto ? Cesium.Math.toDegrees(cameraCarto.longitude) : 0; @@ -345,7 +347,8 @@ export async function prepareRoadSurfaces( if (validHeight(sampled)) metrics.depthHits++; } if ( - !scene.engine && !scene.globe?.show && + !scene.engine && + !scene.globe?.show && !validHeight(sampled) && scene.sampleHeightSupported ) { diff --git a/src/layers/traffic/viewport.js b/src/layers/traffic/viewport.js index 739060c08..f96b49110 100644 --- a/src/layers/traffic/viewport.js +++ b/src/layers/traffic/viewport.js @@ -24,7 +24,9 @@ export function createViewport({ state: layerState, services, parts, source }) { */ function getCameraAltitude() { - const carto = layerState._engine?.camera.pose().cartographic ?? layerState._viewer?.camera.positionCartographic; + const carto = + layerState._engine?.camera.pose().cartographic ?? + layerState._viewer?.camera.positionCartographic; return carto ? carto.height : Infinity; } @@ -35,7 +37,9 @@ export function createViewport({ state: layerState, services, parts, source }) { */ function getViewBounds() { - const rect = layerState._engine?.camera.viewRectangle() ?? layerState._viewer?.camera.computeViewRectangle(); + const rect = + layerState._engine?.camera.viewRectangle() ?? + layerState._viewer?.camera.computeViewRectangle(); if (!rect) return null; return { south: geo.math.toDegrees(rect.south), @@ -60,18 +64,23 @@ export function createViewport({ state: layerState, services, parts, source }) { */ function getFetchCenter() { - const carto = layerState._engine?.camera.pose().cartographic ?? layerState._viewer?.camera.positionCartographic; + const carto = + layerState._engine?.camera.pose().cartographic ?? + layerState._viewer?.camera.positionCartographic; if (!carto) return null; const nadirLat = geo.math.toDegrees(carto.latitude); const nadirLon = geo.math.toDegrees(carto.longitude); let hitLat; let hitLon; - const canvas = layerState._engine?.host.canvas ?? layerState._viewer.scene.canvas; + const canvas = + layerState._engine?.host.canvas ?? layerState._viewer.scene.canvas; const width = canvas.clientWidth || canvas.width; const height = canvas.clientHeight || canvas.height; if (width > 0 && height > 0) { - const hit = (layerState._engine?.camera ?? layerState._viewer.camera).pickEllipsoid( + const hit = ( + layerState._engine?.camera ?? layerState._viewer.camera + ).pickEllipsoid( geo.cartesian2.create(width / 2, height / 2), Cesium.Ellipsoid.WGS84, ); @@ -174,7 +183,12 @@ export function createViewport({ state: layerState, services, parts, source }) { /** Debounce camera events and sample the actual settled view, including final flight steps. */ function onCameraChanged(options) { - if (!layerState._enabled || layerState._waitForScene || layerState._restoringViewport) return; + if ( + !layerState._enabled || + layerState._waitForScene || + layerState._restoringViewport + ) + return; clearTimeout(layerState._fetchTimeout); if (getCameraAltitude() > ACTIVATION_ALTITUDE) { clearTimeout(layerState._retryTimer); @@ -197,10 +211,19 @@ export function createViewport({ state: layerState, services, parts, source }) { : null; layerState._fetchTimeout = setTimeout( () => { - if (!layerState._enabled || layerState._waitForScene || layerState._restoringViewport) return; + if ( + !layerState._enabled || + layerState._waitForScene || + layerState._restoringViewport + ) + return; const alt = getCameraAltitude(); if (alt > ACTIVATION_ALTITUDE) return onCameraChanged(); - const footprint = reticleFootprint(layerState._viewer, alt, layerState._engine); + const footprint = reticleFootprint( + layerState._viewer, + alt, + layerState._engine, + ); const bounds = footprint || getViewBounds(); if (!bounds) return; const center = footprint ? null : getFetchCenter(); diff --git a/src/layers/transit/lifecycle.js b/src/layers/transit/lifecycle.js index f92193777..fbc16d519 100644 --- a/src/layers/transit/lifecycle.js +++ b/src/layers/transit/lifecycle.js @@ -141,7 +141,10 @@ export function createLifecycle({ state._viewer = null; throw error; } - state._overlayHost.setVisible(TRANSIT_SELECTED_OVERLAY_SOURCE_ID, state._enabled); + state._overlayHost.setVisible( + TRANSIT_SELECTED_OVERLAY_SOURCE_ID, + state._enabled, + ); if (state._enabled) { for (const [key, entry] of state._vehicles) { parts.rendering.addMarker(key, entry); @@ -203,7 +206,8 @@ export function createLifecycle({ parts.rendering.refreshVisibility(); parts.trails.rebind(); parts.selection.refreshSelectedCard(true); - for (const feed of state._activeFeeds.values()) parts.renderer.addCredit(feed); + for (const feed of state._activeFeeds.values()) + parts.renderer.addCredit(feed); }, resumeViewport() { if (!state._restoringViewport) return; @@ -217,7 +221,10 @@ export function createLifecycle({ clearInterval(state._maintenanceTimer); clearTimeout(state._cameraDebounceTimer); clearTimeout(state._visibilityTimer); - state._maintenanceTimer = state._cameraDebounceTimer = state._visibilityTimer = null; + state._maintenanceTimer = + state._cameraDebounceTimer = + state._visibilityTimer = + null; parts.selection.removeClickHandler(); unregisterPickOwner('transit'); state._cameraChangedRemove?.(); @@ -238,7 +245,11 @@ export function createLifecycle({ if (state._renderHeld) releaseContinuousRender('transit'); state._renderHeld = false; parts.renderer.destroy(); - parts.renderer = state._markers = state._animatedMarkers = state._viewer = null; + parts.renderer = + state._markers = + state._animatedMarkers = + state._viewer = + null; }, /** diff --git a/src/layers/transit/viewport.js b/src/layers/transit/viewport.js index d710dd187..a9de80238 100644 --- a/src/layers/transit/viewport.js +++ b/src/layers/transit/viewport.js @@ -79,7 +79,8 @@ export function createViewport({ state, services, parts }) { globalThis.document?.visibilityState === 'hidden' ) return; - if (!state._enabled || !parts.renderer?.ready || state._restoringViewport) return; + if (!state._enabled || !parts.renderer?.ready || state._restoringViewport) + return; refreshViewBounds(); const altitude = getCameraAltitude(); state._altitudeGateOpen = altitudeGateOpen(altitude); diff --git a/src/layers/vessels/renderer.test.mjs b/src/layers/vessels/renderer.test.mjs index af3d3b079..496731a07 100644 --- a/src/layers/vessels/renderer.test.mjs +++ b/src/layers/vessels/renderer.test.mjs @@ -304,21 +304,36 @@ test('a pending AIS poll and selected trail survive replacement of the planet', const injected = services(recorders[0].renderer); const restored = []; injected.sprites.restoreSpriteOrderOnEnable = (id) => restored.push(id); - injected.renderer.create = ({ engine }) => recorders[engines.indexOf(engine)].renderer; + injected.renderer.create = ({ engine }) => + recorders[engines.indexOf(engine)].renderer; const previousDocument = globalThis.document; - globalThis.document = Object.assign(new EventTarget(), { getElementById: () => null }); - t.after(() => { globalThis.document = previousDocument; }); - let polls = 0, histories = 0, deliver; + globalThis.document = Object.assign(new EventTarget(), { + getElementById: () => null, + }); + t.after(() => { + globalThis.document = previousDocument; + }); + let polls = 0, + histories = 0, + deliver; const snapshot = (records) => ({ - records, source: 'AIS fixture', complete: true, transportStatus: 'live', - rawRowCount: records.length, observedAtMs: 500, freshness: 'current', + records, + source: 'AIS fixture', + complete: true, + transportStatus: 'live', + rawRowCount: records.length, + observedAtMs: 500, + freshness: 'current', }); const layer = createVesselLayer({ services: injected, source: { - getSnapshot: () => ++polls === 1 - ? snapshot([row('111', 40.6, -73.9)]) - : new Promise((resolve) => { deliver = resolve; }), + getSnapshot: () => + ++polls === 1 + ? snapshot([row('111', 40.6, -73.9)]) + : new Promise((resolve) => { + deliver = resolve; + }), getTrack: async () => { histories++; return { records: [{ latitude: 40.59, longitude: -73.92 }] }; diff --git a/src/layers/weather/index.js b/src/layers/weather/index.js index e2bfdf16a..74648fba6 100644 --- a/src/layers/weather/index.js +++ b/src/layers/weather/index.js @@ -108,10 +108,17 @@ export function createWeatherLayer({ clock ? clock.getState().mode === 'latest' : followLatest; const getHost = () => sceneEngine?.kind === 'navara' - ? { kind: sceneEngine.maps.photorealActive() ? 'tileset' : 'globe', collection: sceneEngine } - : imageryHost?.() ?? { collection: viewer?.imageryLayers, kind: 'globe' }; + ? { + kind: sceneEngine.maps.photorealActive() ? 'tileset' : 'globe', + collection: sceneEngine, + } + : (imageryHost?.() ?? { + collection: viewer?.imageryLayers, + kind: 'globe', + }); const notify = () => listener?.(); - const shownTime = () => (noFrame ? null : rendering?.getDiagnostics().time ?? lastShown); + const shownTime = () => + noFrame ? null : (rendering?.getDiagnostics().time ?? lastShown); const unsubscribeClock = clock?.subscribe(() => { if (!clock.getState().playing || suspended()) rendering?.cancelPrefetch?.(); notify(); @@ -298,23 +305,32 @@ export function createWeatherLayer({ motion = matchMedia?.('(prefers-reduced-motion: reduce)'); motion?.addEventListener?.('change', onVisibility); documentRef?.addEventListener?.('visibilitychange', onVisibility); - if (nextViewer || options.engine) layer.attachRenderer(nextViewer, options); + if (nextViewer || options.engine) + layer.attachRenderer(nextViewer, options); }, attachRenderer(nextViewer, { engine = null } = {}) { viewer = nextViewer; sceneEngine = engine; const options = { viewer, cesium, getHost, onChange: notify }; - rendering = engine?.kind === 'navara' - ? createNativeRendering(engine, options) - : createRendering(options); + rendering = + engine?.kind === 'navara' + ? createNativeRendering(engine, options) + : createRendering(options); checkHost(false); - eventTarget?.addEventListener?.('gev:map-stack-changed', onMapStackChanged); + eventTarget?.addEventListener?.( + 'gev:map-stack-changed', + onMapStackChanged, + ); rendering.setAlpha(opacity === 'light' ? 0.4 : satellite ? 0.7 : 0.8); if (!enabled) return; watchCamera(); if (!manifest) return; - const time = clock && !isLatest() ? clock.selectFor(id, clock.getState().target) : - (followLatest ? manifest.latest : lastShown); + const time = + clock && !isLatest() + ? clock.selectFor(id, clock.getState().target) + : followLatest + ? manifest.latest + : lastShown; if (time) void show(time); else rendering.setHidden(true); }, @@ -326,7 +342,10 @@ export function createWeatherLayer({ timer = null; removeCamera?.(); removeCamera = null; - eventTarget?.removeEventListener?.('gev:map-stack-changed', onMapStackChanged); + eventTarget?.removeEventListener?.( + 'gev:map-stack-changed', + onMapStackChanged, + ); rendering?.clear(); rendering = null; hostCollection = null; @@ -622,7 +641,10 @@ export function createWeatherLayer({ viewer?.scene?.globe?.ellipsoid, ); const camera = point - ? cesium.Cartographic.fromCartesian(point, viewer?.scene?.globe?.ellipsoid) + ? cesium.Cartographic.fromCartesian( + point, + viewer?.scene?.globe?.ellipsoid, + ) : null; const lon = camera ? cesium.Math.toDegrees(camera.longitude) : 0; const lat = camera ? cesium.Math.toDegrees(camera.latitude) : 0; diff --git a/src/layers/weather/render/navaraSurface.js b/src/layers/weather/render/navaraSurface.js index a2ea37dd1..fd5bc2779 100644 --- a/src/layers/weather/render/navaraSurface.js +++ b/src/layers/weather/render/navaraSurface.js @@ -16,7 +16,13 @@ const KEY = 'gevObservedWeather'; const owners = new WeakMap(); const products = ['Wind', 'Satellite', 'Radar', 'Lightning']; const slotFor = (p) => - p === 'wind' ? 'Wind' : p === 'radar' ? 'Radar' : p === 'lightning' ? 'Lightning' : 'Satellite'; + p === 'wind' + ? 'Wind' + : p === 'radar' + ? 'Radar' + : p === 'lightning' + ? 'Lightning' + : 'Satellite'; const emptyRect = [0, 0, -1, -1]; const rectArray = (r) => (r ? [r.west, r.south, r.east, r.north] : emptyRect); diff --git a/src/layers/weather/shellRendering.js b/src/layers/weather/shellRendering.js index 924637cdc..b20a3711a 100644 --- a/src/layers/weather/shellRendering.js +++ b/src/layers/weather/shellRendering.js @@ -407,7 +407,8 @@ export function createWeatherShell({ cacheBytes = WEATHER_SHELL_CACHE_BYTES, createSurface = createShellSurface, requestRender = () => viewer.scene.requestRender(), - viewRectangle = () => viewer.camera?.computeViewRectangle?.(viewer.scene.globe?.ellipsoid), + viewRectangle = () => + viewer.camera?.computeViewRectangle?.(viewer.scene.globe?.ellipsoid), moveEnd = viewer?.camera?.moveEnd, }) { if (!Object.hasOwn(WEATHER_IMAGE_SIZES, product)) @@ -572,9 +573,7 @@ export function createWeatherShell({ } /** Recompute the window from the camera's ground footprint; returns whether it changed. */ function computeView() { - const rectangle = detailed - ? viewRectangle() - : null; + const rectangle = detailed ? viewRectangle() : null; const degrees = cesium.Math.toDegrees; const footprint = rectangle && { west: degrees(rectangle.west), diff --git a/src/layers/wind/index.js b/src/layers/wind/index.js index 1efbfcd16..89c0e1a8d 100644 --- a/src/layers/wind/index.js +++ b/src/layers/wind/index.js @@ -85,7 +85,10 @@ export function createWindLayer({ const sample = (position) => { if (!position && engine && !viewer) { const canvas = engine.host.canvas; - position = engine.camera.pickEllipsoid({ x: canvas.clientWidth / 2, y: canvas.clientHeight / 2 }); + position = engine.camera.pickEllipsoid({ + x: canvas.clientWidth / 2, + y: canvas.clientHeight / 2, + }); } reading = inspectWindAtCenter(manifest, viewer, cesium, { position, @@ -122,20 +125,25 @@ export function createWindLayer({ attachRenderer(nextViewer, { engine: nextEngine = null } = {}) { viewer = nextViewer; engine = nextEngine; - rendering = engine?.kind === 'navara' ? createNativeRendering(engine) : createRendering({ - cesium, - container: container ?? nextViewer.container, - getViewer: () => viewer, - getHost: () => - imageryHost?.() ?? { - collection: viewer.imageryLayers ?? viewer.scene?.imageryLayers, - kind: 'globe', - }, - onStatusChange: notify, - }); + rendering = + engine?.kind === 'navara' + ? createNativeRendering(engine) + : createRendering({ + cesium, + container: container ?? nextViewer.container, + getViewer: () => viewer, + getHost: () => + imageryHost?.() ?? { + collection: + viewer.imageryLayers ?? viewer.scene?.imageryLayers, + kind: 'globe', + }, + onStatusChange: notify, + }); rendering.attach(); rendering.setOptions?.({ overlay, paused }); - const target = container ?? nextViewer?.container ?? engine?.host.container; + const target = + container ?? nextViewer?.container ?? engine?.host.container; inspectionMarker = createWindInspectionMarker({ container: target, viewer, diff --git a/src/layers/wind/inspectionMarker.js b/src/layers/wind/inspectionMarker.js index 704e89ea0..b08be5fba 100644 --- a/src/layers/wind/inspectionMarker.js +++ b/src/layers/wind/inspectionMarker.js @@ -44,10 +44,12 @@ export function createWindInspectionMarker({ ) return; present({ owner, container, visible: false, left: 0, top: 0 }); - const occluder = !engine && new cesium.EllipsoidalOccluder( - scene.globe?.ellipsoid || cesium.Ellipsoid.WGS84, - viewer.camera.positionWC, - ); + const occluder = + !engine && + new cesium.EllipsoidalOccluder( + scene.globe?.ellipsoid || cesium.Ellipsoid.WGS84, + viewer.camera.positionWC, + ); const render = () => { if (!shown()) return; if (occluder) occluder.cameraPosition = viewer.camera.positionWC; @@ -60,8 +62,10 @@ export function createWindInspectionMarker({ point.y >= 0 && point.x <= canvas.clientWidth && point.y <= canvas.clientHeight && - (engine ? engine.projector.horizonVisible(position) : - scene.mode !== cesium.SceneMode.SCENE3D || occluder.isPointVisible(position)); + (engine + ? engine.projector.horizonVisible(position) + : scene.mode !== cesium.SceneMode.SCENE3D || + occluder.isPointVisible(position)); if (!visible) { // A hidden marker keeps its last place, as the element did. if (marker.visible) present({ visible: false }); diff --git a/src/scenes/dataPacks/presentation.js b/src/scenes/dataPacks/presentation.js index feb99b751..563ad2121 100644 --- a/src/scenes/dataPacks/presentation.js +++ b/src/scenes/dataPacks/presentation.js @@ -30,10 +30,7 @@ export function createPackPresentations( let key, cards = []; function present() { - store.publish( - owner, - cards.length ? { key, container, cards } : null, - ); + store.publish(owner, cards.length ? { key, container, cards } : null); } function addCard(pack) { if (!cards.length) key = ++panelKeys; @@ -205,27 +202,35 @@ export function createPackPresentations( signal.throwIfAborted(); if (image.naturalWidth * image.naturalHeight > 16 * 1024 * 1024) throw new Error('Image dimensions exceed limit'); - handle.display(native => native ? native.image(pack,image,handle) : handle.primitive( - new Cesium.Primitive({ - asynchronous: false, - geometryInstances: new Cesium.GeometryInstance({ - id: { packId: pack.id }, - geometry: new Cesium.RectangleGeometry({ - rectangle: Cesium.Rectangle.fromDegrees(...pack.placement.bounds), - height: pack.placement.height, - vertexFormat: - Cesium.MaterialAppearance.MaterialSupport.TEXTURED.vertexFormat, - }), - }), - appearance: new Cesium.MaterialAppearance({ - material: Cesium.Material.fromType('Image', { image }), - materialSupport: Cesium.MaterialAppearance.MaterialSupport.TEXTURED, - flat: true, - faceForward: true, - translucent: true, - }), - }), - )); + handle.display((native) => + native + ? native.image(pack, image, handle) + : handle.primitive( + new Cesium.Primitive({ + asynchronous: false, + geometryInstances: new Cesium.GeometryInstance({ + id: { packId: pack.id }, + geometry: new Cesium.RectangleGeometry({ + rectangle: Cesium.Rectangle.fromDegrees( + ...pack.placement.bounds, + ), + height: pack.placement.height, + vertexFormat: + Cesium.MaterialAppearance.MaterialSupport.TEXTURED + .vertexFormat, + }), + }), + appearance: new Cesium.MaterialAppearance({ + material: Cesium.Material.fromType('Image', { image }), + materialSupport: + Cesium.MaterialAppearance.MaterialSupport.TEXTURED, + flat: true, + faceForward: true, + translucent: true, + }), + }), + ), + ); }), media: guarded(({ asset, pack, anchors }, handle) => { if ( @@ -243,16 +248,20 @@ export function createPackPresentations( (item) => item.id === pack.placement.anchorId, ); if (!anchor) throw new Error('Missing media anchor'); - handle.display(native => native ? native.media(pack,anchor,handle) : handle.add({ - name: pack.id, - position: xyz([anchor.lon, anchor.lat, anchor.alt]), - point: { pixelSize: 16, color: Cesium.Color.YELLOW }, - label: { - text: pack.id, - font: '14px sans-serif', - pixelOffset: new Cesium.Cartesian2(0, -24), - }, - })); + handle.display((native) => + native + ? native.media(pack, anchor, handle) + : handle.add({ + name: pack.id, + position: xyz([anchor.lon, anchor.lat, anchor.alt]), + point: { pixelSize: 16, color: Cesium.Color.YELLOW }, + label: { + text: pack.id, + font: '14px sans-serif', + pixelOffset: new Cesium.Cartesian2(0, -24), + }, + }), + ); handle.media({ kind: asset.mimeType.startsWith('video/') ? 'video' : 'audio', src: handle.url(new Blob([asset.bytes], { type: asset.mimeType })), diff --git a/src/ui/contextControls.js b/src/ui/contextControls.js index 6105d107f..ad7aab9f9 100644 --- a/src/ui/contextControls.js +++ b/src/ui/contextControls.js @@ -86,9 +86,13 @@ export class ContextControls { } } canRetainScene() { - return ['flights', 'space-missions'].includes(this._contextMode) && !this._contextModeChanging && - !this._contextRestoreState && !this._clearSelectedLayersPromise && - !this._contextLayerReactionPromises.size; + return ( + ['flights', 'space-missions'].includes(this._contextMode) && + !this._contextModeChanging && + !this._contextRestoreState && + !this._clearSelectedLayersPromise && + !this._contextLayerReactionPromises.size + ); } captureScene() { return { mode: this._contextMode, snapshot: this._contextSessionSnapshot }; diff --git a/src/ui/shareRestoration.js b/src/ui/shareRestoration.js index 0dd1e2a31..67d508805 100644 --- a/src/ui/shareRestoration.js +++ b/src/ui/shareRestoration.js @@ -176,7 +176,8 @@ export class ShareRestoration { this._initialShareRestoreTimeout = null; if (this._disposed) return; const generation = this._initialShareNavigationGeneration; - const retained = engineSwitch && this._layerStateCoordinator?.retainedTrackingTarget(); + const retained = + engineSwitch && this._layerStateCoordinator?.retainedTrackingTarget(); const applyCamera = Number.isInteger(generation) && this.navigation._reassertNavigationHandoff(generation, { @@ -246,7 +247,15 @@ export class ShareRestoration { */ async _runShareRestore( state, - { applyCamera, instant = false, retained, retainScene = false, navigationToken, layersPromise, settle }, + { + applyCamera, + instant = false, + retained, + retainScene = false, + navigationToken, + layersPromise, + settle, + }, ) { const generation = this._shareRestoreGeneration; const current = () => @@ -259,11 +268,13 @@ export class ShareRestoration { }); const layers = await (layersPromise || Promise.resolve([])); if (!current()) return; - if (retainScene && !retained) retained = this._layerStateCoordinator?.retainedTrackingTarget(); + if (retainScene && !retained) + retained = this._layerStateCoordinator?.retainedTrackingTarget(); const tracking = share.camera === 'applied' - ? (retained && this._layerStateCoordinator.resumeRetainedTracking(retained)) || - await this._layerStateCoordinator?.restoreShareTrackingSelection?.() + ? (retained && + this._layerStateCoordinator.resumeRetainedTracking(retained)) || + (await this._layerStateCoordinator?.restoreShareTrackingSelection?.()) : { status: 'superseded', cleared: