mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-06 11:24:00 +00:00
Merge pull request #492 from bilawalsidhu/refactor/construct-application-layers
refactor: construct application layers from supplied sources
This commit is contained in:
53 files changed
+1146
-394
No files matched your search
@@ -91,6 +91,8 @@ of current runtime behavior, see [`docs/CURRENT-STATE.md`](docs/CURRENT-STATE.md
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
- Construct application layers from explicit sources, with standalone provider selection and catalog-owned aircraft classification; controls and voice queries use those instances.
|
||||||
|
|
||||||
- Let application data and controls receive the same explicit layer catalog; keep standalone defaults and registration-before-restoration ordering.
|
- Let application data and controls receive the same explicit layer catalog; keep standalone defaults and registration-before-restoration ordering.
|
||||||
|
|
||||||
- Expose existing FIRMS CSV parsing and UTC time-window helpers through a portable package export, with shared contract fixtures.
|
- Expose existing FIRMS CSV parsing and UTC time-window helpers through a portable package export, with shared contract fixtures.
|
||||||
|
|||||||
@@ -409,3 +409,24 @@ that schema requires a corresponding codec change.
|
|||||||
`standalone/catalog` selects the existing page-scoped default instances and metadata.
|
`standalone/catalog` selects the existing page-scoped default instances and metadata.
|
||||||
Reusable data setup imports no standalone layer defaults. The current compatibility
|
Reusable data setup imports no standalone layer defaults. The current compatibility
|
||||||
source setters remain available while callers migrate to instance construction.
|
source setters remain available while callers migrate to instance construction.
|
||||||
|
|
||||||
|
### Layer construction
|
||||||
|
|
||||||
|
`application/layers` constructs the current catalog from explicit source objects
|
||||||
|
and an application AbortSignal. Small `src/app/layers` modules wire existing scene
|
||||||
|
services into each family factory. Standalone provider selection lives in
|
||||||
|
`src/standalone/layerSources.js`. The construction export has its own checked
|
||||||
|
dependency graph, excluding standalone setup and compatibility layer instances.
|
||||||
|
|
||||||
|
Both aircraft layers share the catalog's classification registry; launches use
|
||||||
|
its satellites and Contacts uses its aircraft, vessels and installations. Data
|
||||||
|
registration, controls and voice actions read those same instances. Destruction
|
||||||
|
remains the manager's responsibility; classification also observes application
|
||||||
|
abort when startup has not reached registration. Scene engines remain page-owned,
|
||||||
|
so this change does not introduce multiple simultaneous viewers.
|
||||||
|
|
||||||
|
Direct `src/data` compatibility entries retain their old defaults and testing
|
||||||
|
exports. Browser regression probes use the registered instance's testing surface
|
||||||
|
to avoid accidentally inspecting an unused compatibility instance. Existing source
|
||||||
|
setters apply only to compatibility instances; the normal application supplies
|
||||||
|
its sources at construction.
|
||||||
@@ -1,5 +1,13 @@
|
|||||||
# God's Eye View Current State
|
# God's Eye View Current State
|
||||||
|
|
||||||
|
Application startup constructs fresh layer instances from explicit source objects.
|
||||||
|
Standalone composition selects the existing providers. Controls, launch orbit
|
||||||
|
lookups, Contacts voice answers and ISS pass queries use the registered catalog.
|
||||||
|
Aircraft classification is catalog-owned and cancelled with application teardown;
|
||||||
|
scene engines still support one application per page. Compatibility data entrypoints
|
||||||
|
remain for direct callers, but normal startup does not configure global sources.
|
||||||
|
|
||||||
|
|
||||||
Application data setup receives an ordered layer catalog and serialization metadata.
|
Application data setup receives an ordered layer catalog and serialization metadata.
|
||||||
Controls use the same instances. The standalone composition selects the existing
|
Controls use the same instances. The standalone composition selects the existing
|
||||||
default catalog, and restoration starts only after all registrations are sealed.
|
default catalog, and restoration starts only after all registrations are sealed.
|
||||||
|
|||||||
+2
-1
@@ -193,6 +193,7 @@
|
|||||||
"node": "./build/application-html.js"
|
"node": "./build/application-html.js"
|
||||||
},
|
},
|
||||||
"./application/catalog": "./src/app/catalog.js",
|
"./application/catalog": "./src/app/catalog.js",
|
||||||
"./standalone/catalog": "./src/standalone/catalog.js"
|
"./standalone/catalog": "./src/standalone/catalog.js",
|
||||||
|
"./application/layers": "./src/app/constructCatalog.js"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -246,7 +246,8 @@ if [[ ! -f "$SOURCE_ROOT/src/data/cctv.js" ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! grep -q "^[[:space:]]*cctvLayer," "$SOURCE_ROOT/src/standalone/catalog.js"; then
|
if ! grep -q "return createApplicationCatalog(" "$SOURCE_ROOT/src/standalone/catalog.js" || \
|
||||||
|
! grep -q "^[[:space:]]*createApplicationCctv({" "$SOURCE_ROOT/src/app/constructCatalog.js"; then
|
||||||
echo "error: CCTV layer not wired in src/standalone/catalog.js"
|
echo "error: CCTV layer not wired in src/standalone/catalog.js"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -558,5 +558,24 @@
|
|||||||
"src/toolProjectRoot.test.mjs",
|
"src/toolProjectRoot.test.mjs",
|
||||||
"src/app/catalog.js",
|
"src/app/catalog.js",
|
||||||
"src/standalone/catalog.js",
|
"src/standalone/catalog.js",
|
||||||
"src/app/catalog.test.mjs"
|
"src/app/catalog.test.mjs",
|
||||||
|
"src/app/layers/militaryInstallations.js",
|
||||||
|
"src/app/layers/alprCameras.js",
|
||||||
|
"src/app/layers/traffic.js",
|
||||||
|
"src/app/layers/submarineCables.js",
|
||||||
|
"src/app/layers/militaryFlights.js",
|
||||||
|
"src/app/layers/bikeshare.js",
|
||||||
|
"src/app/layers/flights.js",
|
||||||
|
"src/app/layers/radio.js",
|
||||||
|
"src/app/layers/earthquakes.js",
|
||||||
|
"src/app/layers/cctv.js",
|
||||||
|
"src/app/layers/firms.js",
|
||||||
|
"src/app/layers/satellites.js",
|
||||||
|
"src/app/layers/militaryAwareness.js",
|
||||||
|
"src/app/layers/rocketLaunches.js",
|
||||||
|
"src/app/layers/overlayHost.js",
|
||||||
|
"src/app/layers/aisLiveVessels.js",
|
||||||
|
"src/app/constructCatalog.js",
|
||||||
|
"src/standalone/layerSources.js",
|
||||||
|
"src/app/constructCatalog.test.mjs"
|
||||||
]
|
]
|
||||||
@@ -1503,7 +1503,25 @@
|
|||||||
"src/weatherEffectsMath.js",
|
"src/weatherEffectsMath.js",
|
||||||
"src/worldFocus.js",
|
"src/worldFocus.js",
|
||||||
"src/app/catalog.js",
|
"src/app/catalog.js",
|
||||||
"src/standalone/catalog.js"
|
"src/standalone/catalog.js",
|
||||||
|
"src/app/layers/militaryInstallations.js",
|
||||||
|
"src/app/layers/alprCameras.js",
|
||||||
|
"src/app/layers/traffic.js",
|
||||||
|
"src/app/layers/submarineCables.js",
|
||||||
|
"src/app/layers/militaryFlights.js",
|
||||||
|
"src/app/layers/bikeshare.js",
|
||||||
|
"src/app/layers/flights.js",
|
||||||
|
"src/app/layers/radio.js",
|
||||||
|
"src/app/layers/earthquakes.js",
|
||||||
|
"src/app/layers/cctv.js",
|
||||||
|
"src/app/layers/firms.js",
|
||||||
|
"src/app/layers/satellites.js",
|
||||||
|
"src/app/layers/militaryAwareness.js",
|
||||||
|
"src/app/layers/rocketLaunches.js",
|
||||||
|
"src/app/layers/overlayHost.js",
|
||||||
|
"src/app/layers/aisLiveVessels.js",
|
||||||
|
"src/app/constructCatalog.js",
|
||||||
|
"src/standalone/layerSources.js"
|
||||||
],
|
],
|
||||||
"external": [
|
"external": [
|
||||||
"@mapbox/vector-tile",
|
"@mapbox/vector-tile",
|
||||||
@@ -1518,5 +1536,328 @@
|
|||||||
"exports": ["./application/catalog"],
|
"exports": ["./application/catalog"],
|
||||||
"modules": ["src/app/catalog.js"],
|
"modules": ["src/app/catalog.js"],
|
||||||
"external": []
|
"external": []
|
||||||
|
},
|
||||||
|
"application-layer-construction": {
|
||||||
|
"exports": ["./application/layers"],
|
||||||
|
"modules": [
|
||||||
|
"src/annotations/annotationResolver.js",
|
||||||
|
"src/app/catalog.js",
|
||||||
|
"src/app/constructCatalog.js",
|
||||||
|
"src/app/layers/aisLiveVessels.js",
|
||||||
|
"src/app/layers/alprCameras.js",
|
||||||
|
"src/app/layers/bikeshare.js",
|
||||||
|
"src/app/layers/cctv.js",
|
||||||
|
"src/app/layers/earthquakes.js",
|
||||||
|
"src/app/layers/firms.js",
|
||||||
|
"src/app/layers/flights.js",
|
||||||
|
"src/app/layers/militaryAwareness.js",
|
||||||
|
"src/app/layers/militaryFlights.js",
|
||||||
|
"src/app/layers/militaryInstallations.js",
|
||||||
|
"src/app/layers/overlayHost.js",
|
||||||
|
"src/app/layers/radio.js",
|
||||||
|
"src/app/layers/rocketLaunches.js",
|
||||||
|
"src/app/layers/satellites.js",
|
||||||
|
"src/app/layers/submarineCables.js",
|
||||||
|
"src/app/layers/traffic.js",
|
||||||
|
"src/app/sourceSlot.js",
|
||||||
|
"src/cctvFocusRequest.js",
|
||||||
|
"src/celestialRing.js",
|
||||||
|
"src/cockpitMath.js",
|
||||||
|
"src/data/aircraftClass.js",
|
||||||
|
"src/data/aircraftIcons.js",
|
||||||
|
"src/data/aircraftMeta.js",
|
||||||
|
"src/data/aircraftNearbyPolicy.js",
|
||||||
|
"src/data/aircraftRecession.js",
|
||||||
|
"src/data/cctvCards.js",
|
||||||
|
"src/data/cctvFootprint.js",
|
||||||
|
"src/data/cctvGizmo.js",
|
||||||
|
"src/data/cctvLod.js",
|
||||||
|
"src/data/cctvViewshed.js",
|
||||||
|
"src/data/cockpitAirLod.js",
|
||||||
|
"src/data/cockpitContactDot.js",
|
||||||
|
"src/data/contactMatch.js",
|
||||||
|
"src/data/contextStore.js",
|
||||||
|
"src/data/dataCredits.js",
|
||||||
|
"src/data/detectionCohort.js",
|
||||||
|
"src/data/detectionDraw.js",
|
||||||
|
"src/data/detectionPolicy.js",
|
||||||
|
"src/data/fireAnchors.js",
|
||||||
|
"src/data/firmsAdapt.js",
|
||||||
|
"src/data/firmsLabels.js",
|
||||||
|
"src/data/flowMatch.js",
|
||||||
|
"src/data/focusDeemphasis.js",
|
||||||
|
"src/data/geoid.js",
|
||||||
|
"src/data/groundFloor.js",
|
||||||
|
"src/data/groundSnap.js",
|
||||||
|
"src/data/iconOrientation.js",
|
||||||
|
"src/data/infrastructure.js",
|
||||||
|
"src/data/installationFeedback.js",
|
||||||
|
"src/data/issPass.js",
|
||||||
|
"src/data/labelArbiter.js",
|
||||||
|
"src/data/layerState.js",
|
||||||
|
"src/data/localGeojson.js",
|
||||||
|
"src/data/localGeojsonCore.js",
|
||||||
|
"src/data/localGeojsonLod.js",
|
||||||
|
"src/data/local_data/natural_earth/marine.json",
|
||||||
|
"src/data/local_data/natural_earth/regions.json",
|
||||||
|
"src/data/local_data/neighborhoods/san-francisco.json",
|
||||||
|
"src/data/meshFloorSampler.js",
|
||||||
|
"src/data/militaryAwarenessEngine.js",
|
||||||
|
"src/data/militaryInstallationData.js",
|
||||||
|
"src/data/modelVisualAnchor.js",
|
||||||
|
"src/data/motionModel.js",
|
||||||
|
"src/data/naturalEarthRegions.js",
|
||||||
|
"src/data/neighborhoodPolygons.js",
|
||||||
|
"src/data/pickRegistry.js",
|
||||||
|
"src/data/radioCountry.js",
|
||||||
|
"src/data/renderAltitude.js",
|
||||||
|
"src/data/retryableLoad.js",
|
||||||
|
"src/data/routePlausible.js",
|
||||||
|
"src/data/satelliteClass.js",
|
||||||
|
"src/data/scenePick.js",
|
||||||
|
"src/data/spriteOrder.js",
|
||||||
|
"src/data/terrainHeights.js",
|
||||||
|
"src/data/tomtomTiles.js",
|
||||||
|
"src/data/tr3bRegistry.js",
|
||||||
|
"src/data/trackedCamera.js",
|
||||||
|
"src/data/trackedModelRegime.js",
|
||||||
|
"src/data/trackedReadout.js",
|
||||||
|
"src/data/trackingClickGesture.js",
|
||||||
|
"src/data/trafficBounds.js",
|
||||||
|
"src/data/trafficFlowStyle.js",
|
||||||
|
"src/data/trafficPresetStyle.js",
|
||||||
|
"src/data/trafficQueue.js",
|
||||||
|
"src/data/trailRenderer.js",
|
||||||
|
"src/data/vesselLabels.js",
|
||||||
|
"src/keylessGeocoder.js",
|
||||||
|
"src/layers/aircraft/classification.js",
|
||||||
|
"src/layers/alpr/index.js",
|
||||||
|
"src/layers/alpr/model.js",
|
||||||
|
"src/layers/alpr/policy.js",
|
||||||
|
"src/layers/alpr/presentation.js",
|
||||||
|
"src/layers/alpr/source.js",
|
||||||
|
"src/layers/awareness/controls.js",
|
||||||
|
"src/layers/awareness/dependencies.js",
|
||||||
|
"src/layers/awareness/focus.js",
|
||||||
|
"src/layers/awareness/history.js",
|
||||||
|
"src/layers/awareness/index.js",
|
||||||
|
"src/layers/awareness/ingestion.js",
|
||||||
|
"src/layers/awareness/lifecycle.js",
|
||||||
|
"src/layers/awareness/model.js",
|
||||||
|
"src/layers/awareness/navigation.js",
|
||||||
|
"src/layers/awareness/panel.js",
|
||||||
|
"src/layers/awareness/policy.js",
|
||||||
|
"src/layers/awareness/queries.js",
|
||||||
|
"src/layers/awareness/rendering.js",
|
||||||
|
"src/layers/awareness/state.js",
|
||||||
|
"src/layers/awareness/subject.js",
|
||||||
|
"src/layers/awareness/testing.js",
|
||||||
|
"src/layers/bikeshare/controls.js",
|
||||||
|
"src/layers/bikeshare/index.js",
|
||||||
|
"src/layers/bikeshare/ingestion.js",
|
||||||
|
"src/layers/bikeshare/lifecycle.js",
|
||||||
|
"src/layers/bikeshare/model.js",
|
||||||
|
"src/layers/bikeshare/policy.js",
|
||||||
|
"src/layers/bikeshare/queries.js",
|
||||||
|
"src/layers/bikeshare/registry.js",
|
||||||
|
"src/layers/bikeshare/rendering.js",
|
||||||
|
"src/layers/bikeshare/selection.js",
|
||||||
|
"src/layers/bikeshare/source.js",
|
||||||
|
"src/layers/bikeshare/state.js",
|
||||||
|
"src/layers/bikeshare/testing.js",
|
||||||
|
"src/layers/bikeshare/viewport.js",
|
||||||
|
"src/layers/cctv/calibration.js",
|
||||||
|
"src/layers/cctv/cards.js",
|
||||||
|
"src/layers/cctv/catalog.js",
|
||||||
|
"src/layers/cctv/controls.js",
|
||||||
|
"src/layers/cctv/frames.js",
|
||||||
|
"src/layers/cctv/geometry.js",
|
||||||
|
"src/layers/cctv/geometryQueue.js",
|
||||||
|
"src/layers/cctv/ground.js",
|
||||||
|
"src/layers/cctv/health.js",
|
||||||
|
"src/layers/cctv/hover.js",
|
||||||
|
"src/layers/cctv/index.js",
|
||||||
|
"src/layers/cctv/ingestion.js",
|
||||||
|
"src/layers/cctv/lifecycle.js",
|
||||||
|
"src/layers/cctv/model.js",
|
||||||
|
"src/layers/cctv/navigation.js",
|
||||||
|
"src/layers/cctv/policy.js",
|
||||||
|
"src/layers/cctv/presentation.js",
|
||||||
|
"src/layers/cctv/projection.js",
|
||||||
|
"src/layers/cctv/rendering.js",
|
||||||
|
"src/layers/cctv/selection.js",
|
||||||
|
"src/layers/cctv/source.js",
|
||||||
|
"src/layers/cctv/state.js",
|
||||||
|
"src/layers/cctv/testing.js",
|
||||||
|
"src/layers/earthquakes/index.js",
|
||||||
|
"src/layers/earthquakes/model.js",
|
||||||
|
"src/layers/earthquakes/source.js",
|
||||||
|
"src/layers/firms/anchors.js",
|
||||||
|
"src/layers/firms/cards.js",
|
||||||
|
"src/layers/firms/index.js",
|
||||||
|
"src/layers/firms/ingestion.js",
|
||||||
|
"src/layers/firms/lifecycle.js",
|
||||||
|
"src/layers/firms/model.js",
|
||||||
|
"src/layers/firms/policy.js",
|
||||||
|
"src/layers/firms/queries.js",
|
||||||
|
"src/layers/firms/rendering.js",
|
||||||
|
"src/layers/firms/selection.js",
|
||||||
|
"src/layers/firms/source.js",
|
||||||
|
"src/layers/firms/state.js",
|
||||||
|
"src/layers/firms/viewport.js",
|
||||||
|
"src/layers/flights/controller.js",
|
||||||
|
"src/layers/flights/enrichment.js",
|
||||||
|
"src/layers/flights/evidence.js",
|
||||||
|
"src/layers/flights/index.js",
|
||||||
|
"src/layers/flights/ingestion.js",
|
||||||
|
"src/layers/flights/lifecycle.js",
|
||||||
|
"src/layers/flights/motion.js",
|
||||||
|
"src/layers/flights/policy.js",
|
||||||
|
"src/layers/flights/queries.js",
|
||||||
|
"src/layers/flights/rendering.js",
|
||||||
|
"src/layers/flights/state.js",
|
||||||
|
"src/layers/flights/testing.js",
|
||||||
|
"src/layers/flights/tracking.js",
|
||||||
|
"src/layers/installations/controls.js",
|
||||||
|
"src/layers/installations/index.js",
|
||||||
|
"src/layers/installations/ingestion.js",
|
||||||
|
"src/layers/installations/lifecycle.js",
|
||||||
|
"src/layers/installations/model.js",
|
||||||
|
"src/layers/installations/policy.js",
|
||||||
|
"src/layers/installations/rendering.js",
|
||||||
|
"src/layers/installations/selection.js",
|
||||||
|
"src/layers/installations/source.js",
|
||||||
|
"src/layers/installations/state.js",
|
||||||
|
"src/layers/installations/viewport.js",
|
||||||
|
"src/layers/launches/camera.js",
|
||||||
|
"src/layers/launches/controls.js",
|
||||||
|
"src/layers/launches/index.js",
|
||||||
|
"src/layers/launches/ingestion.js",
|
||||||
|
"src/layers/launches/launchPad.js",
|
||||||
|
"src/layers/launches/lifecycle.js",
|
||||||
|
"src/layers/launches/model.js",
|
||||||
|
"src/layers/launches/orbitRendering.js",
|
||||||
|
"src/layers/launches/overlays.js",
|
||||||
|
"src/layers/launches/panel.js",
|
||||||
|
"src/layers/launches/paths.js",
|
||||||
|
"src/layers/launches/policy.js",
|
||||||
|
"src/layers/launches/policyHelpers.js",
|
||||||
|
"src/layers/launches/rendering.js",
|
||||||
|
"src/layers/launches/replay.js",
|
||||||
|
"src/layers/launches/selection.js",
|
||||||
|
"src/layers/launches/source.js",
|
||||||
|
"src/layers/launches/state.js",
|
||||||
|
"src/layers/launches/testing.js",
|
||||||
|
"src/layers/military/controller.js",
|
||||||
|
"src/layers/military/index.js",
|
||||||
|
"src/layers/military/ingestion.js",
|
||||||
|
"src/layers/military/lifecycle.js",
|
||||||
|
"src/layers/military/motion.js",
|
||||||
|
"src/layers/military/policy.js",
|
||||||
|
"src/layers/military/queries.js",
|
||||||
|
"src/layers/military/rendering.js",
|
||||||
|
"src/layers/military/state.js",
|
||||||
|
"src/layers/military/testing.js",
|
||||||
|
"src/layers/military/tracking.js",
|
||||||
|
"src/layers/radio/catalogModel.js",
|
||||||
|
"src/layers/radio/categories.js",
|
||||||
|
"src/layers/radio/clustering.js",
|
||||||
|
"src/layers/radio/controls.js",
|
||||||
|
"src/layers/radio/index.js",
|
||||||
|
"src/layers/radio/ingestion.js",
|
||||||
|
"src/layers/radio/interaction.js",
|
||||||
|
"src/layers/radio/labels.js",
|
||||||
|
"src/layers/radio/lifecycle.js",
|
||||||
|
"src/layers/radio/model.js",
|
||||||
|
"src/layers/radio/navigation.js",
|
||||||
|
"src/layers/radio/playback.js",
|
||||||
|
"src/layers/radio/policy.js",
|
||||||
|
"src/layers/radio/presentation.js",
|
||||||
|
"src/layers/radio/queries.js",
|
||||||
|
"src/layers/radio/rendering.js",
|
||||||
|
"src/layers/radio/selection.js",
|
||||||
|
"src/layers/radio/source.js",
|
||||||
|
"src/layers/radio/state.js",
|
||||||
|
"src/layers/radio/tuning.js",
|
||||||
|
"src/layers/radio/tuningNoise.js",
|
||||||
|
"src/layers/radio/volume.js",
|
||||||
|
"src/layers/satellites/catalog.js",
|
||||||
|
"src/layers/satellites/controls.js",
|
||||||
|
"src/layers/satellites/index.js",
|
||||||
|
"src/layers/satellites/ingestion.js",
|
||||||
|
"src/layers/satellites/interaction.js",
|
||||||
|
"src/layers/satellites/labels.js",
|
||||||
|
"src/layers/satellites/lifecycle.js",
|
||||||
|
"src/layers/satellites/orbits.js",
|
||||||
|
"src/layers/satellites/policy.js",
|
||||||
|
"src/layers/satellites/rendering.js",
|
||||||
|
"src/layers/satellites/source.js",
|
||||||
|
"src/layers/satellites/state.js",
|
||||||
|
"src/layers/satellites/testing.js",
|
||||||
|
"src/layers/satellites/tracking.js",
|
||||||
|
"src/layers/submarineCables/geometry.js",
|
||||||
|
"src/layers/submarineCables/index.js",
|
||||||
|
"src/layers/submarineCables/ingestion.js",
|
||||||
|
"src/layers/submarineCables/interaction.js",
|
||||||
|
"src/layers/submarineCables/lifecycle.js",
|
||||||
|
"src/layers/submarineCables/overlay.js",
|
||||||
|
"src/layers/submarineCables/policy.js",
|
||||||
|
"src/layers/submarineCables/rendering.js",
|
||||||
|
"src/layers/submarineCables/state.js",
|
||||||
|
"src/layers/submarineCables/surface.js",
|
||||||
|
"src/layers/traffic/animation.js",
|
||||||
|
"src/layers/traffic/controls.js",
|
||||||
|
"src/layers/traffic/flow.js",
|
||||||
|
"src/layers/traffic/flowDecode.js",
|
||||||
|
"src/layers/traffic/flowSource.js",
|
||||||
|
"src/layers/traffic/index.js",
|
||||||
|
"src/layers/traffic/ingestion.js",
|
||||||
|
"src/layers/traffic/lifecycle.js",
|
||||||
|
"src/layers/traffic/model.js",
|
||||||
|
"src/layers/traffic/policy.js",
|
||||||
|
"src/layers/traffic/rendering.js",
|
||||||
|
"src/layers/traffic/source.js",
|
||||||
|
"src/layers/traffic/state.js",
|
||||||
|
"src/layers/traffic/style.js",
|
||||||
|
"src/layers/traffic/timing.js",
|
||||||
|
"src/layers/traffic/viewport.js",
|
||||||
|
"src/layers/vessels/cards.js",
|
||||||
|
"src/layers/vessels/evidence.js",
|
||||||
|
"src/layers/vessels/index.js",
|
||||||
|
"src/layers/vessels/ingestion.js",
|
||||||
|
"src/layers/vessels/lifecycle.js",
|
||||||
|
"src/layers/vessels/policy.js",
|
||||||
|
"src/layers/vessels/queries.js",
|
||||||
|
"src/layers/vessels/rendering.js",
|
||||||
|
"src/layers/vessels/selection.js",
|
||||||
|
"src/layers/vessels/state.js",
|
||||||
|
"src/layers/vessels/store.js",
|
||||||
|
"src/layers/vessels/testing.js",
|
||||||
|
"src/layers/vessels/tracking.js",
|
||||||
|
"src/locations.js",
|
||||||
|
"src/navigationPolicy.js",
|
||||||
|
"src/overlays/worldOverlay.js",
|
||||||
|
"src/overlays/worldOverlayDraw.js",
|
||||||
|
"src/overlays/worldOverlayTokens.js",
|
||||||
|
"src/renderGovernor.js",
|
||||||
|
"src/search/defaults.js",
|
||||||
|
"src/search/geospatial.js",
|
||||||
|
"src/search/google.js",
|
||||||
|
"src/search/http.js",
|
||||||
|
"src/search/placeSearch.js",
|
||||||
|
"src/services/application.js",
|
||||||
|
"src/services/requests.js",
|
||||||
|
"src/ui/panelRailGeometry.js",
|
||||||
|
"src/ui/radioTunerModel.js",
|
||||||
|
"src/worldFocus.js"
|
||||||
|
],
|
||||||
|
"external": [
|
||||||
|
"cesium",
|
||||||
|
"egm96-universal",
|
||||||
|
"satellite.js",
|
||||||
|
"@mapbox/vector-tile",
|
||||||
|
"pbf",
|
||||||
|
"mgrs"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3813,27 +3813,11 @@ async function main() {
|
|||||||
const Cesium = await import('/node_modules/cesium/Build/Cesium/index.js');
|
const Cesium = await import('/node_modules/cesium/Build/Cesium/index.js');
|
||||||
const v = window.__godsEyeView.viewer;
|
const v = window.__godsEyeView.viewer;
|
||||||
const fl = window.__godsEyeView.dataManager.layers.get('flights').module;
|
const fl = window.__godsEyeView.dataManager.layers.get('flights').module;
|
||||||
// `.module` is the layer OBJECT (the default export), not the module
|
// Test the registered instance directly, including catalogs constructed
|
||||||
// namespace, so the handoff seam is not on it. Reach the namespace the
|
// with their own sources. Never import a second compatibility instance.
|
||||||
// same way this group reaches groundFloor's: offer the URL the app
|
const ns = fl.testing;
|
||||||
// itself loaded (Vite serves an edited file as `…?t=<hmr stamp>`, and
|
if (typeof ns?._driveFleetModelHandoffForTest !== 'function')
|
||||||
// the plain path would hand back a second, unrelated instance whose
|
return { error: "the registered flights instance has no handoff test seam" };
|
||||||
// module state the app never touches), then prove identity by requiring
|
|
||||||
// its default export to BE the live layer object.
|
|
||||||
let ns = null;
|
|
||||||
const urls = [...new Set([
|
|
||||||
...performance.getEntriesByType('resource').map((e) => e.name)
|
|
||||||
.filter((n) => /\/src\/data\/flights\.js(\?|$)/.test(n)).reverse(),
|
|
||||||
`${window.__gevQaSourceBase || '/src'}/data/flights.js`,
|
|
||||||
])];
|
|
||||||
for (const url of urls) {
|
|
||||||
let mod; try { mod = await import(/* @vite-ignore */ url); } catch { continue; }
|
|
||||||
if (mod?.default === fl && typeof mod._driveFleetModelHandoffForTest === 'function') {
|
|
||||||
ns = mod;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!ns) return { skipped: `the app's own flights module was not reachable (tried ${urls.length})` };
|
|
||||||
// Use a scenario-owned contact so its groundSnap entry is provably cold;
|
// Use a scenario-owned contact so its groundSnap entry is provably cold;
|
||||||
// earlier display-floor cases intentionally exercise aaa097's cache.
|
// earlier display-floor cases intentionally exercise aaa097's cache.
|
||||||
const holdIcao = 'aaa098';
|
const holdIcao = 'aaa098';
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
import { createLayerCatalog } from './catalog.js';
|
||||||
|
import { LAYER_STATE_REGISTRY } from '../data/layerState.js';
|
||||||
|
import { createMilitaryRegistry } from '../layers/aircraft/classification.js';
|
||||||
|
import { createApplicationFlights } from './layers/flights.js';
|
||||||
|
import { createApplicationMilitary } from './layers/militaryFlights.js';
|
||||||
|
import { createApplicationVessels } from './layers/aisLiveVessels.js';
|
||||||
|
import { createApplicationCctv } from './layers/cctv.js';
|
||||||
|
import { createApplicationRadio } from './layers/radio.js';
|
||||||
|
import { createApplicationTraffic } from './layers/traffic.js';
|
||||||
|
import { createApplicationBikeshare } from './layers/bikeshare.js';
|
||||||
|
import { createApplicationInstallations } from './layers/militaryInstallations.js';
|
||||||
|
import { createApplicationSatellites } from './layers/satellites.js';
|
||||||
|
import { createApplicationLaunches } from './layers/rocketLaunches.js';
|
||||||
|
import { createApplicationAlpr } from './layers/alprCameras.js';
|
||||||
|
import { createApplicationAwareness } from './layers/militaryAwareness.js';
|
||||||
|
import { createApplicationFirms } from './layers/firms.js';
|
||||||
|
import { createApplicationEarthquakes } from './layers/earthquakes.js';
|
||||||
|
import { createApplicationCables } from './layers/submarineCables.js';
|
||||||
|
import { createInfrastructureLayers } from '../data/infrastructure.js';
|
||||||
|
import { localGeoJsonServices } from '../data/localGeojson.js';
|
||||||
|
|
||||||
|
const SOURCE_METHODS = Object.freeze({
|
||||||
|
flights: ['getSnapshot'],
|
||||||
|
military: ['getSnapshot'],
|
||||||
|
vessels: ['getSnapshot'],
|
||||||
|
cctv: ['getCatalog', 'getHealth', 'getFrameUrl', 'getMediaUrl'],
|
||||||
|
radio: ['getDirectory', 'recordClick'],
|
||||||
|
traffic: [
|
||||||
|
'requestRoads',
|
||||||
|
'getStatus',
|
||||||
|
'fetchFlowForBounds',
|
||||||
|
'getFlowSessionStats',
|
||||||
|
'resetFlowTileCache',
|
||||||
|
],
|
||||||
|
bikeshare: ['getStations'],
|
||||||
|
installations: ['getMappedSites', 'searchNearby'],
|
||||||
|
satellites: ['readGroup'],
|
||||||
|
launches: ['getLaunches', 'getActiveTle'],
|
||||||
|
alpr: ['fetch'],
|
||||||
|
firms: ['getSnapshot'],
|
||||||
|
earthquakes: ['getSnapshot'],
|
||||||
|
cables: ['fetch'],
|
||||||
|
});
|
||||||
|
|
||||||
|
/** Construct the current catalog without choosing any source provider.
|
||||||
|
* Scene engines remain page-owned; layers and classification have this app's lifetime.
|
||||||
|
* The manager owns layer destruction, while abort releases classification even if startup fails.
|
||||||
|
*/
|
||||||
|
export function createApplicationCatalog({
|
||||||
|
sources,
|
||||||
|
signal,
|
||||||
|
metadata = LAYER_STATE_REGISTRY,
|
||||||
|
vesselOptions,
|
||||||
|
resolveAsset,
|
||||||
|
}) {
|
||||||
|
if (!signal?.addEventListener)
|
||||||
|
throw new TypeError('An application lifetime signal is required');
|
||||||
|
signal.throwIfAborted();
|
||||||
|
for (const [name, methods] of Object.entries(SOURCE_METHODS)) {
|
||||||
|
if (
|
||||||
|
methods.some((method) => typeof sources?.[name]?.[method] !== 'function')
|
||||||
|
)
|
||||||
|
throw new TypeError(`Invalid catalog source: ${name}`);
|
||||||
|
}
|
||||||
|
const militaryRegistry = createMilitaryRegistry();
|
||||||
|
const dispose = () => {
|
||||||
|
signal.removeEventListener('abort', dispose);
|
||||||
|
militaryRegistry.dispose();
|
||||||
|
};
|
||||||
|
signal.addEventListener('abort', dispose, { once: true });
|
||||||
|
try {
|
||||||
|
militaryRegistry.configureSource(sources.military, { signal });
|
||||||
|
const flights = createApplicationFlights({
|
||||||
|
source: sources.flights,
|
||||||
|
militaryRegistry,
|
||||||
|
resolveAsset,
|
||||||
|
});
|
||||||
|
const military = createApplicationMilitary({
|
||||||
|
source: sources.military,
|
||||||
|
militaryRegistry,
|
||||||
|
resolveAsset,
|
||||||
|
});
|
||||||
|
const vessels = createApplicationVessels({
|
||||||
|
source: sources.vessels,
|
||||||
|
options: vesselOptions,
|
||||||
|
});
|
||||||
|
const installations = createApplicationInstallations({
|
||||||
|
source: sources.installations,
|
||||||
|
});
|
||||||
|
const satellites = createApplicationSatellites({
|
||||||
|
source: sources.satellites,
|
||||||
|
});
|
||||||
|
const catalog = createLayerCatalog(
|
||||||
|
[
|
||||||
|
flights,
|
||||||
|
military,
|
||||||
|
createApplicationEarthquakes({ source: sources.earthquakes }),
|
||||||
|
createApplicationAlpr({ source: sources.alpr }),
|
||||||
|
satellites,
|
||||||
|
createApplicationLaunches({ source: sources.launches, satellites }),
|
||||||
|
createApplicationTraffic({ source: sources.traffic }),
|
||||||
|
createApplicationCctv({ source: sources.cctv }),
|
||||||
|
createApplicationRadio({ source: sources.radio }),
|
||||||
|
createApplicationBikeshare({ source: sources.bikeshare }),
|
||||||
|
vessels,
|
||||||
|
installations,
|
||||||
|
createApplicationAwareness({
|
||||||
|
flights,
|
||||||
|
military,
|
||||||
|
vessels,
|
||||||
|
installations,
|
||||||
|
}),
|
||||||
|
...createInfrastructureLayers(localGeoJsonServices),
|
||||||
|
createApplicationCables({ source: sources.cables }),
|
||||||
|
createApplicationFirms({
|
||||||
|
id: 'local-firms',
|
||||||
|
name: 'FIRMS Active Fires',
|
||||||
|
icon: '▲',
|
||||||
|
source: 'NASA FIRMS · LIVE',
|
||||||
|
feed: sources.firms,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
metadata,
|
||||||
|
);
|
||||||
|
return Object.freeze({ ...catalog, militaryRegistry });
|
||||||
|
} catch (error) {
|
||||||
|
dispose();
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import test from 'node:test';
|
||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import { createApplicationCatalog } from './constructCatalog.js';
|
||||||
|
import { createStandaloneLayerSources } from '../standalone/layerSources.js';
|
||||||
|
import { catalogControlServices } from './catalog.js';
|
||||||
|
|
||||||
|
function fixtureSources(ids, calls) {
|
||||||
|
const sources = createStandaloneLayerSources();
|
||||||
|
sources.military = {
|
||||||
|
getSnapshot: async () => {
|
||||||
|
throw new Error('Use identity acquisition');
|
||||||
|
},
|
||||||
|
async getIdentities(_query, { signal }) {
|
||||||
|
calls.push(signal);
|
||||||
|
return ids;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('catalogs construct distinct layers and classification from their supplied source', async (t) => {
|
||||||
|
const a = new AbortController();
|
||||||
|
const b = new AbortController();
|
||||||
|
t.after(() => {
|
||||||
|
a.abort();
|
||||||
|
b.abort();
|
||||||
|
});
|
||||||
|
const callsA = [];
|
||||||
|
const callsB = [];
|
||||||
|
const first = createApplicationCatalog({
|
||||||
|
sources: fixtureSources(['abc123'], callsA),
|
||||||
|
signal: a.signal,
|
||||||
|
});
|
||||||
|
const second = createApplicationCatalog({
|
||||||
|
sources: fixtureSources(['def456'], callsB),
|
||||||
|
signal: b.signal,
|
||||||
|
});
|
||||||
|
assert.equal(first.layers.length, 17);
|
||||||
|
assert.deepEqual(
|
||||||
|
first.layers.map(({ id }) => id),
|
||||||
|
second.layers.map(({ id }) => id),
|
||||||
|
);
|
||||||
|
for (const layer of first.layers)
|
||||||
|
assert.notEqual(layer, second.get(layer.id));
|
||||||
|
assert.equal(
|
||||||
|
catalogControlServices(first).satellitesLayer,
|
||||||
|
first.get('satellites'),
|
||||||
|
);
|
||||||
|
assert.equal(callsA.length, 0, 'construction must not acquire');
|
||||||
|
await first.militaryRegistry.refreshMilitaryRegistryIfStale();
|
||||||
|
await second.militaryRegistry.refreshMilitaryRegistryIfStale();
|
||||||
|
assert.equal(first.militaryRegistry.isMilitaryIcao('abc123'), true);
|
||||||
|
assert.equal(first.militaryRegistry.isMilitaryIcao('def456'), false);
|
||||||
|
assert.equal(second.militaryRegistry.isMilitaryIcao('def456'), true);
|
||||||
|
a.abort();
|
||||||
|
assert.equal(callsA[0].aborted, true);
|
||||||
|
assert.equal(first.militaryRegistry.isMilitaryIcao('abc123'), false);
|
||||||
|
assert.equal(callsB[0].aborted, false);
|
||||||
|
assert.equal(second.militaryRegistry.isMilitaryIcao('def456'), true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('invalid or already cancelled construction fails before classification can acquire', () => {
|
||||||
|
const lifetime = new AbortController();
|
||||||
|
assert.throws(
|
||||||
|
() => createApplicationCatalog({ sources: {}, signal: lifetime.signal }),
|
||||||
|
/catalog source/,
|
||||||
|
);
|
||||||
|
lifetime.abort();
|
||||||
|
assert.throws(
|
||||||
|
() =>
|
||||||
|
createApplicationCatalog({
|
||||||
|
sources: createStandaloneLayerSources(),
|
||||||
|
signal: lifetime.signal,
|
||||||
|
}),
|
||||||
|
{ name: 'AbortError' },
|
||||||
|
);
|
||||||
|
});
|
||||||
+1
-1
@@ -49,5 +49,5 @@ export function createApplicationData({
|
|||||||
dataManager.buildTogglePanel(document.getElementById('data-toggles'));
|
dataManager.buildTogglePanel(document.getElementById('data-toggles'));
|
||||||
styleManager.attachDataManager(dataManager);
|
styleManager.attachDataManager(dataManager);
|
||||||
|
|
||||||
return { dataManager };
|
return { dataManager, catalog };
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { createVesselLayer } from '../../layers/vessels/index.js';
|
||||||
|
import * as context from '../../data/contextStore.js';
|
||||||
|
import * as trails from '../../data/trailRenderer.js';
|
||||||
|
import * as labels from '../../data/detectionDraw.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as overlay from '../../overlays/worldOverlay.js';
|
||||||
|
import * as geoid from '../../data/geoid.js';
|
||||||
|
import * as sprites from '../../data/spriteOrder.js';
|
||||||
|
import * as focus from '../../data/focusDeemphasis.js';
|
||||||
|
import * as worldFocus from '../../worldFocus.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationVessels({ source, options = {} }) {
|
||||||
|
return createVesselLayer({
|
||||||
|
source,
|
||||||
|
options,
|
||||||
|
services: {
|
||||||
|
context,
|
||||||
|
trails,
|
||||||
|
labels,
|
||||||
|
picking,
|
||||||
|
overlay,
|
||||||
|
geoid,
|
||||||
|
sprites,
|
||||||
|
focus,
|
||||||
|
worldFocus,
|
||||||
|
render,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { createAlprCamerasLayer } from '../../layers/alpr/index.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
import * as context from '../../data/contextStore.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as groundFloor from '../../data/groundFloor.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationAlpr({ source }) {
|
||||||
|
return createAlprCamerasLayer({
|
||||||
|
source,
|
||||||
|
services: { render, context, picking, groundFloor },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { createBikeshareLayer } from '../../layers/bikeshare/index.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
import * as sprites from '../../data/spriteOrder.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as overlays from '../../overlays/worldOverlay.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationBikeshare({ source }) {
|
||||||
|
return createBikeshareLayer({
|
||||||
|
source,
|
||||||
|
services: { render, sprites, picking, overlays },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { createCctvLayer } from '../../layers/cctv/index.js';
|
||||||
|
import * as sprites from '../../data/spriteOrder.js';
|
||||||
|
import * as activation from '../../cctvFocusRequest.js';
|
||||||
|
import * as overlays from '../../overlays/worldOverlay.js';
|
||||||
|
import * as locations from '../../locations.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as terrain from '../../data/terrainHeights.js';
|
||||||
|
import * as ground from '../../data/groundFloor.js';
|
||||||
|
import * as mesh from '../../data/meshFloorSampler.js';
|
||||||
|
import * as focus from '../../data/focusDeemphasis.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationCctv({ source }) {
|
||||||
|
return createCctvLayer({
|
||||||
|
source,
|
||||||
|
services: {
|
||||||
|
sprites,
|
||||||
|
activation,
|
||||||
|
overlays,
|
||||||
|
locations,
|
||||||
|
picking,
|
||||||
|
terrain,
|
||||||
|
ground,
|
||||||
|
mesh,
|
||||||
|
focus,
|
||||||
|
render,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { createEarthquakesLayer } from '../../layers/earthquakes/index.js';
|
||||||
|
import { overlayHost } from './overlayHost.js';
|
||||||
|
/** Wire earthquake observations to the application overlay host. */
|
||||||
|
export function createApplicationEarthquakes(options) {
|
||||||
|
return createEarthquakesLayer({ overlayHost, ...options });
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import * as Cesium from 'cesium';
|
||||||
|
import { createFirmsHeatmapLayer as createLayer } from '../../layers/firms/index.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
import * as sprites from '../../data/spriteOrder.js';
|
||||||
|
import * as context from '../../data/contextStore.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as anchors from '../../data/fireAnchors.js';
|
||||||
|
import * as overlays from '../../overlays/worldOverlay.js';
|
||||||
|
import * as focus from '../../worldFocus.js';
|
||||||
|
export const firmsServices = {
|
||||||
|
render,
|
||||||
|
sprites,
|
||||||
|
context,
|
||||||
|
picking,
|
||||||
|
anchors,
|
||||||
|
overlays,
|
||||||
|
focus,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Bind a supplied fire feed to the application scene service owners. */
|
||||||
|
export function createApplicationFirms(options) {
|
||||||
|
return createLayer({
|
||||||
|
...options,
|
||||||
|
icon: options.icon ?? '▲',
|
||||||
|
source: options.source ?? 'NASA FIRMS',
|
||||||
|
feed: options.feed,
|
||||||
|
services: firmsServices,
|
||||||
|
overlayHost: options.overlayHost ?? {
|
||||||
|
setEntries: overlays.setOverlayEntries,
|
||||||
|
setVisible: overlays.setOverlaySourceVisible,
|
||||||
|
clearSource: overlays.clearOverlaySource,
|
||||||
|
hitTest: overlays.hitTestWorldOverlay,
|
||||||
|
},
|
||||||
|
screenSpaceEventHandlerFactory:
|
||||||
|
options.screenSpaceEventHandlerFactory ??
|
||||||
|
((viewer) => new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)),
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { createCivilFlightLayer } from '../../layers/flights/index.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as sprites from '../../data/spriteOrder.js';
|
||||||
|
import * as trails from '../../data/trailRenderer.js';
|
||||||
|
import * as aircraftPresentation from '../../data/tr3bRegistry.js';
|
||||||
|
import * as camera from '../../data/trackedCamera.js';
|
||||||
|
import * as labels from '../../data/detectionDraw.js';
|
||||||
|
import * as groundFloor from '../../data/groundFloor.js';
|
||||||
|
import * as meshFloor from '../../data/meshFloorSampler.js';
|
||||||
|
import * as geoid from '../../data/geoid.js';
|
||||||
|
import * as focus from '../../data/focusDeemphasis.js';
|
||||||
|
import * as readout from '../../data/trackedReadout.js';
|
||||||
|
import * as context from '../../data/contextStore.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
import * as groundSnap from '../../data/groundSnap.js';
|
||||||
|
import * as recession from '../../data/aircraftRecession.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationFlights({
|
||||||
|
source,
|
||||||
|
militaryRegistry,
|
||||||
|
resolveAsset = (url) =>
|
||||||
|
`${import.meta.env?.BASE_URL || '/'}${url.replace(/^\//, '')}`,
|
||||||
|
}) {
|
||||||
|
return createCivilFlightLayer({
|
||||||
|
source,
|
||||||
|
resolveAsset,
|
||||||
|
services: {
|
||||||
|
picking,
|
||||||
|
sprites,
|
||||||
|
trails,
|
||||||
|
aircraftPresentation,
|
||||||
|
camera,
|
||||||
|
militaryRegistry,
|
||||||
|
labels,
|
||||||
|
groundFloor,
|
||||||
|
meshFloor,
|
||||||
|
geoid,
|
||||||
|
focus,
|
||||||
|
readout,
|
||||||
|
context,
|
||||||
|
render,
|
||||||
|
groundSnap,
|
||||||
|
recession,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { createAwarenessLayer } from '../../layers/awareness/index.js';
|
||||||
|
import * as navigation from '../../navigationPolicy.js';
|
||||||
|
import * as geometry from '../../celestialRing.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
/** Bind Contacts to the same catalog instances used for rendering. */
|
||||||
|
export function createApplicationAwareness({
|
||||||
|
flights,
|
||||||
|
military,
|
||||||
|
vessels,
|
||||||
|
installations,
|
||||||
|
}) {
|
||||||
|
return createAwarenessLayer({
|
||||||
|
services: {
|
||||||
|
flights,
|
||||||
|
military,
|
||||||
|
vessels,
|
||||||
|
installations,
|
||||||
|
navigation,
|
||||||
|
geometry,
|
||||||
|
render,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { createMilitaryFlightLayer } from '../../layers/military/index.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as sprites from '../../data/spriteOrder.js';
|
||||||
|
import * as trails from '../../data/trailRenderer.js';
|
||||||
|
import * as aircraftPresentation from '../../data/tr3bRegistry.js';
|
||||||
|
import * as camera from '../../data/trackedCamera.js';
|
||||||
|
import * as labels from '../../data/detectionDraw.js';
|
||||||
|
import * as groundFloor from '../../data/groundFloor.js';
|
||||||
|
import * as meshFloor from '../../data/meshFloorSampler.js';
|
||||||
|
import * as geoid from '../../data/geoid.js';
|
||||||
|
import * as focus from '../../data/focusDeemphasis.js';
|
||||||
|
import * as readout from '../../data/trackedReadout.js';
|
||||||
|
import * as context from '../../data/contextStore.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
import * as groundSnap from '../../data/groundSnap.js';
|
||||||
|
import * as recession from '../../data/aircraftRecession.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationMilitary({
|
||||||
|
source,
|
||||||
|
militaryRegistry,
|
||||||
|
resolveAsset = (url) =>
|
||||||
|
`${import.meta.env?.BASE_URL || '/'}${url.replace(/^\//, '')}`,
|
||||||
|
}) {
|
||||||
|
return createMilitaryFlightLayer({
|
||||||
|
source,
|
||||||
|
resolveAsset,
|
||||||
|
services: {
|
||||||
|
picking,
|
||||||
|
sprites,
|
||||||
|
trails,
|
||||||
|
aircraftPresentation,
|
||||||
|
camera,
|
||||||
|
militaryRegistry,
|
||||||
|
labels,
|
||||||
|
groundFloor,
|
||||||
|
meshFloor,
|
||||||
|
geoid,
|
||||||
|
focus,
|
||||||
|
readout,
|
||||||
|
context,
|
||||||
|
render,
|
||||||
|
groundSnap,
|
||||||
|
recession,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { createInstallationsLayer } from '../../layers/installations/index.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
import * as context from '../../data/contextStore.js';
|
||||||
|
import * as ground from '../../data/groundFloor.js';
|
||||||
|
import * as anchors from '../../data/fireAnchors.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationInstallations({ source }) {
|
||||||
|
return createInstallationsLayer({
|
||||||
|
source,
|
||||||
|
services: { render, context, ground, anchors, picking },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import {
|
||||||
|
clearOverlaySource,
|
||||||
|
setOverlayEntries,
|
||||||
|
setOverlaySourceVisible,
|
||||||
|
} from '../../overlays/worldOverlay.js';
|
||||||
|
export const overlayHost = Object.freeze({
|
||||||
|
clearSource: clearOverlaySource,
|
||||||
|
setEntries: setOverlayEntries,
|
||||||
|
setVisible: setOverlaySourceVisible,
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { createRadioLayer } from '../../layers/radio/index.js';
|
||||||
|
import * as ground from '../../data/groundFloor.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as overlays from '../../overlays/worldOverlay.js';
|
||||||
|
import * as globe from '../../celestialRing.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationRadio({ source }) {
|
||||||
|
return createRadioLayer({
|
||||||
|
source,
|
||||||
|
services: { ground, picking, overlays, globe, render },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { createRocketLaunchesLayer } from '../../layers/launches/index.js';
|
||||||
|
import * as geometry from '../../celestialRing.js';
|
||||||
|
import * as overlays from '../../overlays/worldOverlay.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationLaunches({ source, satellites }) {
|
||||||
|
return createRocketLaunchesLayer({
|
||||||
|
source,
|
||||||
|
services: { satellites, geometry, overlays, render },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import { createSatellitesLayer } from '../../layers/satellites/index.js';
|
||||||
|
import * as picking from '../../data/pickRegistry.js';
|
||||||
|
import * as focus from '../../data/focusDeemphasis.js';
|
||||||
|
import * as readout from '../../data/trackedReadout.js';
|
||||||
|
import * as overlays from '../../overlays/worldOverlay.js';
|
||||||
|
import * as context from '../../data/contextStore.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
import * as layerState from '../../data/layerState.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationSatellites({ source }) {
|
||||||
|
return createSatellitesLayer({
|
||||||
|
source,
|
||||||
|
services: {
|
||||||
|
picking,
|
||||||
|
focus,
|
||||||
|
readout,
|
||||||
|
overlays,
|
||||||
|
context,
|
||||||
|
render,
|
||||||
|
layerState,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import * as Cesium from 'cesium';
|
||||||
|
import { createSubmarineCableLayer } from '../../layers/submarineCables/index.js';
|
||||||
|
import { overlayHost } from './overlayHost.js';
|
||||||
|
/** Wire cable source geometry to the application overlay host. */
|
||||||
|
export function createApplicationCables(options) {
|
||||||
|
return createSubmarineCableLayer({
|
||||||
|
overlayHost,
|
||||||
|
screenSpaceEventHandlerFactory: (canvas) =>
|
||||||
|
new Cesium.ScreenSpaceEventHandler(canvas),
|
||||||
|
mapStackEventTarget: typeof window !== 'undefined' ? window : null,
|
||||||
|
...options,
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { createTrafficLayer } from '../../layers/traffic/index.js';
|
||||||
|
import * as credits from '../../data/dataCredits.js';
|
||||||
|
import * as render from '../../renderGovernor.js';
|
||||||
|
|
||||||
|
/** Construct one layer using the application scene owners and a supplied source. */
|
||||||
|
export function createApplicationTraffic({ source }) {
|
||||||
|
return createTrafficLayer({
|
||||||
|
source,
|
||||||
|
services: { credits, render },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,17 +1,8 @@
|
|||||||
import { createVesselLayer } from '../layers/vessels/index.js';
|
import { createApplicationVessels } from '../app/layers/aisLiveVessels.js';
|
||||||
import { createAisStreamSource } from '../sources/live/standalone.js';
|
|
||||||
import * as context from './contextStore.js';
|
|
||||||
import * as trails from './trailRenderer.js';
|
|
||||||
import * as labels from './detectionDraw.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as overlay from '../overlays/worldOverlay.js';
|
|
||||||
import * as geoid from './geoid.js';
|
|
||||||
import * as sprites from './spriteOrder.js';
|
|
||||||
import * as focus from './focusDeemphasis.js';
|
|
||||||
import * as worldFocus from '../worldFocus.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
|
|
||||||
const aisLiveVesselsLayer = createVesselLayer({
|
import { createAisStreamSource } from '../sources/live/standalone.js';
|
||||||
|
|
||||||
|
const aisLiveVesselsLayer = createApplicationVessels({
|
||||||
source: createAisStreamSource({
|
source: createAisStreamSource({
|
||||||
apiUrl: import.meta.env?.VITE_AIS_LIVE_API_URL || '/api/ais-live',
|
apiUrl: import.meta.env?.VITE_AIS_LIVE_API_URL || '/api/ais-live',
|
||||||
}),
|
}),
|
||||||
@@ -19,18 +10,6 @@ const aisLiveVesselsLayer = createVesselLayer({
|
|||||||
maxRows: import.meta.env?.VITE_AIS_LIVE_MAX_ROWS,
|
maxRows: import.meta.env?.VITE_AIS_LIVE_MAX_ROWS,
|
||||||
maxLabels: import.meta.env?.VITE_AIS_LIVE_LABEL_MAX_ROWS,
|
maxLabels: import.meta.env?.VITE_AIS_LIVE_LABEL_MAX_ROWS,
|
||||||
},
|
},
|
||||||
services: {
|
|
||||||
context,
|
|
||||||
trails,
|
|
||||||
labels,
|
|
||||||
picking,
|
|
||||||
overlay,
|
|
||||||
geoid,
|
|
||||||
sprites,
|
|
||||||
focus,
|
|
||||||
worldFocus,
|
|
||||||
render,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
export { AIS_FIRST_CONNECT_GRACE_MS } from '../layers/vessels/policy.js';
|
export { AIS_FIRST_CONNECT_GRACE_MS } from '../layers/vessels/policy.js';
|
||||||
export const deriveAisFeedError = aisLiveVesselsLayer.deriveAisFeedError;
|
export const deriveAisFeedError = aisLiveVesselsLayer.deriveAisFeedError;
|
||||||
|
|||||||
+3
-10
@@ -1,12 +1,6 @@
|
|||||||
|
import { createApplicationAlpr } from '../app/layers/alprCameras.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import {
|
import { createOverpassAlprSource } from '../layers/alpr/index.js';
|
||||||
createAlprCamerasLayer,
|
|
||||||
createOverpassAlprSource,
|
|
||||||
} from '../layers/alpr/index.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
import * as context from './contextStore.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as groundFloor from './groundFloor.js';
|
|
||||||
export * from '../layers/alpr/index.js';
|
export * from '../layers/alpr/index.js';
|
||||||
const slot = createSourceSlot(
|
const slot = createSourceSlot(
|
||||||
createOverpassAlprSource(),
|
createOverpassAlprSource(),
|
||||||
@@ -14,7 +8,6 @@ const slot = createSourceSlot(
|
|||||||
'ALPR source',
|
'ALPR source',
|
||||||
);
|
);
|
||||||
export const configureAlprSource = slot.configure;
|
export const configureAlprSource = slot.configure;
|
||||||
export default createAlprCamerasLayer({
|
export default createApplicationAlpr({
|
||||||
source: slot.source,
|
source: slot.source,
|
||||||
services: { render, context, picking, groundFloor },
|
|
||||||
});
|
});
|
||||||
@@ -1,10 +1,6 @@
|
|||||||
|
import { createApplicationBikeshare } from '../app/layers/bikeshare.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import { createBikeshareSource } from '../layers/bikeshare/source.js';
|
import { createBikeshareSource } from '../layers/bikeshare/source.js';
|
||||||
import { createBikeshareLayer } from '../layers/bikeshare/index.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
import * as sprites from './spriteOrder.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as overlays from '../overlays/worldOverlay.js';
|
|
||||||
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createBikeshareSource(),
|
createBikeshareSource(),
|
||||||
@@ -12,9 +8,8 @@ const sourceSlot = createSourceSlot(
|
|||||||
'Bikeshare source',
|
'Bikeshare source',
|
||||||
);
|
);
|
||||||
export const configureBikeshareSource = sourceSlot.configure;
|
export const configureBikeshareSource = sourceSlot.configure;
|
||||||
const layer = createBikeshareLayer({
|
const layer = createApplicationBikeshare({
|
||||||
source: sourceSlot.source,
|
source: sourceSlot.source,
|
||||||
services: { render, sprites, picking, overlays },
|
|
||||||
});
|
});
|
||||||
export const createBikeshareSelectedOverlayEntry =
|
export const createBikeshareSelectedOverlayEntry =
|
||||||
layer.createBikeshareSelectedOverlayEntry;
|
layer.createBikeshareSelectedOverlayEntry;
|
||||||
|
|||||||
+2
-24
@@ -1,16 +1,6 @@
|
|||||||
|
import { createApplicationCctv } from '../app/layers/cctv.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import { createCctvSource } from '../layers/cctv/source.js';
|
import { createCctvSource } from '../layers/cctv/source.js';
|
||||||
import { createCctvLayer } from '../layers/cctv/index.js';
|
|
||||||
import * as sprites from './spriteOrder.js';
|
|
||||||
import * as activation from '../cctvFocusRequest.js';
|
|
||||||
import * as overlays from '../overlays/worldOverlay.js';
|
|
||||||
import * as locations from '../locations.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as terrain from './terrainHeights.js';
|
|
||||||
import * as ground from './groundFloor.js';
|
|
||||||
import * as mesh from './meshFloorSampler.js';
|
|
||||||
import * as focus from './focusDeemphasis.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createCctvSource(),
|
createCctvSource(),
|
||||||
@@ -18,20 +8,8 @@ const sourceSlot = createSourceSlot(
|
|||||||
'Cctv source',
|
'Cctv source',
|
||||||
);
|
);
|
||||||
export const configureCctvSource = sourceSlot.configure;
|
export const configureCctvSource = sourceSlot.configure;
|
||||||
const layer = createCctvLayer({
|
const layer = createApplicationCctv({
|
||||||
source: sourceSlot.source,
|
source: sourceSlot.source,
|
||||||
services: {
|
|
||||||
sprites,
|
|
||||||
activation,
|
|
||||||
overlays,
|
|
||||||
locations,
|
|
||||||
picking,
|
|
||||||
terrain,
|
|
||||||
ground,
|
|
||||||
mesh,
|
|
||||||
focus,
|
|
||||||
render,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
export const calibrationPatchMovesAnchor = layer.calibrationPatchMovesAnchor;
|
export const calibrationPatchMovesAnchor = layer.calibrationPatchMovesAnchor;
|
||||||
export const migrateRangeScaleForFloor = layer.migrateRangeScaleForFloor;
|
export const migrateRangeScaleForFloor = layer.migrateRangeScaleForFloor;
|
||||||
|
|||||||
+4
-15
@@ -1,22 +1,11 @@
|
|||||||
import {
|
import { createUsgsEarthquakeSource } from '../layers/earthquakes/index.js';
|
||||||
createEarthquakesLayer as createLayer,
|
import { createApplicationEarthquakes } from '../app/layers/earthquakes.js';
|
||||||
createUsgsEarthquakeSource,
|
|
||||||
} from '../layers/earthquakes/index.js';
|
|
||||||
import {
|
|
||||||
clearOverlaySource,
|
|
||||||
setOverlayEntries,
|
|
||||||
setOverlaySourceVisible,
|
|
||||||
} from '../overlays/worldOverlay.js';
|
|
||||||
export * from '../layers/earthquakes/index.js';
|
export * from '../layers/earthquakes/index.js';
|
||||||
/** Wire the standalone source and application overlay owner. */
|
/** Wire the standalone source and application overlay owner. */
|
||||||
export function createEarthquakesLayer({
|
export function createEarthquakesLayer({
|
||||||
source = createUsgsEarthquakeSource(),
|
source = createUsgsEarthquakeSource(),
|
||||||
overlayHost = {
|
...options
|
||||||
setEntries: setOverlayEntries,
|
|
||||||
setVisible: setOverlaySourceVisible,
|
|
||||||
clearSource: clearOverlaySource,
|
|
||||||
},
|
|
||||||
} = {}) {
|
} = {}) {
|
||||||
return createLayer({ source, overlayHost });
|
return createApplicationEarthquakes({ source, ...options });
|
||||||
}
|
}
|
||||||
export default createEarthquakesLayer();
|
export default createEarthquakesLayer();
|
||||||
@@ -1,33 +1,16 @@
|
|||||||
|
import { createApplicationFirms, firmsServices } from '../app/layers/firms.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import * as Cesium from 'cesium';
|
|
||||||
import {
|
import {
|
||||||
createFirmsHeatmapLayer as createLayer,
|
|
||||||
createFirmsHelpers,
|
createFirmsHelpers,
|
||||||
createFirmsSource,
|
createFirmsSource,
|
||||||
} from '../layers/firms/index.js';
|
} from '../layers/firms/index.js';
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
import * as sprites from './spriteOrder.js';
|
|
||||||
import * as context from './contextStore.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as anchors from './fireAnchors.js';
|
|
||||||
import * as overlays from '../overlays/worldOverlay.js';
|
|
||||||
import * as focus from '../worldFocus.js';
|
|
||||||
const services = {
|
|
||||||
render,
|
|
||||||
sprites,
|
|
||||||
context,
|
|
||||||
picking,
|
|
||||||
anchors,
|
|
||||||
overlays,
|
|
||||||
focus,
|
|
||||||
};
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createFirmsSource(),
|
createFirmsSource(),
|
||||||
['getSnapshot'],
|
['getSnapshot'],
|
||||||
'Fire source',
|
'Fire source',
|
||||||
);
|
);
|
||||||
export const configureFirmsSource = sourceSlot.configure;
|
export const configureFirmsSource = sourceSlot.configure;
|
||||||
const helpers = createFirmsHelpers({ services });
|
const helpers = createFirmsHelpers({ services: firmsServices });
|
||||||
export const mapAnalystRecord = helpers.mapAnalystRecord;
|
export const mapAnalystRecord = helpers.mapAnalystRecord;
|
||||||
export const fireCullPosition = helpers.fireCullPosition;
|
export const fireCullPosition = helpers.fireCullPosition;
|
||||||
export const applyHorizonCull = helpers.applyHorizonCull;
|
export const applyHorizonCull = helpers.applyHorizonCull;
|
||||||
@@ -36,22 +19,10 @@ export const buildFireCard = helpers.buildFireCard;
|
|||||||
export const buildCellCard = helpers.buildCellCard;
|
export const buildCellCard = helpers.buildCellCard;
|
||||||
export const applyFirmsOverlayPolicy = helpers.applyFirmsOverlayPolicy;
|
export const applyFirmsOverlayPolicy = helpers.applyFirmsOverlayPolicy;
|
||||||
|
|
||||||
/** Supply the local source and the application's scene service owners. */
|
/** Retain the existing default feed and helper exports for direct callers. */
|
||||||
export function createFirmsHeatmapLayer(options) {
|
export function createFirmsHeatmapLayer(options) {
|
||||||
return createLayer({
|
return createApplicationFirms({
|
||||||
...options,
|
...options,
|
||||||
icon: options.icon ?? '▲',
|
|
||||||
source: options.source ?? 'NASA FIRMS',
|
|
||||||
feed: options.feed ?? sourceSlot.source,
|
feed: options.feed ?? sourceSlot.source,
|
||||||
services,
|
|
||||||
overlayHost: options.overlayHost ?? {
|
|
||||||
setEntries: overlays.setOverlayEntries,
|
|
||||||
setVisible: overlays.setOverlaySourceVisible,
|
|
||||||
clearSource: overlays.clearOverlaySource,
|
|
||||||
hitTest: overlays.hitTestWorldOverlay,
|
|
||||||
},
|
|
||||||
screenSpaceEventHandlerFactory:
|
|
||||||
options.screenSpaceEventHandlerFactory ??
|
|
||||||
((viewer) => new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
+5
-38
@@ -1,44 +1,11 @@
|
|||||||
import { createCivilFlightLayer } from '../layers/flights/index.js';
|
import { createApplicationFlights } from '../app/layers/flights.js';
|
||||||
import { createOpenSkySource } from '../sources/live/standalone.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as sprites from './spriteOrder.js';
|
|
||||||
import * as trails from './trailRenderer.js';
|
|
||||||
import * as aircraftPresentation from './tr3bRegistry.js';
|
|
||||||
import * as camera from './trackedCamera.js';
|
|
||||||
import * as militaryRegistry from './militaryRegistry.js';
|
|
||||||
import * as labels from './detectionDraw.js';
|
|
||||||
import * as groundFloor from './groundFloor.js';
|
|
||||||
import * as meshFloor from './meshFloorSampler.js';
|
|
||||||
import * as geoid from './geoid.js';
|
|
||||||
import * as focus from './focusDeemphasis.js';
|
|
||||||
import * as readout from './trackedReadout.js';
|
|
||||||
import * as context from './contextStore.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
import * as groundSnap from './groundSnap.js';
|
|
||||||
import * as recession from './aircraftRecession.js';
|
|
||||||
|
|
||||||
const flightsLayer = createCivilFlightLayer({
|
import { createOpenSkySource } from '../sources/live/standalone.js';
|
||||||
|
import * as militaryRegistry from './militaryRegistry.js';
|
||||||
|
|
||||||
|
const flightsLayer = createApplicationFlights({
|
||||||
source: createOpenSkySource(),
|
source: createOpenSkySource(),
|
||||||
resolveAsset: (url) =>
|
|
||||||
`${import.meta.env?.BASE_URL || '/'}${url.replace(/^\//, '')}`,
|
|
||||||
services: {
|
|
||||||
picking,
|
|
||||||
sprites,
|
|
||||||
trails,
|
|
||||||
aircraftPresentation,
|
|
||||||
camera,
|
|
||||||
militaryRegistry,
|
militaryRegistry,
|
||||||
labels,
|
|
||||||
groundFloor,
|
|
||||||
meshFloor,
|
|
||||||
geoid,
|
|
||||||
focus,
|
|
||||||
readout,
|
|
||||||
context,
|
|
||||||
render,
|
|
||||||
groundSnap,
|
|
||||||
recession,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
export { TRACKED_MODEL_MAX_PX } from '../layers/flights/policy.js';
|
export { TRACKED_MODEL_MAX_PX } from '../layers/flights/policy.js';
|
||||||
export const _floorGroundedDisplayPositionForTest =
|
export const _floorGroundedDisplayPositionForTest =
|
||||||
|
|||||||
@@ -1,22 +1,14 @@
|
|||||||
import { createAwarenessLayer } from '../layers/awareness/index.js';
|
import { createApplicationAwareness } from '../app/layers/militaryAwareness.js';
|
||||||
import flights from './flights.js';
|
import flights from './flights.js';
|
||||||
import military from './militaryFlights.js';
|
import military from './militaryFlights.js';
|
||||||
import vessels from './aisLiveVessels.js';
|
import vessels from './aisLiveVessels.js';
|
||||||
import installations from './militaryInstallations.js';
|
import installations from './militaryInstallations.js';
|
||||||
import * as navigation from '../navigationPolicy.js';
|
|
||||||
import * as geometry from '../celestialRing.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
|
|
||||||
const layer = createAwarenessLayer({
|
const layer = createApplicationAwareness({
|
||||||
services: {
|
|
||||||
flights,
|
flights,
|
||||||
military,
|
military,
|
||||||
vessels,
|
vessels,
|
||||||
installations,
|
installations,
|
||||||
navigation,
|
|
||||||
geometry,
|
|
||||||
render,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
export const contextTargetFlyToAllowed = layer.contextTargetFlyToAllowed;
|
export const contextTargetFlyToAllowed = layer.contextTargetFlyToAllowed;
|
||||||
export const awarenessClearMatchesSubject = layer.awarenessClearMatchesSubject;
|
export const awarenessClearMatchesSubject = layer.awarenessClearMatchesSubject;
|
||||||
|
|||||||
@@ -1,44 +1,11 @@
|
|||||||
import { createMilitaryFlightLayer } from '../layers/military/index.js';
|
import { createApplicationMilitary } from '../app/layers/militaryFlights.js';
|
||||||
import { createAdsbLolSource } from '../sources/live/standalone.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as sprites from './spriteOrder.js';
|
|
||||||
import * as trails from './trailRenderer.js';
|
|
||||||
import * as aircraftPresentation from './tr3bRegistry.js';
|
|
||||||
import * as camera from './trackedCamera.js';
|
|
||||||
import * as militaryRegistry from './militaryRegistry.js';
|
|
||||||
import * as labels from './detectionDraw.js';
|
|
||||||
import * as groundFloor from './groundFloor.js';
|
|
||||||
import * as meshFloor from './meshFloorSampler.js';
|
|
||||||
import * as geoid from './geoid.js';
|
|
||||||
import * as focus from './focusDeemphasis.js';
|
|
||||||
import * as readout from './trackedReadout.js';
|
|
||||||
import * as context from './contextStore.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
import * as groundSnap from './groundSnap.js';
|
|
||||||
import * as recession from './aircraftRecession.js';
|
|
||||||
|
|
||||||
const militaryFlightsLayer = createMilitaryFlightLayer({
|
import { createAdsbLolSource } from '../sources/live/standalone.js';
|
||||||
|
import * as militaryRegistry from './militaryRegistry.js';
|
||||||
|
|
||||||
|
const militaryFlightsLayer = createApplicationMilitary({
|
||||||
source: createAdsbLolSource(),
|
source: createAdsbLolSource(),
|
||||||
resolveAsset: (url) =>
|
|
||||||
`${import.meta.env?.BASE_URL || '/'}${url.replace(/^\//, '')}`,
|
|
||||||
services: {
|
|
||||||
picking,
|
|
||||||
sprites,
|
|
||||||
trails,
|
|
||||||
aircraftPresentation,
|
|
||||||
camera,
|
|
||||||
militaryRegistry,
|
militaryRegistry,
|
||||||
labels,
|
|
||||||
groundFloor,
|
|
||||||
meshFloor,
|
|
||||||
geoid,
|
|
||||||
focus,
|
|
||||||
readout,
|
|
||||||
context,
|
|
||||||
render,
|
|
||||||
groundSnap,
|
|
||||||
recession,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
export { TRACKED_MODEL_MAX_PX } from '../layers/military/policy.js';
|
export { TRACKED_MODEL_MAX_PX } from '../layers/military/policy.js';
|
||||||
export const _setTrackedMilitaryRefreshStateForTest =
|
export const _setTrackedMilitaryRefreshStateForTest =
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
|
import { createApplicationInstallations } from '../app/layers/militaryInstallations.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import {
|
import { createInstallationSource } from '../layers/installations/index.js';
|
||||||
createInstallationsLayer,
|
|
||||||
createInstallationSource,
|
|
||||||
} from '../layers/installations/index.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
import * as context from './contextStore.js';
|
|
||||||
import * as ground from './groundFloor.js';
|
|
||||||
import * as anchors from './fireAnchors.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createInstallationSource(),
|
createInstallationSource(),
|
||||||
@@ -15,9 +8,8 @@ const sourceSlot = createSourceSlot(
|
|||||||
'Installation source',
|
'Installation source',
|
||||||
);
|
);
|
||||||
export const configureInstallationSource = sourceSlot.configure;
|
export const configureInstallationSource = sourceSlot.configure;
|
||||||
const layer = createInstallationsLayer({
|
const layer = createApplicationInstallations({
|
||||||
source: sourceSlot.source,
|
source: sourceSlot.source,
|
||||||
services: { render, context, ground, anchors, picking },
|
|
||||||
});
|
});
|
||||||
export const approximateSurfaceDistanceM = layer.approximateSurfaceDistanceM;
|
export const approximateSurfaceDistanceM = layer.approximateSurfaceDistanceM;
|
||||||
export const classifyGoogleMilitaryPlace = layer.classifyGoogleMilitaryPlace;
|
export const classifyGoogleMilitaryPlace = layer.classifyGoogleMilitaryPlace;
|
||||||
|
|||||||
+2
-8
@@ -1,11 +1,6 @@
|
|||||||
|
import { createApplicationRadio } from '../app/layers/radio.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import { createRadioSource } from '../layers/radio/source.js';
|
import { createRadioSource } from '../layers/radio/source.js';
|
||||||
import { createRadioLayer } from '../layers/radio/index.js';
|
|
||||||
import * as ground from './groundFloor.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as overlays from '../overlays/worldOverlay.js';
|
|
||||||
import * as globe from '../celestialRing.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createRadioSource(),
|
createRadioSource(),
|
||||||
@@ -13,9 +8,8 @@ const sourceSlot = createSourceSlot(
|
|||||||
'Radio source',
|
'Radio source',
|
||||||
);
|
);
|
||||||
export const configureRadioSource = sourceSlot.configure;
|
export const configureRadioSource = sourceSlot.configure;
|
||||||
const layer = createRadioLayer({
|
const layer = createApplicationRadio({
|
||||||
source: sourceSlot.source,
|
source: sourceSlot.source,
|
||||||
services: { ground, picking, overlays, globe, render },
|
|
||||||
});
|
});
|
||||||
export const radioGlobeLabel = layer.radioGlobeLabel;
|
export const radioGlobeLabel = layer.radioGlobeLabel;
|
||||||
export const createRadioSelectedOverlayEntry =
|
export const createRadioSelectedOverlayEntry =
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
|
import { createApplicationLaunches } from '../app/layers/rocketLaunches.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import {
|
import { createLaunchSource } from '../layers/launches/index.js';
|
||||||
createRocketLaunchesLayer,
|
|
||||||
createLaunchSource,
|
|
||||||
} from '../layers/launches/index.js';
|
|
||||||
import * as satellites from './satellites.js';
|
import * as satellites from './satellites.js';
|
||||||
import * as geometry from '../celestialRing.js';
|
|
||||||
import * as overlays from '../overlays/worldOverlay.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createLaunchSource(),
|
createLaunchSource(),
|
||||||
@@ -14,9 +9,9 @@ const sourceSlot = createSourceSlot(
|
|||||||
'Launch source',
|
'Launch source',
|
||||||
);
|
);
|
||||||
export const configureLaunchSource = sourceSlot.configure;
|
export const configureLaunchSource = sourceSlot.configure;
|
||||||
const layer = createRocketLaunchesLayer({
|
const layer = createApplicationLaunches({
|
||||||
source: sourceSlot.source,
|
source: sourceSlot.source,
|
||||||
services: { satellites, geometry, overlays, render },
|
satellites,
|
||||||
});
|
});
|
||||||
export const satelliteParamsForSpaceMissions =
|
export const satelliteParamsForSpaceMissions =
|
||||||
layer.satelliteParamsForSpaceMissions;
|
layer.satelliteParamsForSpaceMissions;
|
||||||
|
|||||||
+3
-13
@@ -1,15 +1,6 @@
|
|||||||
|
import { createApplicationSatellites } from '../app/layers/satellites.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import {
|
import { createSatelliteSource } from '../layers/satellites/index.js';
|
||||||
createSatellitesLayer,
|
|
||||||
createSatelliteSource,
|
|
||||||
} from '../layers/satellites/index.js';
|
|
||||||
import * as picking from './pickRegistry.js';
|
|
||||||
import * as focus from './focusDeemphasis.js';
|
|
||||||
import * as readout from './trackedReadout.js';
|
|
||||||
import * as overlays from '../overlays/worldOverlay.js';
|
|
||||||
import * as context from './contextStore.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
import * as layerState from './layerState.js';
|
|
||||||
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createSatelliteSource(),
|
createSatelliteSource(),
|
||||||
@@ -17,9 +8,8 @@ const sourceSlot = createSourceSlot(
|
|||||||
'Satellite source',
|
'Satellite source',
|
||||||
);
|
);
|
||||||
export const configureSatelliteSource = sourceSlot.configure;
|
export const configureSatelliteSource = sourceSlot.configure;
|
||||||
const layer = createSatellitesLayer({
|
const layer = createApplicationSatellites({
|
||||||
source: sourceSlot.source,
|
source: sourceSlot.source,
|
||||||
services: { picking, focus, readout, overlays, context, render, layerState },
|
|
||||||
});
|
});
|
||||||
export const satelliteVisualsVisible = layer.satelliteVisualsVisible;
|
export const satelliteVisualsVisible = layer.satelliteVisualsVisible;
|
||||||
export const satelliteCatalogModeChanged = layer.satelliteCatalogModeChanged;
|
export const satelliteCatalogModeChanged = layer.satelliteCatalogModeChanged;
|
||||||
|
|||||||
@@ -1,26 +1,10 @@
|
|||||||
import * as Cesium from 'cesium';
|
import { createApplicationCables } from '../app/layers/submarineCables.js';
|
||||||
import {
|
import { overlayHost } from '../app/layers/overlayHost.js';
|
||||||
createSubmarineCableLayer,
|
import { createCableOverlayPublisher as createPublisher } from '../layers/submarineCables/index.js';
|
||||||
createCableOverlayPublisher as createPublisher,
|
|
||||||
} from '../layers/submarineCables/index.js';
|
|
||||||
import { createBundledCableSource } from '../layers/submarineCables/bundledSource.js';
|
import { createBundledCableSource } from '../layers/submarineCables/bundledSource.js';
|
||||||
import {
|
|
||||||
clearOverlaySource,
|
|
||||||
setOverlayEntries,
|
|
||||||
setOverlaySourceVisible,
|
|
||||||
} from '../overlays/worldOverlay.js';
|
|
||||||
const overlayHost = Object.freeze({
|
|
||||||
clearSource: clearOverlaySource,
|
|
||||||
setEntries: setOverlayEntries,
|
|
||||||
setVisible: setOverlaySourceVisible,
|
|
||||||
});
|
|
||||||
export function createTeleGeographySubmarineCableLayer(options = {}) {
|
export function createTeleGeographySubmarineCableLayer(options = {}) {
|
||||||
return createSubmarineCableLayer({
|
return createApplicationCables({
|
||||||
source: createBundledCableSource(),
|
source: createBundledCableSource(),
|
||||||
overlayHost,
|
|
||||||
screenSpaceEventHandlerFactory: (canvas) =>
|
|
||||||
new Cesium.ScreenSpaceEventHandler(canvas),
|
|
||||||
mapStackEventTarget: typeof window !== 'undefined' ? window : null,
|
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -631,6 +631,8 @@ test('the depth decision is the dated Option-2 host migration, superseding Optio
|
|||||||
test('cables create no native labels and no per-frame geometry callbacks', () => {
|
test('cables create no native labels and no per-frame geometry callbacks', () => {
|
||||||
const source = [
|
const source = [
|
||||||
'../../data/telegeographySubmarineCables.js',
|
'../../data/telegeographySubmarineCables.js',
|
||||||
|
'../../app/layers/submarineCables.js',
|
||||||
|
'../../app/layers/overlayHost.js',
|
||||||
'rendering.js',
|
'rendering.js',
|
||||||
'overlay.js',
|
'overlay.js',
|
||||||
'surface.js',
|
'surface.js',
|
||||||
|
|||||||
+2
-5
@@ -1,8 +1,6 @@
|
|||||||
|
import { createApplicationTraffic } from '../app/layers/traffic.js';
|
||||||
import { createSourceSlot } from '../app/sourceSlot.js';
|
import { createSourceSlot } from '../app/sourceSlot.js';
|
||||||
import { createTrafficSource } from '../layers/traffic/source.js';
|
import { createTrafficSource } from '../layers/traffic/source.js';
|
||||||
import { createTrafficLayer } from '../layers/traffic/index.js';
|
|
||||||
import * as credits from './dataCredits.js';
|
|
||||||
import * as render from '../renderGovernor.js';
|
|
||||||
|
|
||||||
const sourceSlot = createSourceSlot(
|
const sourceSlot = createSourceSlot(
|
||||||
createTrafficSource(),
|
createTrafficSource(),
|
||||||
@@ -14,9 +12,8 @@ const sourceSlot = createSourceSlot(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
export const configureTrafficSource = sourceSlot.configure;
|
export const configureTrafficSource = sourceSlot.configure;
|
||||||
const layer = createTrafficLayer({
|
const layer = createApplicationTraffic({
|
||||||
source: sourceSlot.source,
|
source: sourceSlot.source,
|
||||||
services: { credits, render },
|
|
||||||
});
|
});
|
||||||
export const getTrafficTimingDiagnostics = layer.getTrafficTimingDiagnostics;
|
export const getTrafficTimingDiagnostics = layer.getTrafficTimingDiagnostics;
|
||||||
export const deriveTrafficFlowError = layer.deriveTrafficFlowError;
|
export const deriveTrafficFlowError = layer.deriveTrafficFlowError;
|
||||||
|
|||||||
@@ -22,7 +22,10 @@ async function launch(overrides = {}, dotenv = '', omitCctv = false) {
|
|||||||
}
|
}
|
||||||
await fs.mkdir(path.join(root, 'src', 'standalone'), { recursive: true });
|
await fs.mkdir(path.join(root, 'src', 'standalone'), { recursive: true });
|
||||||
const catalog = await fs.readFile(new URL('./standalone/catalog.js', import.meta.url), 'utf8');
|
const catalog = await fs.readFile(new URL('./standalone/catalog.js', import.meta.url), 'utf8');
|
||||||
await fs.writeFile(path.join(root, 'src', 'standalone', 'catalog.js'), omitCctv ? catalog.replace(/^\s*cctvLayer,\s*$/m, '') : catalog);
|
await fs.writeFile(path.join(root, 'src', 'standalone', 'catalog.js'), catalog);
|
||||||
|
await fs.mkdir(path.join(root, 'src', 'app'), { recursive: true });
|
||||||
|
const assembly = await fs.readFile(new URL('./app/constructCatalog.js', import.meta.url), 'utf8');
|
||||||
|
await fs.writeFile(path.join(root, 'src', 'app', 'constructCatalog.js'), omitCctv ? assembly.replace(/^\s*createApplicationCctv\(.*$/m, '') : assembly);
|
||||||
await fs.mkdir(path.join(root, 'node_modules'));
|
await fs.mkdir(path.join(root, 'node_modules'));
|
||||||
await fs.symlink(fileURLToPath(new URL('.', import.meta.resolve('vite/package.json'))), path.join(root, 'node_modules', 'vite'), 'dir');
|
await fs.symlink(fileURLToPath(new URL('.', import.meta.resolve('vite/package.json'))), path.join(root, 'node_modules', 'vite'), 'dir');
|
||||||
await fs.writeFile(path.join(root, '.env'), dotenv);
|
await fs.writeFile(path.join(root, '.env'), dotenv);
|
||||||
|
|||||||
@@ -23,9 +23,10 @@ export function createStandaloneApplication({
|
|||||||
const loadingScreen = document.getElementById('loading-screen');
|
const loadingScreen = document.getElementById('loading-screen');
|
||||||
const loaderStatus = loadingScreen.querySelector('.loader-status');
|
const loaderStatus = loadingScreen.querySelector('.loader-status');
|
||||||
let placeSearch;
|
let placeSearch;
|
||||||
const catalog = createStandaloneCatalog();
|
let catalog;
|
||||||
return createApplication({
|
return createApplication({
|
||||||
createScene: (context) => {
|
createScene: (context) => {
|
||||||
|
catalog = createStandaloneCatalog({ signal: context.signal });
|
||||||
placeSearch = createStandalonePlaceSearch({
|
placeSearch = createStandalonePlaceSearch({
|
||||||
...geospatial,
|
...geospatial,
|
||||||
resolveApiKey: () => googleApiKey,
|
resolveApiKey: () => googleApiKey,
|
||||||
|
|||||||
+20
-37
@@ -1,40 +1,23 @@
|
|||||||
import flightsLayer from '../data/flights.js';
|
import { createApplicationCatalog } from '../app/constructCatalog.js';
|
||||||
import militaryFlightsLayer from '../data/militaryFlights.js';
|
import { createStandaloneLayerSources } from './layerSources.js';
|
||||||
import alprCamerasLayer from '../data/alprCameras.js';
|
export { createStandaloneReferenceSources } from './layerSources.js';
|
||||||
import earthquakesLayer from '../data/earthquakes.js';
|
|
||||||
import satellitesLayer from '../data/satellites.js';
|
|
||||||
import rocketLaunchesLayer from '../data/rocketLaunches.js';
|
|
||||||
import trafficLayer from '../data/traffic.js';
|
|
||||||
import cctvLayer from '../data/cctv.js';
|
|
||||||
import radioLayer from '../data/radio.js';
|
|
||||||
import bikeshareLayer from '../data/bikeshare.js';
|
|
||||||
import aisLiveVesselsLayer from '../data/aisLiveVessels.js';
|
|
||||||
import militaryInstallationsLayer from '../data/militaryInstallations.js';
|
|
||||||
import militaryAwarenessLayer from '../data/militaryAwareness.js';
|
|
||||||
import localDataLayers from '../data/localLayers.js';
|
|
||||||
import { LAYER_STATE_REGISTRY } from '../data/layerState.js';
|
|
||||||
|
|
||||||
import { createLayerCatalog } from '../app/catalog.js';
|
/** Create fresh layer instances using the existing standalone source choices. */
|
||||||
|
export function createStandaloneCatalog({
|
||||||
|
signal = new AbortController().signal,
|
||||||
|
} = {}) {
|
||||||
|
return createApplicationCatalog({
|
||||||
|
sources: createStandaloneLayerSources(),
|
||||||
|
signal,
|
||||||
|
vesselOptions: {
|
||||||
|
maxRows: import.meta.env?.VITE_AIS_LIVE_MAX_ROWS,
|
||||||
|
maxLabels: import.meta.env?.VITE_AIS_LIVE_LABEL_MAX_ROWS,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** Select the existing standalone instances and current persistence schema. */
|
// Direct compatibility callers share one catalog; application startup supplies its own.
|
||||||
export function createStandaloneCatalog() {
|
let compatibilityCatalog;
|
||||||
return createLayerCatalog(
|
export function getStandaloneCatalog() {
|
||||||
[
|
return (compatibilityCatalog ||= createStandaloneCatalog());
|
||||||
flightsLayer,
|
|
||||||
militaryFlightsLayer,
|
|
||||||
earthquakesLayer,
|
|
||||||
alprCamerasLayer,
|
|
||||||
satellitesLayer,
|
|
||||||
rocketLaunchesLayer,
|
|
||||||
trafficLayer,
|
|
||||||
cctvLayer,
|
|
||||||
radioLayer,
|
|
||||||
bikeshareLayer,
|
|
||||||
aisLiveVesselsLayer,
|
|
||||||
militaryInstallationsLayer,
|
|
||||||
militaryAwarenessLayer,
|
|
||||||
...localDataLayers,
|
|
||||||
],
|
|
||||||
LAYER_STATE_REGISTRY,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Vendored
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
import { createApplicationControls } from '../app/controls.js';
|
import { createApplicationControls } from '../app/controls.js';
|
||||||
import { createStandaloneCatalog } from './catalog.js';
|
import { getStandaloneCatalog } from './catalog.js';
|
||||||
export function createStandaloneControls(options) {
|
export function createStandaloneControls(options) {
|
||||||
return createApplicationControls({
|
return createApplicationControls({
|
||||||
catalog: createStandaloneCatalog(),
|
catalog: options?.catalog ?? getStandaloneCatalog(),
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { createApplicationData } from '../app/data.js';
|
import { createApplicationData } from '../app/data.js';
|
||||||
import { createStandaloneCatalog } from './catalog.js';
|
import { getStandaloneCatalog } from './catalog.js';
|
||||||
export function createStandaloneData(options) {
|
export function createStandaloneData(options) {
|
||||||
return createApplicationData({
|
return createApplicationData({
|
||||||
catalog: createStandaloneCatalog(),
|
catalog: options?.catalog ?? getStandaloneCatalog(),
|
||||||
...options,
|
...options,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
import {
|
||||||
|
createOpenSkySource,
|
||||||
|
createAdsbLolSource,
|
||||||
|
createAisStreamSource,
|
||||||
|
} from '../sources/live/standalone.js';
|
||||||
|
import { createCctvSource } from '../layers/cctv/source.js';
|
||||||
|
import { createRadioSource } from '../layers/radio/source.js';
|
||||||
|
import { createTrafficSource } from '../layers/traffic/source.js';
|
||||||
|
import { createBikeshareSource } from '../layers/bikeshare/source.js';
|
||||||
|
import { createInstallationSource } from '../layers/installations/index.js';
|
||||||
|
import { createSatelliteSource } from '../layers/satellites/index.js';
|
||||||
|
import { createLaunchSource } from '../layers/launches/index.js';
|
||||||
|
import { createOverpassAlprSource } from '../layers/alpr/index.js';
|
||||||
|
import { createFirmsSource } from '../layers/firms/index.js';
|
||||||
|
import { createUsgsEarthquakeSource } from '../layers/earthquakes/source.js';
|
||||||
|
import { createBundledCableSource } from '../layers/submarineCables/bundledSource.js';
|
||||||
|
|
||||||
|
/** Existing reference feeds, usable independently of live source selection. */
|
||||||
|
export function createStandaloneReferenceSources() {
|
||||||
|
return {
|
||||||
|
earthquakes: createUsgsEarthquakeSource(),
|
||||||
|
cables: createBundledCableSource(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Select standalone providers without starting their acquisition. */
|
||||||
|
export function createStandaloneLayerSources() {
|
||||||
|
return {
|
||||||
|
...createStandaloneReferenceSources(),
|
||||||
|
flights: createOpenSkySource(),
|
||||||
|
military: createAdsbLolSource(),
|
||||||
|
vessels: createAisStreamSource({
|
||||||
|
apiUrl: import.meta.env?.VITE_AIS_LIVE_API_URL || '/api/ais-live',
|
||||||
|
}),
|
||||||
|
cctv: createCctvSource(),
|
||||||
|
radio: createRadioSource(),
|
||||||
|
traffic: createTrafficSource(),
|
||||||
|
bikeshare: createBikeshareSource(),
|
||||||
|
installations: createInstallationSource(),
|
||||||
|
satellites: createSatelliteSource(),
|
||||||
|
launches: createLaunchSource(),
|
||||||
|
alpr: createOverpassAlprSource(),
|
||||||
|
firms: createFirmsSource(),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,2 +1,15 @@
|
|||||||
/** Compatibility entry for the application's UI composition. */
|
import { StyleManager as ApplicationControls } from './ui/composition.js';
|
||||||
export { StyleManager } from './ui/composition.js';
|
import { catalogControlServices } from './app/catalog.js';
|
||||||
|
import { getStandaloneCatalog } from './standalone/catalog.js';
|
||||||
|
/** Compatibility entry for direct standalone control construction. */
|
||||||
|
export class StyleManager extends ApplicationControls {
|
||||||
|
constructor(viewer, options = {}) {
|
||||||
|
super(viewer, {
|
||||||
|
...options,
|
||||||
|
services: {
|
||||||
|
...catalogControlServices(getStandaloneCatalog()),
|
||||||
|
...options.services,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,18 +40,7 @@ import {
|
|||||||
setDetectionStyle,
|
setDetectionStyle,
|
||||||
setDetectionTuning,
|
setDetectionTuning,
|
||||||
} from '../data/detection.js';
|
} from '../data/detection.js';
|
||||||
import trafficLayer from '../data/traffic.js';
|
|
||||||
import flightsLayer from '../data/flights.js';
|
|
||||||
import militaryFlightsLayer from '../data/militaryFlights.js';
|
|
||||||
import { isTr3b, toggleTr3b } from '../data/tr3bRegistry.js';
|
import { isTr3b, toggleTr3b } from '../data/tr3bRegistry.js';
|
||||||
import satellitesLayer from '../data/satellites.js';
|
|
||||||
import cctvLayer from '../data/cctv.js';
|
|
||||||
import radioLayer from '../data/radio.js';
|
|
||||||
import bikeshareLayer from '../data/bikeshare.js';
|
|
||||||
import aisLiveVesselsLayer from '../data/aisLiveVessels.js';
|
|
||||||
import militaryAwarenessLayer from '../data/militaryAwareness.js';
|
|
||||||
import militaryInstallationsLayer from '../data/militaryInstallations.js';
|
|
||||||
import rocketLaunchesLayer from '../data/rocketLaunches.js';
|
|
||||||
import {
|
import {
|
||||||
cachedGroundFloor,
|
cachedGroundFloor,
|
||||||
cachedMeshFloor,
|
cachedMeshFloor,
|
||||||
@@ -114,19 +103,8 @@ export class StyleManager extends ApplicationShell {
|
|||||||
resumeDetection,
|
resumeDetection,
|
||||||
setDetectionStyle,
|
setDetectionStyle,
|
||||||
setDetectionTuning,
|
setDetectionTuning,
|
||||||
trafficLayer,
|
|
||||||
flightsLayer,
|
|
||||||
militaryFlightsLayer,
|
|
||||||
isTr3b,
|
isTr3b,
|
||||||
toggleTr3b,
|
toggleTr3b,
|
||||||
satellitesLayer,
|
|
||||||
cctvLayer,
|
|
||||||
radioLayer,
|
|
||||||
bikeshareLayer,
|
|
||||||
aisLiveVesselsLayer,
|
|
||||||
militaryAwarenessLayer,
|
|
||||||
militaryInstallationsLayer,
|
|
||||||
rocketLaunchesLayer,
|
|
||||||
cachedGroundFloor,
|
cachedGroundFloor,
|
||||||
cachedMeshFloor,
|
cachedMeshFloor,
|
||||||
GROUND_FLOOR_LIFT_M,
|
GROUND_FLOOR_LIFT_M,
|
||||||
|
|||||||
+16
-20
@@ -8,15 +8,10 @@ import {
|
|||||||
getSelectedEntityContext,
|
getSelectedEntityContext,
|
||||||
isContextRecordActive,
|
isContextRecordActive,
|
||||||
} from '../data/contextStore.js';
|
} from '../data/contextStore.js';
|
||||||
import { getNextIssPass } from '../data/satellites.js';
|
import { CCTV_FOCUS_RESULT } from '../layers/cctv/index.js';
|
||||||
import { CCTV_FOCUS_RESULT } from '../data/cctv.js';
|
|
||||||
import { contextModeWord } from '../contextModePolicy.js';
|
import { contextModeWord } from '../contextModePolicy.js';
|
||||||
import { createAnalystEngine } from '../data/analystEngine.js';
|
import { createAnalystEngine } from '../data/analystEngine.js';
|
||||||
import { layerFeedState } from '../data/manager.js';
|
import { layerFeedState } from '../data/manager.js';
|
||||||
import militaryAwarenessLayer, {
|
|
||||||
collectAircraftProximityWindow,
|
|
||||||
contactsWindowFromSnapshot,
|
|
||||||
} from '../data/militaryAwareness.js';
|
|
||||||
import { initCameraVerbs, moveCamera, flyRoute, interruptCameraMotion, adjustOrbitRange } from '../cameraVerbs.js';
|
import { initCameraVerbs, moveCamera, flyRoute, interruptCameraMotion, adjustOrbitRange } from '../cameraVerbs.js';
|
||||||
import { cachedGroundFloor, warmGroundFloor } from '../data/groundFloor.js';
|
import { cachedGroundFloor, warmGroundFloor } from '../data/groundFloor.js';
|
||||||
import { isPickedWorldPosition } from '../data/scenePick.js';
|
import { isPickedWorldPosition } from '../data/scenePick.js';
|
||||||
@@ -612,7 +607,7 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
const contactsWindow = ['contacts', 'flights'].includes(mode)
|
const contactsWindow = ['contacts', 'flights'].includes(mode)
|
||||||
? activeContactsWindow()
|
? activeContactsWindow(dataManager)
|
||||||
: null;
|
: null;
|
||||||
return {
|
return {
|
||||||
...withContextModeVocabulary(result),
|
...withContextModeVocabulary(result),
|
||||||
@@ -792,7 +787,7 @@ export function createGevActionRunner({ viewer, styleManager, dataManager, scene
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'next_iss_pass') {
|
if (name === 'next_iss_pass') {
|
||||||
return nextIssPass(viewer, args);
|
return nextIssPass(viewer, dataManager, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === 'analyst_query') {
|
if (name === 'analyst_query') {
|
||||||
@@ -2007,7 +2002,7 @@ function compassDir(azDeg) {
|
|||||||
return COMPASS_16[Math.round(((((azDeg % 360) + 360) % 360)) / 22.5) % 16];
|
return COMPASS_16[Math.round(((((azDeg % 360) + 360) % 360)) / 22.5) % 16];
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextIssPass(viewer, args) {
|
function nextIssPass(viewer, dataManager, args) {
|
||||||
let latDeg = Number.isFinite(args.latitude) ? args.latitude : null;
|
let latDeg = Number.isFinite(args.latitude) ? args.latitude : null;
|
||||||
let lonDeg = Number.isFinite(args.longitude) ? args.longitude : null;
|
let lonDeg = Number.isFinite(args.longitude) ? args.longitude : null;
|
||||||
if (latDeg == null || lonDeg == null) {
|
if (latDeg == null || lonDeg == null) {
|
||||||
@@ -2017,7 +2012,7 @@ function nextIssPass(viewer, args) {
|
|||||||
lonDeg = Cesium.Math.toDegrees(carto.longitude);
|
lonDeg = Cesium.Math.toDegrees(carto.longitude);
|
||||||
}
|
}
|
||||||
const minElevDeg = Number.isFinite(args.minElevationDeg) ? args.minElevationDeg : 10;
|
const minElevDeg = Number.isFinite(args.minElevationDeg) ? args.minElevationDeg : 10;
|
||||||
const result = getNextIssPass({ latDeg, lonDeg, minElevDeg });
|
const result = dataManager?.layers?.get('satellites')?.module?.getNextIssPass?.({ latDeg, lonDeg, minElevDeg }) ?? { status: 'no-tle' };
|
||||||
if (result.status === 'no-tle') {
|
if (result.status === 'no-tle') {
|
||||||
return {
|
return {
|
||||||
ok: false,
|
ok: false,
|
||||||
@@ -2337,7 +2332,7 @@ function getCurrentViewState(viewer, styleManager, dataManager, sceneDirector =
|
|||||||
...withContextModeVocabulary(styleManager.getContextModeState()),
|
...withContextModeVocabulary(styleManager.getContextModeState()),
|
||||||
// The numbers on the operator's Contacts panel, so a window/count
|
// The numbers on the operator's Contacts panel, so a window/count
|
||||||
// question can be answered from what they are looking at.
|
// question can be answered from what they are looking at.
|
||||||
...(activeContactsWindow() ? { contactsWindow: activeContactsWindow() } : {}),
|
...(activeContactsWindow(dataManager) ? { contactsWindow: activeContactsWindow(dataManager) } : {}),
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
cockpit: typeof styleManager.getCockpitState === 'function'
|
cockpit: typeof styleManager.getCockpitState === 'function'
|
||||||
@@ -2408,19 +2403,19 @@ async function getEntityContext(viewer, dataManager, styleManager, args = {}, se
|
|||||||
* @param {object} result The general engine's result, reused for scope text.
|
* @param {object} result The general engine's result, reused for scope text.
|
||||||
* @returns {object|null} A unified-count payload, or null.
|
* @returns {object|null} A unified-count payload, or null.
|
||||||
*/
|
*/
|
||||||
function aircraftProximityWindowForQuery(args, result) {
|
function aircraftProximityWindowForQuery(dataManager, args, result) {
|
||||||
const scope = args?.scope;
|
const scope = args?.scope;
|
||||||
if (String(scope?.kind || '').toLowerCase() !== 'radius') return null;
|
if (String(scope?.kind || '').toLowerCase() !== 'radius') return null;
|
||||||
const layers = Array.isArray(args.layers) ? args.layers : [];
|
const layers = Array.isArray(args.layers) ? args.layers : [];
|
||||||
if (!layers.some((layer) => layer === 'flights' || layer === 'military')) return null;
|
if (!layers.some((layer) => layer === 'flights' || layer === 'military')) return null;
|
||||||
const snapshot = militaryAwarenessLayer.getContextSnapshot?.();
|
const snapshot = dataManager?.layers?.get('military-awareness')?.module?.getContextSnapshot?.();
|
||||||
const subject = snapshot?.subject;
|
const subject = snapshot?.subject;
|
||||||
if (!subject?.position) return null;
|
if (!subject?.position) return null;
|
||||||
// An explicit centre only qualifies when it IS the subject; otherwise the
|
// An explicit centre only qualifies when it IS the subject; otherwise the
|
||||||
// operator asked about somewhere else and must get that answer.
|
// operator asked about somewhere else and must get that answer.
|
||||||
if (scope.center && !centerMatchesSubject(scope.center, subject.position)) return null;
|
if (scope.center && !centerMatchesSubject(scope.center, subject.position)) return null;
|
||||||
const radiusM = Number.isFinite(scope.km) ? scope.km * 1000 : (snapshot.radiusM || 250_000);
|
const radiusM = Number.isFinite(scope.km) ? scope.km * 1000 : (snapshot.radiusM || 250_000);
|
||||||
const window = collectAircraftProximityWindow(subject.position, { radiusM, subject });
|
const window = dataManager.layers.get('military-awareness').module.collectAircraftProximityWindow(subject.position, { radiusM, subject });
|
||||||
if (!window) return null;
|
if (!window) return null;
|
||||||
const label = subject.label || subject.id || 'the selected contact';
|
const label = subject.label || subject.id || 'the selected contact';
|
||||||
const radiusKm = Math.round(radiusM / 1000);
|
const radiusKm = Math.round(radiusM / 1000);
|
||||||
@@ -2459,7 +2454,7 @@ function aircraftProximityWindowForQuery(args, result) {
|
|||||||
military: window.military.length,
|
military: window.military.length,
|
||||||
aircraft: window.aircraft,
|
aircraft: window.aircraft,
|
||||||
},
|
},
|
||||||
...(activeContactsWindow() ? { contactsWindow: activeContactsWindow() } : {}),
|
...(activeContactsWindow(dataManager) ? { contactsWindow: activeContactsWindow(dataManager) } : {}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3235,9 +3230,10 @@ const VIEWPORT_LOADED_LAYERS = new Set(['flights']);
|
|||||||
* diverge no matter which surface asks.
|
* diverge no matter which surface asks.
|
||||||
* @returns {object|null} Panel-equivalent window counts.
|
* @returns {object|null} Panel-equivalent window counts.
|
||||||
*/
|
*/
|
||||||
function activeContactsWindow() {
|
function activeContactsWindow(dataManager) {
|
||||||
try {
|
try {
|
||||||
return contactsWindowFromSnapshot(militaryAwarenessLayer.getContextSnapshot?.());
|
const layer = dataManager?.layers?.get('military-awareness')?.module;
|
||||||
|
return layer?.contactsWindowFromSnapshot?.(layer.getContextSnapshot?.()) ?? null;
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -3263,7 +3259,7 @@ function analystProviders(viewer, dataManager, { recordLimitByLayer = null, plac
|
|||||||
* @returns {{lat: number, lon: number, label: string|null}|null} Subject centre.
|
* @returns {{lat: number, lon: number, label: string|null}|null} Subject centre.
|
||||||
*/
|
*/
|
||||||
getContextSubject() {
|
getContextSubject() {
|
||||||
const snapshot = militaryAwarenessLayer.getContextSnapshot?.();
|
const snapshot = dataManager?.layers?.get('military-awareness')?.module?.getContextSnapshot?.();
|
||||||
const subject = snapshot?.subject;
|
const subject = snapshot?.subject;
|
||||||
if (!subject?.position) return null;
|
if (!subject?.position) return null;
|
||||||
const carto = Cesium.Cartographic.fromCartesian(subject.position);
|
const carto = Cesium.Cartographic.fromCartesian(subject.position);
|
||||||
@@ -3343,10 +3339,10 @@ async function runAnalystQuery(viewer, dataManager, args = {}, placeSearch = una
|
|||||||
// differ. The generic record/scope engine still owns explicit regions and
|
// differ. The generic record/scope engine still owns explicit regions and
|
||||||
// arbitrary points — only "how many aircraft around <this contact>" is
|
// arbitrary points — only "how many aircraft around <this contact>" is
|
||||||
// unified, because that is the question the panel is already answering.
|
// unified, because that is the question the panel is already answering.
|
||||||
const entityWindow = aircraftProximityWindowForQuery(args, result);
|
const entityWindow = aircraftProximityWindowForQuery(dataManager, args, result);
|
||||||
if (entityWindow) return entityWindow;
|
if (entityWindow) return entityWindow;
|
||||||
|
|
||||||
const contactsWindow = activeContactsWindow();
|
const contactsWindow = activeContactsWindow(dataManager);
|
||||||
const aircraftQueried = (result.coverage?.layersQueried || [])
|
const aircraftQueried = (result.coverage?.layersQueried || [])
|
||||||
.some((l) => l.layerKey === 'flights' || l.layerKey === 'military');
|
.some((l) => l.layerKey === 'flights' || l.layerKey === 'military');
|
||||||
// Both numbers, and which one answers the question. The window counts have
|
// Both numbers, and which one answers the question. The window counts have
|
||||||
|
|||||||
@@ -2838,7 +2838,7 @@ async function withAwareness(harness, run) {
|
|||||||
: originals.cartoFrom(value)
|
: originals.cartoFrom(value)
|
||||||
);
|
);
|
||||||
try {
|
try {
|
||||||
return await run();
|
return await run(awareness);
|
||||||
} finally {
|
} finally {
|
||||||
awareness.getContextSnapshot = originals.snapshot;
|
awareness.getContextSnapshot = originals.snapshot;
|
||||||
flightsLayer.getNearby = originals.flightsNearby;
|
flightsLayer.getNearby = originals.flightsNearby;
|
||||||
@@ -2847,7 +2847,7 @@ async function withAwareness(harness, run) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function analystRunner() {
|
function analystRunner(awareness) {
|
||||||
const flights = {
|
const flights = {
|
||||||
id: 'flights',
|
id: 'flights',
|
||||||
// Deliberately a DIFFERENT population from the proximity window: this is
|
// Deliberately a DIFFERENT population from the proximity window: this is
|
||||||
@@ -2869,7 +2869,7 @@ function analystRunner() {
|
|||||||
viewer,
|
viewer,
|
||||||
styleManager: {},
|
styleManager: {},
|
||||||
dataManager: {
|
dataManager: {
|
||||||
layers: new Map([['flights', { module: flights }]]),
|
layers: new Map([['flights', { module: flights }], ['military-awareness', { module: awareness }]]),
|
||||||
isEnabled: (id) => id === 'flights',
|
isEnabled: (id) => id === 'flights',
|
||||||
getAll: () => [{ id: 'flights', name: 'Live Flights', enabled: true, stats: { count: 1 } }],
|
getAll: () => [{ id: 'flights', name: 'Live Flights', enabled: true, stats: { count: 1 } }],
|
||||||
},
|
},
|
||||||
@@ -2883,8 +2883,8 @@ test('front5: a nearby ask centres on the Contacts SUBJECT, not the selected dat
|
|||||||
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}`, distance: 1000 * i })),
|
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}`, distance: 1000 * i })),
|
||||||
military: Array.from({ length: 5 }, (_, i) => ({ id: `M${i}`, icao24: `m${i}`, distance: 500 * i })),
|
military: Array.from({ length: 5 }, (_, i) => ({ id: `M${i}`, icao24: `m${i}`, distance: 500 * i })),
|
||||||
});
|
});
|
||||||
await withAwareness(harness, async () => {
|
await withAwareness(harness, async (awareness) => {
|
||||||
const runner = analystRunner();
|
const runner = analystRunner(awareness);
|
||||||
const result = await runner('analyst_query', {
|
const result = await runner('analyst_query', {
|
||||||
layers: ['flights', 'military'],
|
layers: ['flights', 'military'],
|
||||||
// The centre the model reached for in the field: the selected datacenter.
|
// The centre the model reached for in the field: the selected datacenter.
|
||||||
@@ -2913,13 +2913,13 @@ test('front5: the spoken count and the panel window are ONE number by constructi
|
|||||||
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}` })),
|
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}` })),
|
||||||
military: Array.from({ length: 5 }, (_, i) => ({ id: `M${i}`, icao24: `m${i}` })),
|
military: Array.from({ length: 5 }, (_, i) => ({ id: `M${i}`, icao24: `m${i}` })),
|
||||||
});
|
});
|
||||||
await withAwareness(harness, async () => {
|
await withAwareness(harness, async (awareness) => {
|
||||||
// What the PANEL computes for this subject...
|
// What the PANEL computes for this subject...
|
||||||
const panel = collectAircraftProximityWindow(harness.snapshot.subject.position, {
|
const panel = collectAircraftProximityWindow(harness.snapshot.subject.position, {
|
||||||
subject: harness.snapshot.subject,
|
subject: harness.snapshot.subject,
|
||||||
});
|
});
|
||||||
// ...and what VOICE answers for the same subject.
|
// ...and what VOICE answers for the same subject.
|
||||||
const spoken = await analystRunner()('analyst_query', {
|
const spoken = await analystRunner(awareness)('analyst_query', {
|
||||||
layers: ['flights', 'military'],
|
layers: ['flights', 'military'],
|
||||||
scope: { kind: 'radius', km: 250 },
|
scope: { kind: 'radius', km: 250 },
|
||||||
});
|
});
|
||||||
@@ -2936,8 +2936,8 @@ test('front5: the spoken count and the panel window are ONE number by constructi
|
|||||||
test('front5: Contacts active with NO subject falls back to the view, not an empty panel', async () => {
|
test('front5: Contacts active with NO subject falls back to the view, not an empty panel', async () => {
|
||||||
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
|
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
|
||||||
const harness = awarenessSubjectHarness({ subject: null });
|
const harness = awarenessSubjectHarness({ subject: null });
|
||||||
await withAwareness(harness, async () => {
|
await withAwareness(harness, async (awareness) => {
|
||||||
const result = await analystRunner()('analyst_query', {
|
const result = await analystRunner(awareness)('analyst_query', {
|
||||||
layers: ['flights'],
|
layers: ['flights'],
|
||||||
scope: { kind: 'radius', km: 250 },
|
scope: { kind: 'radius', km: 250 },
|
||||||
});
|
});
|
||||||
@@ -2954,8 +2954,8 @@ test('front5: an explicit region still uses the region engine while Contacts is
|
|||||||
subject: { id: 'a1b2c3', label: 'N546PC' },
|
subject: { id: 'a1b2c3', label: 'N546PC' },
|
||||||
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}` })),
|
flights: Array.from({ length: 111 }, (_, i) => ({ id: `F${i}`, icao24: `f${i}` })),
|
||||||
});
|
});
|
||||||
await withAwareness(harness, async () => {
|
await withAwareness(harness, async (awareness) => {
|
||||||
const result = await analystRunner()('analyst_query', {
|
const result = await analystRunner(awareness)('analyst_query', {
|
||||||
layers: ['flights'],
|
layers: ['flights'],
|
||||||
scope: { kind: 'region', name: 'Texas' },
|
scope: { kind: 'region', name: 'Texas' },
|
||||||
});
|
});
|
||||||
@@ -2988,8 +2988,8 @@ test('front5: the box DIAGONAL is not the subject — 1.32 km away is somewhere
|
|||||||
// separation is 1.32 km. This is the case the coordinator flagged: a centre
|
// separation is 1.32 km. This is the case the coordinator flagged: a centre
|
||||||
// far enough to be a different place, slipping through on the diagonal.
|
// far enough to be a different place, slipping through on the diagonal.
|
||||||
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
|
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
|
||||||
await withAwareness(subjectWindowHarness(), async () => {
|
await withAwareness(subjectWindowHarness(), async (awareness) => {
|
||||||
const result = await analystRunner()('analyst_query', {
|
const result = await analystRunner(awareness)('analyst_query', {
|
||||||
layers: ['flights', 'military'],
|
layers: ['flights', 'military'],
|
||||||
scope: { kind: 'radius', km: 250, center: { lat: 29.9 + 0.009, lon: -97.9 + 0.009 } },
|
scope: { kind: 'radius', km: 250, center: { lat: 29.9 + 0.009, lon: -97.9 + 0.009 } },
|
||||||
});
|
});
|
||||||
@@ -3009,8 +3009,8 @@ test('front5: 0.99 km due EAST is the subject, though a degree box rejects it',
|
|||||||
// 0.99 km — inside 1 km — yet over the 0.01 box threshold. A box would send
|
// 0.99 km — inside 1 km — yet over the 0.01 box threshold. A box would send
|
||||||
// the operator a different, smaller number for a centre that IS the contact.
|
// the operator a different, smaller number for a centre that IS the contact.
|
||||||
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
|
globalThis.window = globalThis.window || { clearTimeout, setTimeout, requestIdleCallback: null };
|
||||||
await withAwareness(subjectWindowHarness(), async () => {
|
await withAwareness(subjectWindowHarness(), async (awareness) => {
|
||||||
const result = await analystRunner()('analyst_query', {
|
const result = await analystRunner(awareness)('analyst_query', {
|
||||||
layers: ['flights', 'military'],
|
layers: ['flights', 'military'],
|
||||||
scope: { kind: 'radius', km: 250, center: { lat: 29.9, lon: -97.9 + 0.0103 } },
|
scope: { kind: 'radius', km: 250, center: { lat: 29.9, lon: -97.9 + 0.0103 } },
|
||||||
});
|
});
|
||||||
@@ -3164,3 +3164,21 @@ test('ALPR common names toggle only the registered camera layer through the norm
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('ISS voice lookup uses the registered satellite instance', async () => {
|
||||||
|
const calls = [];
|
||||||
|
const viewer = {
|
||||||
|
clock: { onTick: { addEventListener: () => () => {} } },
|
||||||
|
scene: { canvas: { addEventListener() {}, removeEventListener() {} } },
|
||||||
|
camera: { moveEnd: { addEventListener() {} } },
|
||||||
|
};
|
||||||
|
const runner = createGevActionRunner({ viewer, styleManager: {}, dataManager: {
|
||||||
|
layers: new Map([['satellites', { module: { getNextIssPass(query) {
|
||||||
|
calls.push(query);
|
||||||
|
return { status: 'none' };
|
||||||
|
} } }]]),
|
||||||
|
} });
|
||||||
|
const result = await runner('next_iss_pass', { latitude: 30, longitude: -97, minElevationDeg: 15 });
|
||||||
|
assert.deepEqual(calls, [{ latDeg: 30, lonDeg: -97, minElevDeg: 15 }]);
|
||||||
|
assert.match(result.error, /No ISS pass above 15/);
|
||||||
|
});
|
||||||
Reference in new issue
Block a user