Commit Graph
597 Commits
Author SHA1 Message Date
German Khokhlov 645f042450 feat(globe): surface height quality
engine.surface.sampleHeightDetailed(place, { exclude, lod }) answers
{ height, source: 'tiles' | 'terrain', tileSizeM? } (src/globe/surfaceDetail.js).
On Navara tileSizeM is the extent of the tile that answered and
lod 'finest' takes the finest loaded tile, drawn or not; Cesium answers
tiles without a size, else the globe's terrain. acceptedSurfaceHeight()
turns an answer into a height or a miss: tiles larger than
MAX_FLOOR_TILE_SIZE_M (4 km) are too coarse, terrain is not the mesh.

The mesh floor sampler and the ground snap are engine-only now (a viewer
or scene reaches its engine through engineFromViewer, which also registers
the scene) and treat a coarse answer as a miss, as they treat undefined.
Measured on Navara at Austin: while the tiles stream, a 27.9 km tile reads
143.2 m where the ground is 122.5 m (a 1.8 km tile).

`exclude` takes the engine's own mover sets (MoverSet.id): Cesium hides a
set's primitive for the synchronous sample (Cesium matches excluded objects
per billboard, not per collection); Navara's ray only meets tiles.

"sampleHeight undefined in the app on Navara" did not reproduce: the
startup Austin sample is 121.9 m (tile 889 m) on Navara and 122.4 m on
Cesium; one ModelMesh class is patched.

S2 QA (scripts/qa-surface-heights.mjs, 20 places at JFK, SFO, AUS, roofs
and streets): Navara within 2 m of Cesium at 19/20 from 800 m and 18/20
from 15 km with lod 'finest' (gates 18 and 15); offsets per region
-0.21 / +0.4 / +0.1 m; the Empire State roof differs by 39 m (spire
geometry). Navara tile sizes at 800 m: 112-314 m.
2026-10-16 20:51:29 +04:00
Mazeyar Moeini Feizabadi fc11fd0370 docs(sdr): document bounded playback and credit seek fix 2026-10-16 18:48:44 +02:00
German Khokhlov f640890cf6 feat(globe): follow camera on both engines
camera.follow({ position, viewFrom, minRange = 150, native? }) keeps a
moving target centred at an east-north-up offset the user may turn and
zoom; camera.following() and camera.followChanged report it; stop() frees
the camera and a camera command (setView, flyTo, flyToSphere, lookAt,
cancelFlight) ends it.

Probe ruling: Navara 0.2.1's cameraFollow holds the target but ignores the
user's drag and wheel, the aircraft sits ~13 px off centre with 7.6 px of
spread, and calling it without an offset each frame gives a NaN camera.
So Navara (and the fake engine) follow with src/globe/follow.js: each
frame, before Navara draws, the offset is re-read from where the user put
the camera relative to the previous target, kept outside the minimum
range and carried to the new target.

Cesium follows with viewer.trackedEntity: the layer's own Entity
(`native`) or a proxy Entity at position() with a 1 px transparent point,
since EntityView moves the camera only for an entity with a bounding
sphere. applyTrackedCameraFrame and the zoom limits moved to
src/globe/cesium/follow.js; src/data/trackedCamera.js keeps the pure parts
(the range clamp now lives in src/globe/follow.js). following() also
reports an entity the app tracked itself. Camera sensitivity claims made
on a viewer's Cesium camera now share the engine camera's ledger, and the
orientation controls and the world jump read the follow from the engine.

