engine.imagery draws raster layers over the globe: Web Mercator tile
templates and single images or canvases stretched over a rectangle, with
alpha, show, a left/right split against the engine's split position and
an order. A layer's source can be replaced in place.
- Cesium puts one ImageryLayer per layer on the globe's imagery stack,
or on the shown 3D tileset's when the globe is hidden, and moves it
when the surface changes; the Recent Imagery request gate moved here.
- Navara draws them in one screen pass after tone mapping that samples up
to eight layers by geodetic position from mipmapped textures and a tile
atlas, under the other drapes and engine.ground.
- The fake engine keeps each layer's state.
The FIRMS far-zoom heat raster and Recent Imagery now draw through it, so
FIRMS' heat seam and both its backends, and Recent Imagery's Cesium and
Navara renderers and tile plan, go. Against HEAD, Cesium's pixels are
identical in all four compared scenes; Navara's orbital heat changes by a
mean of 3.8 from new mipmapping, slightly towards Cesium. The contract
suite passes on both engines (Cesium 68/68, Navara 71/71).
engine.maps.addOverlay stays a Navara-only extension for Sea Marks and
Ski Pistes until imagery has a finer tile path for oblique global views.
- Street Level: coverage sequences are ground corridor sets of up to
120, draped on terrain and tiles as Cesium's ground polylines were;
the marker and view cones are sprite movers turned to their compass
bearing on screen and culled 1 km under WGS84. The layer no longer
loads the Cesium SDK on Navara.
- Traffic: vehicles are two point mover sets (an outline disc under each
fill) scaled and faded with Cesium's NearFarScalar maths; congestion
heat lines are ground corridor sets draped on tiles. Heights, depth,
readiness and the surface key go through the engine, and Navara no
longer loads the Cesium SDK for traffic.
- Directions: the route is one ground corridor set in the annotation
route's dash pattern, maneuvers are point movers and the A/B markers a
disc and a letter sprite; the in-layer Navara branch and the Cesium
point collection go.
Known gaps against Cesium, to close in the contract: the traffic heat
lines lose their glow and pulse and the route its flowing dashes
(no animated ground fills yet), and dense Street Level coverage on
Navara is costly as ground corridors (about 80 to 190 ms a frame at
6,000 to 14,400 sequences) until corridors are binned set-wide.
Coupling: Navara renderer lines fall from 3349 to 2767, Cesium-importing
files from 51 to 32, `viewer` lines from 1591 to 1373, Cesium SDK loads
on Navara from 6 to 4.
- Mapped Installations: one display holds the single copy of the logic
the Cesium and Navara renderers each had (renderable records, painting,
the selection fill, floors, named markers, selection). Discs are point
movers, outlines ground corridors and the selection fill a ground
polygon. Cesium's behaviour wins where they drifted: named areas draw
their outlines (Navara dropped them), discs sit under the moving
contacts, and outlines and the fill pick their site.
- Global Context: the range ring is a halo and a core line on
engine.lines and the awareness logic no longer imports Cesium. The
animated pulse disc, which no primitive draws yet, sits behind one
seam, render/pulseDisc.js, with a Cesium and a Navara backend.
- Datacenters and dams: one display on movers, lines and ground sets
with Cesium's LOD, motion probe, floor sampling and card cohort;
localGeojsonCore.js keeps only the engine-free card and record code
(1381 to 329 lines).
Coupling: layer renderer lines fall from 1011/4832 to 351/3349
(Cesium/Navara), `viewer` lines from 1745 to 1591,
ScreenSpaceEventHandler files from 8 to 6, Navara ground passes from 5
to 4.
Earthquakes, air quality, GNSS Integrity, fire perimeters, Prohibited &
Restricted Airspace, Active Fires, S2 GhostMaps, cyclone advisories and
submarine cables each have one render/display.js on the globe contract;
their render/cesium* and render/navara* files go. Their discs, cells,
polygons and corridors are engine.ground sets, so on Navara they come from
the one ground screen pass with exact colours instead of nine hand-written
classification passes and lit SDK vector layers. Cesium's look stays the
reference:
- Earthquake and air-quality discs have no outline (Cesium drew none on
clamped ellipses); earthquake discs can now be picked on Navara too.
- Perimeters, drone zones and GhostMaps polygons outline their outer
ring only, as Cesium did.
- GNSS cells stay out of the pick pass; cyclone cones, tracks and centres
are culled by a port of Cesium's horizon occluder.
- Submarine cables are one ground corridor set, as Cesium batched them;
landing points, tips and stems are movers and lines.
- FIRMS glows are a sprite set and its cells a ground set. The far-zoom
heat raster, which no primitive draws yet, sits behind one seam,
render/heatField.js, with a Cesium and a Navara backend.
The shared Navara ground helpers that only these layers used go
(navaraGroundCells, the cell and ground geometry, the zone renderers), as
does the ground prototype script.
Coupling: layer renderer lines fall from 1636/6953 to 1011/4832
(Cesium/Navara), Navara ground passes from 7 to 5, Cesium-importing files
from 65 to 53, engine.extensions reads from 26 to 19.
engine.ground draws sets of polygons (with holes), discs, cells and
corridors draped on the surface, with fill and outline colours, order and
picks, written in bulk and committed. Every engine implements it:
- Cesium batches each set into one GroundPrimitive for fills and one
GroundPolylinePrimitive for outlines and pixel corridors, classified
against terrain and 3D tiles; sets that are not pickable stay out of the
pick pass.
- Navara draws them in one screen pass after tone mapping that finds each
pixel's ground from the scene depth and tests the items in its screen
cell; long rings are split into edge bins. Measured at Dubai against
SDK vector layers, the pass matched Cesium's frame time up to 10,000
polygons with a few megabytes instead of 70 to 146, drew exact colours
and outlines, and hid reliably across an engine switch, where vector
layers hidden with update({ show: false }) never hid. Dense corridors
remain its limit (10,000 cost 135 ms a frame).
- The fake engine keeps the same registry and CPU picks.
The contract suite checks it on both engines (Cesium 66/66, Navara 69/69).
Earthquakes and fire perimeters have unwired ground prototypes whose
drawing matches today's Cesium one (0.998 on Cesium, 0.976 on Navara).
docs/GLOBE-PRIMITIVES.md describes the primitives and when to use each.
- Radio: stations, clusters, the selection and its brackets are point
and sprite sets; clusters come from the layer's engine-neutral screen
clusterer with Cesium's EntityCluster rules, and clicks go through the
selection dispatcher. One style module holds the sizes and colours.
- Transit: vehicles are two sprite sets from an up-front atlas, rotated
with Cesium's projected-rotation maths. The selected vehicle's trail is
engine.lines in two lanes and 29 age bands, so trails are no longer
draped over terrain until the contract has ground corridors. The
Cesium contact trail renderer and its re-export go.
- CCTV: markers, wedges, the plane outline and the calibration gizmo
(rings, arrows, handles, drags through engine.input) draw on the
contract. The textured monitor plane and the viewshed volume, which no
primitive expresses yet, sit behind one seam, render/meshes.js, with a
Cesium backend and a Navara backend the Navara mode passes in.
Known gaps against Cesium: transit trails are not draped, and the
surveillance-lens click correction runs only where the engine reports
its style stage (Navara).
Coupling: layer renderer lines fall from 2638/8735 to 1636/6953
(Cesium/Navara), ScreenSpaceEventHandler files from 15 to 10, `viewer`
lines from 2014 to 1901.
qa-wildlife-switch.mjs serves four Salish Sea sightings (three from
iNaturalist, one obscured, and an Acartia orca) through the app's own
iNaturalist and Acartia proxies over faked upstream feeds, then on Cesium,
Navara, Cesium and Navara again checks the badge set and its positions,
picks, the density glow, the privacy cell outline and a clicked card.
Two of the switches are made from the keyboard and must keep the
selection; the last is made with the pointer on the Globe Engine picker,
whose click closes the card like any click outside it.
Each layer now has one render/display.js on the globe contract, the same
factory on both engines; its render/cesium* and render/navara* files go.
Cesium's behaviour is the reference, and the logic that was written twice
is written once:
- AIS vessels: atlas chevrons and engine.lines trails. The display
registers the gev-trail pick claim itself and its trailing visibility
pass now also runs on Navara.
- Satellites: point banks per look, orbit rings as lines turned by their
model matrix, and selection and follow through engine.camera.follow.
The emulated _viewer is gone, and the tracked readout republishes card
text on Navara too.
- Space Missions: dots, reticle, trajectories and tactical orbits as
movers and lines. The layer keeps its own records instead of Entities
and DataSources, and camera work goes through engine.camera. Clicks go
through the selection dispatcher, and hover keeps Cesium's throttle. The
pad zone is its rim only: the contract has no surface fill yet.
- ALPR, bikeshare and OSM Places: one display and, for bikeshare, one
style module each; clicks through engine.input, no
ScreenSpaceEventHandler or EntityCluster. ALPR keeps Navara's horizon
cull, and OSM Places points sample the surface where Cesium clamped them.
Coupling: layer renderer lines fall from 4546/11106 to 2638/8735
(Cesium/Navara), `viewer` lines from 2209 to 2014, ScreenSpaceEventHandler
files from 20 to 15.
In the app the world overlay draws on a canvas of its own above the
globe, so letting every canvas take the pointer sent the suite's clicks
there. Only the engine's canvas takes it now. Without makeCanvas the
pick check's sprite atlas was a plain object no real engine draws, so its
sprites could not be picked; the page now hands the suite real 2D
canvases. --only=prefix,... runs a subset of the checks. With both, Cesium
passes 63 of 63 checks and Navara 66 of 66.
The layers that still had one Cesium and one Navara implementation, or
drew on Cesium only, now draw through engine.movers, engine.lines and
engine.camera on both engines, and application code holds the engine
instead of guessing it from a viewer.
- Aircraft family: flights, military and local ADS-B share one display
(src/layers/aircraft/render/display.js). The Cesium and Navara
renderers are gone. Where they differed, the display follows Cesium:
- attach and visibility tracking, live disableDepthTestDistance
changes, the tracked glyph's distance scale, model ids that change,
local ADS-B markers hidden until shown and its UAT ring as a point;
- the trail head's hidden alpha is 0.45, the contract's
depthFailAlpha scaled by the head's 0.9.
The tracked glyph stays over the fleet, as on Navara. The movers
contract reports readiness per model and reads model axes as the
glTF's own on every engine. Cesium's follow takes any native target
through a proxy entity in the ENU frame and mirrors its readout fields.
- Wildlife Sightings draws badges and the density glow as sprite sets
and its privacy cells as line outlines (no fill yet). It takes clicks
from engine.input and its card anchors to the engine's canvas and
camera, so the layer is in both engines' catalogs again and a switch
keeps the session's layer set.
- Cockpit: cockpitHost takes the engine only. The ridden aircraft is the
engine follow's target, hidden through its sprite; only the Cesium
Viewer's stock double-click is still routed around.
- About forty camera, navigation, share-link, HUD, voice, detection and
layer-lifecycle callers take the engine. isGlobeEngine and
engineFromViewer calls outside src/globe fall from 51 to 14.
The coupling baseline drops accordingly: layer renderer lines from
5239/12331 to 4546/11106 (Cesium/Navara), `viewer` lines from 2424 to
2209, Cesium-importing files from 69 to 66.
What only one engine has now travels with the engine instead of a
WeakMap in navaraMode.js: engine.extensions.navara holds Navara's view,
its SDK classes and the daylight reset, engine.extensions.cesium the
Cesium viewer. The contract documents the field as outside the contract,
and the coupling report counts its reads outside src/globe in place of
the navaraViews reads (27, unchanged).
Layers are to draw through the globe contract on both engines, so Cesium
gets back the movers and lines removed as unused:
- engine.movers draws sprite sets as a BillboardCollection (atlas cells
cropped once per set), point sets as a PointPrimitiveCollection and
model sets as one Model per instance. depthTest, disableWithinM, order,
scaleByDistance, colorBlend, unlit and minimumPixelSize follow the
contract; a model's matrix is written in place, so instances no longer
share one scratch matrix.
- engine.lines draws each line as a polyline Primitive with width in CSS
px, geodesic arcs, dashes, the tactical and arrow materials,
depthFailAlpha and modelMatrix; a rebuild keeps its show, matrix,
colour and place in order.
- The picker returns the pickId a layer wrote last, skips sets that are
not pickable and instances behind the horizon, and falls through to
what lies under them. Surface samples can exclude mover sets again.
movers and lines move from the optional capabilities into the required
contract shape, and the contract suite always runs their checks.
The app sets contact sonar on every frame (src/cyberSonarEngine.js), so a
value the contract suite sets never survives to its next frame inside the
app. qa-contract-suite.mjs reports such checks as skipped, naming their
owner, instead of failing them.
scripts/report-engine-coupling.mjs counts the engine-specific code left
outside src/globe/cesium and src/globe/navara: layer renderer lines per
engine, Cesium imports, `viewer` mentions, engine-name checks, raw Navara
reads, Navara ground passes, Cesium SDK loads and ScreenSpaceEventHandler
users. engineCoupling.test.mjs fails npm test when a count grows past
scripts/engine-coupling-baseline.json, so each step towards drawing
through the globe contract can only lower them.
scripts/qa-contract-suite.mjs runs src/globe/testing/contractSuite.mjs in
headless Chrome on the Cesium and Navara of a keyless dev server, which
until now only ran on the fake engine. The page imports the suite from the
dev server, starts at a fixed view, waits for the camera to rest and lets
the pointer through the panels. Cesium passes 57 of 57 checks, Navara 64
of 67; docs/CURRENT-STATE.md names the three that fail.
Main's camera, voice, Cockpit and layer work now runs on the globe
contract where this branch draws through it:
- The voice tilt to an angle, its pivot about the place under the
crosshair and the frame limit from orbit run through the camera rig;
src/globeInView.js measures a camera pose with src/geo instead of a
Cesium camera. The free-look height limit and the globe-in-view
recovery stay Cesium-only for now.
- Cockpit takes main's own-layer entry rule. Double-clicking the
followed track goes through cockpitHost.routeDoubleClick, so it works
on both engines.
- Space Missions' failed-entry rollback and GhostMaps' age filter run
in the engine-neutral layer code. On Cesium an expired report is
dropped in place; on Navara its product is redrawn.
- Wildlife Sightings joins the catalog Cesium-only: Navara leaves it
out, and the shell builds its card only for the real layer, so a
Navara page no longer fails to start on its stand-in.
- One instruction sentence is shorter, which keeps the Realtime prompt
within its 55,500-character budget with the Sentinel-2 basemap.
Review fixes on top of #1601:
- Only redirect a provider the params switch on to a keyless source, so a
filter-only setParams (360°, SINCE) no longer moves an explicitly chosen
keyless Mapillary to KartaView.
- Keyless QA gate: assert the new KartaView default and that the Mapillary
chip explains its key instead of switching (it used to expect KEY REQUIRED).
- Tests: Graph-proxy keyReady, filter-only updates; shared test helper.
- README and CURRENT-STATE describe the keyless default.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Review fixes on top of #1528:
- Narrowing the age window only removes markers; a product loaded with a
narrower window is reloaded when the window widens again. Fixes markers
missing after 7d -> 1d -> 7d and avoids reparsing every KMZ on narrowing.
- A wider window over the same edition keeps the open report card.
- Date-only event dates cover their whole UTC day, so 1d still shows
yesterday's reports.
- The age options are one labelled "Event age" radio switch (row
`segmented` control, as FIRMS confidence) instead of toggle chips mixed
with the product chips.
- Un-nest the per-product age test; QA compares data-source counts with the
live defaults instead of a hard-coded 2.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Navara now draws at CSS pixels without MSAA, like Cesium; Navara HD keeps the device pixel ratio and MSAA 4. Only one Navara variant is ever kept. A parked engine is released after three minutes and its WebGL context is lost. Three leaks that kept a released Navara alive are fixed, the canvas context drops its unused antialias buffer, cloud shadows use two 256 px cascades, and the tile cache is budgeted from the screen buffers.
The voice runner subscribes to camera handoffs as it is created. The Cyber sonar test builds its runner on a StyleManager made from the prototype, which has no navigation owner, so the facade's delegation threw and both Node jobs failed. The facade now returns nothing there, like its other optional owners; the real shell builds its navigation owner before voice creates a runner.
Recent community wildlife observations, as a new Wildlife group in the
layer panel.
- Server: keyless iNaturalist proxy (v2 observations with only the
projected fields; undocumented v1 UTFGrid density tiles, decoded on the
server) sharing one paced queue (burst 4, ~1/s, 60/min, 8,000/day budget,
up to four in flight), in-memory caches and upstream-only cooldowns;
Acartia Salish Sea marine-mammal feed rounded to 0.01 degrees with
comments dropped.
- Layer: grid counts drawn as a soft view-ranked density glow; real sampled
sightings as taxon badges at their true positions with ambient photo
thumbnails and labels; obscured records stay privacy cells; a clicked
sighting stays pinned through camera moves and refreshes.
- Anchored photo card per selection with the single iNaturalist link;
attribution in the layer row and the Data attribution popover.
- Filters (CONFIRMED, taxa presets) round-trip in share links with token 9.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Navara 0.2.1 never frees pick batch ids, and the mover banks rebuilt
their instanced meshes on each attach, leaking about 260 KiB of
WebAssembly memory per switch. A per-view pool hands released ids back
out; the long soak now asserts native growth of at most 32 KiB per switch.
- Navara 0.2.1 sometimes drops the first show:false update on a new
vector layer, so a zone turned off right after a switch back stayed
drawn. Polygon displays hide by deleting their layers and rebuild them
when shown.
- Camera verbs drive both engines through the globe contract; the Cesium
viewer rig and the tests of states it can no longer reach are gone.
- Flights and military tracking share src/layers/aircraft/tracking.js.
- The geo import scenario reads movers only where the engine has them.
- Add the Navara section to docs/CURRENT-STATE.md and a changelog fragment.
- src/geo drops the ported debug argument checks and the tests that
bitExact already covers; package boundaries accept a directory entry,
so src/geo/ is listed once per group.
- Cesium no longer carries movers and lines nobody draws with: they are
optional engine capabilities. The fake engine and the Cesium camera use
the shared cameraMath; unused contract tables, clock.ticked, the
'finest' surface detail and Navara click statistics are gone.
- ALPR keeps one overlay core and one presentation model for both
engines, bikeshare one station bookkeeping, the Navara ground effects
one definition helper; image atlases, view centres, mover banks and
colours are shared.
- The legacy switch route, the clock offset and rate controls and other
unused exports are removed.
- The acceptance runs the Navara draw, geo import and scope checks;
scenarios use the harness switch and page helpers; the main chunk
check keeps the size budget only and runs once in CI.
The launches layer swallowed every fetch error in its update, so the
manager treated a 429 or timeout on /api/launches as a successful start.
Context then committed the isolating mode with no missions: the other
layers stayed off, unrelated enables were refused, the camera stayed at
the full-globe view and the roster kept reading "loading".
Until the first index loads, the update now reports the failure. The
manager fails the enable closed and both entry routes roll back to the
pre-entry layers. The camera pose captured at entry is restored when the
mode is left before any index loaded, and the roster shows a failed
state instead of loading. A valid empty index still enters, and a
refresh failure after a loaded index keeps its records.