Contract (both engines): a target flying at 250 m/s stays within 3 px of
the centre for 2 s, zoomIn through it stops at 150 m, stop() and setView
end the follow. Cesium 71/71, Navara 77/77; shell 23/25/4.
2026-10-16 20:26:43 +04:00
German Khokhlov d483e5897e build: the initial-chunk guard covers Navara's whole dependency closure
Review of Task 0:
- build/navaraDependencies.js reads the dependency closure of
  @navaramap/three and @navaramap/three-default-plugin from node_modules
  (dependencies and peers) and drops what the app's other bundled
  dependencies reach too (Cesium is served as a script, so draco3d, which
  only Cesium shares, stays engine-only), plus three and postprocessing:
  88 packages today (the @navaramap/* packages, @takram/*, @react-three/
  fiber, @sparkjsdev/spark, astronomy-engine, d3*, draco3d, n8ao,
  three-stdlib, …). The browser build's initialChunkGuard fails on a
  module from any of them in an initial chunk. Tested on a fixture tree.
- check-main-chunk's budget measures the entry the page loads (the same
  chunk the composition check reads), falling back to phase 4's largest
  index-*.js only for a dist without a page.

Build clean: no Navara-only module in the initial chunks; main chunk
2,869,565 bytes (gzip 889,041).
2026-10-16 19:30:23 +04:00
German Khokhlov ae52695b15 test(qa): seeded synthetic feeds for the W1 baseline; requests compared
Review of Task 0: the recorded live feeds left vessels (no AIS key here)
and local ADS-B (no receiver) empty, and flights held one adsb.lol
viewport answer. src/tooling/w1Feeds.js builds seeded answers relative to
one frozen instant (2026-10-16T12:00Z): a global /api/flights OpenSky
snapshot (8 000 aircraft, 2 000 within 60 km of JFK), /api/vessels with
12 000 AIS rows worldwide and the rows within the city area (the area
answer first, the global one last as the replay's fallback), and
/api/local-receivers/aircraft with 40 aircraft near JFK on 1090 MHz and
978 MHz UAT. Tests read them back through the app's own parsers.
qa-w1-baseline --synthesize=DIR writes them beside the recorded
military, satellites and launches; a replay now logs every feed request
and how it was answered (exact / as another key / none) into
summary.json, and --compare diffs those too.

Replayed on d50eaa28 and HEAD: flights 8 000, military 154, local ADS-B
40, vessels 12 000, satellites 824, launches 24; the runs agree (counts,
known objects, screen points, tracking poses, requests). The Cesium
reference is re-recorded this way before Tasks 10a, 12a and 13a.
2026-10-16 19:28:12 +04:00
German Khokhlov 8812117013 fix(globe): Navara's shadowed sun leaked a light per frame
Navara 0.2.1's transparent pass refreshes its copy of every scene light
each frame with copy(light, true). For the sun's cascaded shadow lights
(three-csm CascadedDirectionalLights) that copy clones every cascade, so
each frame brought new DirectionalLights with new ids and three's
WebGLLights uniforms cache (keyed by id) grew by about 1.2 KB a frame in
the looks whose sun casts shadows (minimal, photoreal).

src/globe/navara/cascadedLightCopy.js patches the copy to update the
cascades it has in place (pose, colour, intensity, target, shadow
settings and camera) when the counts match, and keeps the rebuild
otherwise; internals.js finds the class among the view's scene lights
(private view._scenes.light) and applies it when the globe is built.
Tests on three-csm's own class: the original copy changes the light ids,
the patched one keeps the lights and takes the values.

Post-GC JS heap (CDP HeapProfiler.collectGarbage every 10 s, the app on
?engine=navara rendering continuously at 60 fps): minimal 4.33 → 0.20
MB/min over 70 s and 0.06 MB/min over 180 s; photoreal 4.34 → 0.19
MB/min; cinematic (no shadow) 0.34. Added to the upstream asks.
2026-10-16 18:41:20 +04:00
German Khokhlov b0cb8ce881 feat(globe): picks on input payloads
input.on('click'|'doubleClick'|'move', fn, { pick, drill }) delivers
{ …event, picked, drilled }: PickResult { id, owner, instance, primitive
(Cesium) } names the mover set (and instance) or line hit. One pick (and
one drill, as deep as the deepest listener) per event, shared by its
listeners; moves with picks are coalesced to one delivery per frame with
the latest position (src/globe/pickDelivery.js). Option checks in the
contract (normalizeInputOptions).

- Cesium: scene.pick / drillPick, and the picker names the owner: movers
  map billboards, points and models to their set and instance, lines
  their primitives to the line.
- Navara: a click with pick takes Navara's GPU pick of the same gesture
  (featureClick; its pick helper answers on the same pointerup, usually
  before the engine's click, so a fresh answer waits for it; otherwise
  the click waits up to 250 ms, then the CPU pick). Navara 0.2.1 answers
  clicks in quick succession with the first click's pick: a GPU answer
  whose instance is not drawn near the click is stale and the CPU pick
  answers. Hover, drill and the synchronous picker.pick/drillPick are CPU
  picks (src/globe/cpuPicks.js) of pickable mover sets and pickable lines
  (within half the width + 3 px), behind the globe excluded, nearest
  first; lines are picked on the CPU after an empty GPU answer.
  picker.clickStats() (Navara only) counts the answers and latencies.
- The fake engine picks its own movers and lines on the CPU unless
  scripted.
- The contract suite clicks with real pointer events (puppeteer) on both
  engines: a sprite is picked with its owner and instance, the map picks
  nothing, two sprites in one place drill both, a line is picked, a
  hover picks the sprite under it. Navara, 20 clicks: payload p50 2.8 ms,
  p95 4.1 ms after the click (puppeteer dispatch included), 20/20 GPU
  answers. The Cesium harness keeps the first-run card closed.
2026-10-16 18:25:12 +04:00
djabhi31 f60eeacb2d test(earthquakes): add unit test coverage for earthquake snapshot normalization
- Pin GeoJSON earthquake snapshot normalization contract in src/layers/earthquakes/records.test.mjs
- Cover complete-feed validation, M2.5 magnitude filtering, coordinate and depth bounds
- Verify stable fallback IDs, duplicate ID rejection, and malformed structure handling
- Register records.test.mjs in scripts/format-scope.json and apply Prettier formatting
2026-10-16 19:12:46 +05:30
German Khokhlov 86aa0bc9ba feat(globe): dynamic lines on both engines
engine.lines.createLine({ id, widthPx, color, depthFailAlpha, dash
({ lengthPx, gapPx } | 'tactical'), arc ('none'|'geodesic'), modelMatrix,
pickId }) → a Line: setPositions (Cartesian3[] or a flat Float64Array),
setColor, setModelMatrix, show, destroy; lines.list() for QA. Options,
flat positions, geodesic densification (great circles of the geodetic
normals, height interpolated, one step per degree) and the registry are
engine-neutral (src/globe/lines.js); the fake engine keeps every line.

- Cesium: a Primitive of a PolylineGeometry (synchronous, rebuilt when
  the positions change) with a PolylineMaterialAppearance in an
  engine-owned collection; depthFailAlpha is depthFailAppearance with the
  alpha scaled; pixel dashes are PolylineDash (16-bit pattern); the
  tactical dash is the launches' selected-orbit shader under its own type
  (launches keep theirs until Task 15a moves them onto engine.lines).
- Navara: a public MeshDesc (transparent pass, unlit) holding a ribbon
  of one quad per segment, widened to CSS pixels in the vertex shader;
  a second copy drawn with GreaterDepth and the alpha scaled is the
  depth-fail part; dashes are cut in the fragment shader (pixels along
  the screen direction, or the tactical pattern over the fraction along
  the line); positions relative to the first point, the model matrix on
  the group. Geodesic arcs are densified on the CPU. Not pickable yet.
- The contract suite draws a three-point line on both engines: magenta
  along both segments, nothing off it, 1° east once turned by its model
  matrix, gone when hidden; a line 30 km under the ground shows only its
  depth-fail copy, dimmer than the line.

Main chunk 2,867,499 bytes (+7,197, +0.25 %: the Cesium lines).
2026-10-16 17:24:36 +04:00
German Khokhlov 0109097440 feat(globe): instanced movers on both engines
engine.movers.createSet({ id, kind: sprite|point|model, capacity, order,
depthTest, sprite|point|model, pickable }) → a MoverSet: set(index,
fields) stores an instance (position, screen rotation, orientation, rgba,
scale, atlas cell, show, pickId) in flat arrays, commit() uploads the
changed range once and requests one render, positionsView() reads the
positions, destroy() removes it; movers.list() for QA. The options, the
store and the registry are engine-neutral (src/globe/movers.js); the fake
engine keeps them for layer tests.

- Cesium: one primitive per set in an engine-owned PrimitiveCollection
  added with the first set, in `order`: sprites a BillboardCollection
  (one image per atlas cell, NearFarScalar, disableDepthTestDistance),
  points a PointPrimitiveCollection, models a Model per instance.
- Navara: a public MeshDesc per set. Sprites and points are one
  InstancedMesh of a quad, faced to the screen and sized in CSS pixels in
  the vertex shader from uniforms (the pick pass draws the same
  footprint), atlas uv, rotation, colour and scale per instance, drawn in
  the transparent pass unlit; offsets from an origin that follows the
  camera past max(1 km, 10 % of its height). Models: the glTF once, one
  InstancedMesh per mesh, minimumPixelSize/maximumScale on the CPU per
  drawn frame, colorBlend in the shader. Pickable sets register a
  PickableInstancedMeshWrapper; pickIds.js maps batch ids back to
  instances (used by Task 4).
- The contract suite draws three 24 px sprites on both engines: centred
  within 2 px of worldToWindow, hidden by show:false, recoloured, gone on
  destroy, and no frames added once the commit has drawn.
- qa-engine-bench --scenario=movers: 10 000 synthetic aircraft
  (src/bench/syntheticFlights.js, moved from the Navara spike with its
  test), every instance moved and committed every frame. p95 Navara
  (look=minimal) / Cesium: global 5.2/7.0 ms, city 5.6/9.9, orbit 6.0/7.4
  (0.74, 0.57, 0.81; gate 1.1). Cesium is measured without its 60 fps cap.

Main chunk 2,860,302 bytes (+9,706, +0.34 %: the Cesium movers).
2026-10-16 17:15:24 +04:00
German Khokhlov 4623c8b616 test(qa): a comparable W1 baseline from recorded feeds and a frozen clock
Review of Task 0: the live baseline could not be compared between
commits. qa-w1-baseline now records every /api response of each layer's
run (--record=DIR, DIR/<layer>.json), replays them through puppeteer
request interception (--replay=DIR; an /api request outside the
recording answers 503) with the page clock frozen at the recording's time
(evaluateOnNewDocument: Date.now and new Date(), hence JulianDate.now and
the app clock), and diffs two runs (--compare=A,B: counts, known objects'
positions within 1e-7° and 5 cm, screen points within 0.5 px, tracking
poses within 1e-6 relative). The rules live in src/tooling/w1Baseline.js
with tests.

Recorded once (qa-artifacts/w1-feeds, git-ignored), replayed on
d50eaa28 and on HEAD: the runs agree within the tolerances for flights,
military, satellites (counts, five known objects each, tracking poses)
and launches (count). Local ADS-B has no receiver and vessels no AIS key
here: both stay empty. This is the reference for the extraction tasks.
2026-10-16 16:37:43 +04:00
German Khokhlov 76851ee949 build: guard the initial chunks by module id; main-chunk check in CI
Review of Task 0:
- build/initialChunkGuard.js, a Rollup plugin in the browser build, fails
  the build when an initial chunk (an entry and what it imports
  statically) holds a module from node_modules/three, @navaramap/* or
  postprocessing. Checked by forcing it on satellite.js, which the main
  chunk does hold: the build fails naming the chunk and module.
  navaraAssets.test now pins both build plugins.
- scripts/check-main-chunk.mjs is one script for both main-chunk checks:
  the composition (text markers in the initial chunks, sizes) and, when
  scripts/main-chunk-baseline.json exists, the phase-4 branch's gzip-9
  budget, with its exports (checkMainChunk, measureMainChunk) so the two
  branches merge into this file. The composition test moved to
  src/tooling/mainChunkComposition.test.mjs to leave phase 4's
  mainChunk.test.mjs its name.
- CI runs npm run check:main-chunk after each npm run build (unit,
  browser-gate and Windows jobs).
2026-10-16 16:22:49 +04:00
German Khokhlov 7c7422e281 Merge migration/phase2-engine (4aaf9746) into migration/phase3-layers
The phase-2 review fixes: look internals in internals.js, the groundFloor
resample throttle, tile height selection, the sharpen mirror, QA tile
cache limits, engine.groundFloor diagnostics. In navara/index.js both
sides are kept: the look gets internals.js's luminance uniforms and reads
the sun's time from the engine clock's frame time, so engine.clock stays
the one time source for the sun.
2026-10-16 15:15:24 +04:00
German Khokhlov 5188e8869d fix(globe): the cinematic look's atmosphere scale, warm-up and sunset clouds
- The takram uniform paths move to internals.js (forEachLuminanceUniform,
  loud in dev when a path is gone); look.js sets each factor to scale x
  the recorded base value, so shared or copied vectors are never scaled
  twice, and the stars keep the base value (own vector when shared).
  Stars at intensity 120: a starfield shows from space.
- Both night-side shader variants compile on the first two frames.
- The clouds hide once the sun sets at the camera (dark puffs at dusk).
- No allocation on look frames that change nothing (frozen updates, a
  scratch camera place from livePosition).
Gallery regenerated.
2026-10-16 15:06:15 +04:00
German Khokhlov 27e28373d0 test(qa): W1 baseline and the main-chunk check
scripts/check-main-chunk.mjs (npm run check:main-chunk) finds the main
chunk of a build (the Vite manifest's entry, or the module script
dist/index.html loads), follows its static imports and fails when any of
them carries three or Navara markers; it prints the sizes. Base build:
main chunk 2,850,229 bytes (gzip 881,783), no static imports, clean.

scripts/qa-w1-baseline.mjs records Cesium mode for the W1 layers (flights,
military, local ADS-B, vessels, satellites, launches): counts, five known
objects with positions and screen points, JPEGs (path, size, sha256) at
the global / New York (JFK) / orbit poses, a tracked object's camera pose
after 2 s and what its share link restores. Live sources until the
fixture sources of Task 8.
2026-10-16 14:49:24 +04:00
German Khokhlov d50eaa28af test(qa): noon sky and stars, surface heights across engines, tile cache
qa-globe-contract checks the noon sky facing the horizon (daylight blue,
no star pixels) and stars at midnight; qa-engine-shell compares
sampleHeight at the Austin startup pose across engines and reports the
wheel floor's lowest frame. QA pages load OSM and keyless terrain tiles
through a disk cache with backoff on 429. Report updated.
2026-10-16 14:29:31 +04:00
Said Turksever d4a6b1e0d4 Merge branch 'main' of https://github.com/bilawalsidhu/gods-eye-view into pr787
# Conflicts:
#	scripts/format-scope.json
2026-10-16 11:28:09 +01:00
German Khokhlov bb8c0b83c4 feat(globe): Sharpen on Navara, on at 80 % by default
The contract gains render.setSharpen({ enabled, amount }) and
render.sharpen(). Navara draws it as a custom EffectDesc: Cesium's unsharp
mask (3x3 box blur, gamma 2.2 round trip so one amount matches Cesium)
right after tone mapping and before SMAA/FXAA, skipping sky and cloud
pixels by scene depth. The Cesium engine keeps the state only; Cesium mode
still sharpens with its own post stage, unchanged. The fake engine records
it and the contract suite checks it.

Without a viewer the Sharpen slider and toggle drive the engine with the
same slider-to-amount mapping. On Navara the defaults, presets and share
links read Cesium's 49 % as 80 %.
2026-10-16 13:30:56 +04:00
daikaginza 202a310c7a Merge remote-tracking branch 'upstream/main' into fix/terrain-heights-bounds
main now rejects out-of-range coordinates at the proxy itself (19fcc00b), the
same check this branch added. main's version is kept, and the changelog entry
no longer claims that part; the cache cap, file ceiling and rate limit remain.
2026-10-16 09:25:51 +07:00
daikaginza 520d81694a Merge remote-tracking branch 'upstream/main' into feat/changelog-fragments 2026-10-16 09:23:03 +07:00
daikaginza 995c1f3edc Merge remote-tracking branch 'upstream/main' into fix/gbfs-rate-limit 2026-10-16 09:16:42 +07:00
quanticsoul4772andClaude Opus 5.5 acd75efc7c Merge upstream main into feat/tools-area-breakdowns
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 17:33:20 -07:00
omerbberk 4399febaa0 fix(cctv): keep missing numbers unknown instead of reading them as zero
Number() reads null, blank strings and booleans as 0 (or 1), and
/api/cctv/sources sends an unknown number as JSON null. A source-pack
camera that left out its pose therefore reached the browser as zeros
clamped to each field's floor (north, pitch -2, FOV 20, range 120 m,
mount 6 m, ground 0 m) instead of taking the defaults, and a null or
blank position put the camera at 0,0.

toFiniteNumber (server) and safeNumber (browser) now return the fallback
for absent, blank and boolean values, as validateBox already does for
Recent Imagery. The same conversion fixes the per-pack paths: a Tallinn
row with a null heading no longer reads as a high-confidence north-facing
camera, a Caltrans camera with a blank elevation keeps its 150 m ground
prior, and TfL or Catalonia cameras with a null or blank coordinate are
dropped instead of kept at 0,0 or on the zero meridian.
2026-10-16 00:27:41 +00:00
Sameh Khamis a478f26eb9 Merge branch 'main' into feat/webapp-icons 2026-10-15 16:26:06 -07:00
Sameh Khamis ee10b2ce1c Merge branch 'main' into feature/catalonia-cctv-cameras 2026-10-15 16:10:47 -07:00
Sameh Khamis b4d852587a Merge branch 'main' into feat/lithuania-cctv 2026-10-15 14:21:07 -07:00
Sameh Khamis e8b6ffd196 Merge branch 'main' into feature/catalonia-cctv-cameras
# Conflicts:
#	CHANGELOG.md
2026-10-15 14:18:47 -07:00
Sameh Khamis ac13acfe3d Merge branch 'main' into feat/aqhi
# Conflicts:
#	CHANGELOG.md
2026-10-15 13:47:21 -07:00
Sameh Khamis 046126eb36 Merge branch 'main' into feat/aqhi
Port to current main: main reserved layer-state token 0 for street-level, so
air-quality takes the next free token, 4 (registry, ledger, and the allocation
tests shift by one). Main moved source contracts into sourceComposition.js, so
air-quality registers its getSnapshot contract there. Package boundaries keep
main's lists plus this branch's air-quality modules and packages.
2026-10-15 12:39:00 -07:00
Sameh Khamis 1bb8643a28 Merge branch 'main' into fix/cap-remaining-upstream-reads
# Conflicts:
#	CHANGELOG.md
2026-10-15 12:34:49 -07:00
German Khokhlov 80cc0b536e style: format the phase 2 files and add them to the format scope
Prettier on the 13 new test files that failed --check; format-scope.json
now lists every file phase 2 added (src/globe/**, engineChoice, navaraMode,
cameraAngles, the QA scripts and pages, build/navaraAssets and tests).
2026-10-15 22:34:25 +04:00
Said TurkseverandClaude Opus 5.5 b5de533567 Merge upstream main into pr/panoramax
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 16:56:15 +01:00
Said TurkseverandClaude Opus 5.5 b645a767c8 Street Level: Panoramax review follow-ups (gekh, #1134)
Image loading (photoViewer.js)
- One AbortController per open: a new open, close or unmount stops the
  previous photo's first and sharper downloads (an <img> by src = '').
  A collapsed viewer never falls back to the original; Panoramax's flat
  fallback is the thumbnail. A superseded open resolves quietly.
- Flat HD tracked as "loading" and "shown" apart: a discarded response no
  longer blocks later upgrades, and resizes while it loads ask once.
- No upgrade of the photo still shown while another opens.
- Size limits before decoding: imageSize.js reads JPEG/PNG/WebP/GIF
  headers; more than 64 MP or 16,384 px a side is refused undecoded, an
  <img> that decodes larger is refused and let go, flat HD needs a known
  width of at most 8,192 px, and panoramas always decode with a resize.

Panoramax provider
- Retry-After parsed as seconds or an HTTP date; the browser cannot read
  it here, so busy answers back off 5 s doubling to 300 s, reset once
  answered.
- Headers timed, then the body by an idle timeout (15 s of silence), so a
  slow link still gets its megabyte tiles.
- Cones centre only on the picture shown (onShowing), never on a late
  lookup; lookups nobody waits for are aborted (shared, refcounted).
- onReopen: a GPU reset redraws in the viewer, not through the core.
- Cones draw the home z15 tile at once and add neighbours as they arrive;
  a neighbour that failed is not counted as drawn.
- Coverage tiles decode in ~8 ms slices (a 2 MB z10 tile over Paris was
  one ~85-200 ms task); sorted by measured length without a Map.
- Box search asks for 20, not 100 (the API returns them nearest first).
- looksLikeEmail checks every '@'; creatorOf de-duplicates with a Set.

lineCoverage.js
- A redraw while a swap still builds keeps what is on screen (the swap's
  old lines and its ready new ones) and drops only the unready ones.

Tests for each (mutation-checked), two vacuous tests made real, the
cones' pictures lane and readable()'s busy/timeout paths covered; the
fixture's box search is nearest first. Docs: README filters and share
links, CHANGELOG HD wording, CURRENT-STATE.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 16:55:59 +01:00
quanticsoul4772 9688f15af7 Merge branch 'main' into feat/tools-area-breakdowns 2026-10-15 08:20:45 -07:00
German Khokhlov 58a196374f feat(globe): engine members and the viewer bridge for the shell
Contract: camera.position(result?) and camera.height() for per-frame
readers, render.request(reason?), maps.releasePhotorealTiles, sources
with a caller-owned ready provider, isGlobeEngine. engine.maps now hides
every tileset it has shown, lets the last imagery/photoreal call win, and
setPhotorealTiles(null) restores the last base imagery and its credit
instead of leaving the photoreal credit on a bare globe.

Cesium engine: reads the Viewer camera/canvas aliases, detaches bridged
events through the remover Cesium returns, raises frame timestamps
without allocating, re-aims the horizon occluder only when the camera
moved, and renders the scene directly when the governor governs another
viewer (or none yet). engineFromViewer returns the registered scene
engine of a viewer, else builds and caches one. The render governor stays
the Cesium render backend under engine.render and gains
isRenderGovernorInstalled.
2026-10-15 18:18:19 +04:00
German Khokhlov a5c98c6b07 feat(app): construct the globe engine with the viewer
The scene builds the page's Cesium engine right after the viewer
(src/app/globeEngine.js), names the startup Google tileset to it once
loaded, returns it beside the viewer and destroys it before the viewer.
The debug handle exposes it as window.__godsEyeView.engine. Nothing
consumes it yet; render holds taken before the governor is installed
apply at install.

qa-globe-contract now runs on the app's engine (self-built only when the
app has none); a throwaway engine checks creation and destroy leave the
scene untouched.
2026-10-15 18:02:11 +04:00
German Khokhlov ab2368f3f3 fix(globe): tighten the Cesium engine after review
Map sources are applied through one src/maps/sceneApply.js helper shared by MapSourceController and engine.maps (live layer kept for an unchanged provider, providers cached per source). The contract gains flyTo endTransform and rectangle destinations, flyToSphere easing, drillPick width/height and pick content, with Cesium's own flyToSphere range. Input skips listeners removed during dispatch and stays inert after destroy.
2026-10-15 17:55:00 +04:00
German Khokhlov d831cbd23a feat(globe): implement the engine on Cesium
createCesiumEngine wraps an existing viewer without changing it: camera, picker, projector, frames, render (through the render governor), surface, input, credits and maps, with geo values out and Cesium values in at the boundary. Map and surface code is shared with src/maps and services/groundSnap instead of copied. scripts/qa-globe-contract.mjs runs the contract suite against the engine in headless Chrome; the camera-events check now waits for the previous move to end, as Cesium's moveStart marks a move from rest.
2026-10-15 17:43:05 +04:00
German Khokhlov 528ec75f32 feat(globe): add the engine contract and its test suite
src/globe/contract.js defines the engine-neutral GlobeEngine capabilities
(JSDoc), createEngineEvent with Cesium.Event semantics and
assertGlobeEngine. src/globe/testing adds a geo-based fake engine for shell
tests and an engine-agnostic contract suite that the fake passes in Node.
check-import-directions keeps the contract free of cesium, three and
@navaramap, and each engine directory free of the other engine.
2026-10-15 17:01:08 +04:00
Said TurkseverandClaude Opus 5.5 de83e1c17f Merge upstream/main into pr/panoramax
CHANGELOG: keep both new [Unreleased] entries (Panoramax, and the
emergency-transponder highlight from #856).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 12:57:53 +01:00
Mazeyar Moeini Feizabadi e3c07e43e3 Merge current main and reviewed GNSS into import integration 2026-10-15 13:48:39 +02:00
Mazeyar Moeini Feizabadi 65239ed110 Merge current main into GNSS integration candidate 2026-10-15 13:46:45 +02:00
ulihhhhandClaude Opus 5.5 da0a840b7b Merge upstream/main into feature/catalonia-cctv-cameras
Keep both: Catalonia CCTV alongside the NYC DOT TMC pack in the source
imports, README layer table and CURRENT-STATE data table, and both
CHANGELOG entries.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-15 10:09:28 +02:00
Mazeyar Moeini Feizabadi 5ea6d81310 fix(import): correct clamping and bound local-file rendering
Normalize GeoJSON ground footprints, preserve explicit KML track heights,
and tessellate ground paths before native loading. Validate coordinates,
geometry types and decoded image budgets before allocating native resources;
strip loader-sensitive metadata and settle deferred failures and cancellation.

Render static pins in owned billboard collections instead of walking tens
of thousands of point entities each frame. Refine nearby ground heights in
batches, isolate imported pins from height picks, and release owned resources
and point selections on removal. Use safe ray origins above a hidden coarse
globe and retain valid mesh heights outside the current footprint.

Frame complete geometry with viewport-aware nadir arrivals, including walls
and rotated overlays; respect camera gestures and accept document file drops.
Strengthen real-loader, native 3D Tiles, paint, clock and lifecycle coverage.
Addresses both P1 and all 25 P2 findings in the follow-up review of #1122.
2026-10-15 04:10:41 +02:00
Mazeyar Moeini Feizabadi 4092c2e9a2 merge: refresh file import integration against main 2026-10-15 02:48:47 +02:00
Kevin Rajan c90e212049 chore(ui): reconcile compact Radio ownership fix with current main
Preserve upstream changes, restore the reviewed Radio ownership regression,
and retain reporter raysung-2026 credit and #804 provenance.

Reconciles upstream main 81cf437 and PR #816 head 702ecc3; no force push.
2026-10-14 18:17:57 -05:00
quanticsoul4772andClaude Opus 5.5 e312a007ab Merge upstream main into feat/tools-area-breakdowns
Keep this branch's CHANGELOG entry above upstream's; upstream main no longer lists the GBFS station-name entry an earlier merge carried here.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 16:09:11 -07:00
quanticsoul4772andSameh Khamis e50bc45674 fix(qa): run the macOS-only gates on Windows and Linux (#1061)
Seven QA scripts hard-coded macOS Google Chrome. They now share chromeExecutablePath(): PUPPETEER_EXECUTABLE_PATH when set, then macOS Chrome when installed, otherwise Puppeteer's managed browser. The cables screenshot path is built with fileURLToPath so it works on Windows. qa-overlay-baseline keeps its fixed Chrome because its baselines record the executable.

Co-authored-by: Sameh Khamis <[email protected]>
2026-10-14 15:36:35 -07:00
Kevin Rajan f11a532139 chore: reconcile Context Radio ownership fix with upstream main 2026-10-14 11:58:52 -05:00
Mazeyar Moeini Feizabadi 4240d04642 Merge current main into HAL-522 review candidate 2026-10-14 16:53:13 +02:00