Commit Graph
3033 Commits
Author SHA1 Message Date
German Khokhlov c327a25744 feat(data): one selection dispatcher
src/data/selectionDispatcher.js: one engine click subscription with
picks (pick, drill 8), the press gesture (travel and duration, as
bindTrackingClickGesture) and one Escape listener for the layers that
move onto the engine. Owners register owns(id) (also in pickRegistry, so
layers still on their own handlers see the ownership) and onSelect /
onEmptyClick / onEscape. Rules of the existing handlers: another layer's
pick is not empty space; passThrough picks (trails) never take the
click; a drillThrough owner (local ADS-B) takes its object from under
another layer's; a drag selects nothing; deselection needs a clean click;
a pointer owned by a tool selects nothing. Tested on the fake engine:
own / foreign / empty / trail clicks, drill-through, drag and long press,
Escape, an owner without handlers, unregister and destroy. No layer uses
it yet (the *a tasks move them).
2026-10-16 18:25:12 +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 KhokhlovandClaude Opus 5.5 a2463ca2d2 Revert hiding vessel detection callouts under ambient cards
The vessels layer hid the detection callout for every vessel whose card
it published to the world overlay. The overlay paints only part of the
published cards (shared budget, collisions, UI chrome), so vessels whose
card was dropped lost all their text: with 60 vessels and Detection on,
21 showed only an arrow. Back to main's behaviour: the callout always
draws. Hiding it only under a painted card is HAL-628.

The translated speed unit in the callout stays.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 17:37:58 +04:00
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 KhokhlovandClaude Opus 5.5 3adce33cc6 i18n: fix the final pre-merge review findings
Fixes the P1 and the confirmed P2 findings of the final review of the
i18n branch.

- CI: the air-quality model no longer imports i18n (portable module),
  so check:boundaries passes again.
- Air quality layer: named in the layer panel, HUD and voice; its
  Weather card, bands and errors translate. A test keeps the layer-name
  table in step with the layer registry.
- Layer statuses that still reached ru/es in English: GNSS, CelesTrak,
  Transit loading and feed errors, launches, perimeters, ALPR, ECCC,
  wind "field unavailable", Local ADS-B streaming lines.
- Voice and HUD: preset ids show city names ("Fly to San Francisco"),
  the HUD city translates, cockpit refusals cover frame, aircraft and
  satellite; empty Transit notes translate on the analyst card.
- tn() keeps a caller's formatted count ("12,345 map objects").
- Cyber HUD: the language button no longer covers the bottom-right
  card or the cockpit position readout.
- CCTV buttons wrap only in non-English locales; English layout matches
  main again.
- Switching language asks first when Provider Settings holds unsaved
  keys.

Catalogs: es/ru 2611 messages each, complete.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 16:39:25 +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 f7f4e9a45c fix(time): latch the Cesium clock at creation; document the clock's limits
Review of Tasks 0–1:
- The Cesium engine clock attaches its frame latch to viewer.clock.onTick
  when the engine is created, so it runs before onTick listeners added
  later (cameraVerbs installs its own after the engine exists). Before, it
  attached on first use and would have run after them. A viewer without a
  clock still latches lazily on the scene's preUpdate.
- GlobeClock lists what is not on the app clock yet: Cesium's viewer clock
  (its lighting and property reads at currentTime), the launch replay
  timeline and the mission orbit's GMST frame (Task 15a), the satellite
  cloud's SGP4 and orbit bake (Task 14a), and wall-clock throttles. The
  plan's T14a/T15a now carry those moves.
- freeze/offset/rate change now() at once and frameTime() from the next
  frame; QA helpers request a render after moving the clock.
2026-10-16 16:18:08 +04:00
Said Turksever d3449313d4 Merge branch 'main' into feat/changelog-fragments 2026-10-16 12:53:10 +01:00
Said Turksever 537e99dfa4 Merge pull request #1063 from YosolosoY/feat/bikeshare-bogota-santiago
feat(bikeshare): add Bogotá (Tembici) and Santiago (Bike Itaú)
2026-10-16 12:47:12 +01:00
manjunath 4fde920665 feat(voice): harden Gemini Live provider integration 2026-10-16 17:09:03 +05:30
Aditya 557d3bb41e fix(military): hold the last track and speed when a poll omits them
MilitaryFlightRecords coerced a missing course to 0 (`courseDeg || 0`)
and a missing speed to 0 (`speedMps ?? 0`) before stickyNumber, so the
hold-last-known-good merge never ran. When an adsb.lol row arrived
without `track` or `gs`, the aircraft's icon swung to north and the
position history got a 0 kt / 0 degree fix, so dead reckoning stopped
or turned it north. The civil FlightRecords holds the last value for
the same input.

Pass the raw values to stickyNumber, with 0 as the fallback only until a
value is known, as before. A grounded row without a speed still reads
0, so a parked aircraft doesn't coast at its landing speed.
2026-10-16 16:52:22 +05:30
Aditya d6c1ea0f4b fix(maps): retry the real imagery and terrain provider after a fallback
In the keyless setup a single transient failure while loading the default
map layers locked in the degraded fallback until reload.

_getImageryProvider cached the construction-fallback resolution (OSM) under
the Esri stack id, so every later "Esri Satellite" selection showed OSM.
createKeylessTerrain turns any Re:Earth failure into a flat
EllipsoidTerrainProvider; the controller cached it and set _terrainMode to
'keyless', so a boot-time network blip left the globe flat.

Fallbacks are still cached and shown at once, and no switch waits on a
retry. One controller timer now retries the real provider behind whichever
fallback is on screen (Esri's OSM stand-in, or flat terrain on a shown
globe), starting at 30 seconds and doubling to a 10-minute cap while it
keeps failing. A failed retry leaves the cached fallback and the installed
terrain untouched. A success replaces the cache entry and applies it live:
terrain is installed in place with a render request (or, behind a 3D
tileset, picked up by the next globe activation), and Esri is reselected
automatically, after any in-flight switch settles. A switch that read the
flat fallback re-reads the terrain cache before installing, and destroy()
cancels the timer and releases a provider built after it.
2026-10-16 16:52:01 +05:30
Aditya a316e12323 fix(overpass): read failure phrases from the remark, not element tags
overpassLooksRuntimeError and overpassLooksRateLimited searched the whole
200 body, element tags included. A result containing a place named
"Timed Out Escape Rooms" (or any tag with "runtime error", "out of
memory", "too many requests" or "rate_limited") was refused as an
upstream failure, and the endpoint was put on cooldown for every other
query.

Parse the body once. A data reply (one with an elements array) reports
failure in its remark, so the phrase checks run on the remark. Any other
body, an error page or JSON without elements, is matched whole as
before.
2026-10-16 16:52:01 +05:30
Aditya 2c61d9e2b7 fix(geo): wrap longitude differences across the antimeridian
The flights ground-floor pipeline subtracted longitudes without wrapping.
approxDistanceKm, and its copy in the mesh-floor sampler, measured two
points 0.2° apart either side of 180° (21 km at Fiji) as about 38,000 km,
so low and grounded aircraft just across the line from the viewer were
never floored or mesh-sampled. corridorFloorCells walked a segment across
180° the long way round, filling its cell budget with ground behind the
contact and never requesting the cells it was about to cross, and
stickyFloorCell dropped its hysteresis for a contact on the seam. Each now
takes the short way round, and corridor points are normalised back into
[-180, 180].

SEARCH NEARBY SITES centred its Google Places search on the mean of west
and east, which for a view across 180° (west > east) is longitude 0, and
sized the radius from the view's height alone. It now measures the view's
width through 180° and searches its real centre.
2026-10-16 16:48:37 +05:30
Aditya bd48533b03 fix(director): land on the requested shot
A shot action seeks to the target shot's startProgress, which
sceneTimingForShot computes as start / total. sceneSeekState multiplied
that back into seconds and compared it with each shot's end, but
(start / total) * total can round one ulp below start. The comparison
then put the seek at the very end of the previous shot. With five equal
3.9 s shots, "go to" shots 2, 3 and 5 landed on shots 1, 2 and 4.

Compare the requested progress against each shot's end / total instead,
the same division the caller used, so a shot's own start always selects
that shot. Exact boundaries still select the next shot, and anything
below a start still selects the previous one.
2026-10-16 16:48:37 +05:30
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 4aaf974600 docs(migration): the review fixes, their measurements and QA 2026-10-16 15:06:15 +04:00
German Khokhlov ce985768bc test(qa): wheel floor against the floor's own ground; polite tile cache
The Navara wheel check measures clearance against engine.groundFloor's
ground (threshold 29 m: the floor keeps 30 m), so tiles streaming under a
still camera cannot flake it. The tile cache identifies itself with the
repository URL, keeps at most two misses in flight and merges identical
ones.
2026-10-16 15:06:15 +04:00
German Khokhlov da6141be4b perf(globe): lazy hidden tiles by tile extent; floor resample interval
sampleHeight collects hidden tiles only after the shown ones miss, with
no per-node allocation, and picks the finest by tile extent (Navara
exposes no geometric error), not per-mesh radius. The ground floor
resamples on a move only after 100 ms, and reports its own ground and
margin (engine.groundFloor, Navara-only diagnostics).
2026-10-16 15:06:15 +04:00
German Khokhlov 6dad3894bd fix(globe): sharpen sky mask past depth 0.99999, not >= 1
RGBA8 depth packing rounds near the far plane.
2026-10-16 15:06:15 +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 bb4e6bafa4 perf(time): the app clock's frame latch without closure boxing
The clock keeps its numbers in object fields (stored in place) instead of
closure variables, and its default wall clock is one module-level
function. Micro-measure (Node, --expose-gc, 200k calls): the flights
render-time arithmetic allocates about 25-35 B per call with the app
clock against 66-102 B with JulianDate.now/addSeconds; an engine tick
(beginFrame + ticked) about 20-30 B.
2026-10-16 15:03:23 +04:00
German Khokhlov c33ee31efc refactor(layers): W1 timestamps without JulianDate
Flights and military keep their fix history in plain milliseconds
(timeMs, whole ms as the Date the JulianDate came from held) and read
"now" from the app clock: the render-delay time, bracketing,
extrapolation, the warm-up check and the trail seed are number
arithmetic (motion.js, tracking.js, snapshotRenderer.js), as is the turn
rate and the forward synthetic fix in data/motionModel.js, which no
longer imports Cesium. The JulianDate scratch fields in state.js and the
unused JulianDate.now() in queries.js go. Launches (mission frame, panel,
selection), the tracked satellite's SGP4 epoch and the celestial ring's
sun and moon read the app clock; where a Cesium property still takes a
JulianDate it is built from the app clock's now (as JulianDate.now did
from Date.now). Wall-clock throttles and feed receipt stamps stay on
Date.now; property reads at viewer.clock.currentTime (render, frozen at
boot) move with the render code in the *a tasks.

Tests that built JulianDate history fixtures now build ms ones with the
same instants (motionModel.test, flights.test, militaryFlights.test); new
tests show the military delayed position and the tracked satellite follow
the app clock (both fail on the previous code).
2026-10-16 15:01:46 +04:00
German Khokhlov f1a2df5cfe feat(time): the app clock on the engine
src/time/clock.js is the app's own time in ms since the epoch: by default
Date.now exactly (read at each call), with freeze/offset/rate/reset for QA
and tests, and one frame time latched per engine frame (beginFrame). The
contract gains engine.clock { now, frameTime, ticked }, in the fake engine
(a controllable wall clock, fake.advanceTime, fake.appClock), the contract
suite (one frame time for every listener of a frame, latched before
preUpdate, never decreasing), Cesium and Navara.

Cesium latches on the viewer clock's onTick, which CesiumWidget runs right
before scene.render, so scene listeners the app added before the engine
existed read the same frame time; the subscription appears on first use
and destroy removes it. The viewer clock itself is untouched: in the app
it never animates (shouldAnimate false, multiplier 1, its time frozen at
boot), and its time still drives Cesium's lighting as before. Navara
latches in the engine's handler of the view's preUpdate, and the look
reads the engine clock's frame time for the sun, so the app clock is the
one time source on Navara too.

src/time is a portable root in check-import-directions: no engine
packages, no browser globals.
2026-10-16 15:01:36 +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
Said TurkseverandClaude Opus 5.5 7a38727b85 fix(bikeshare): drop stations outside a city's load radius
Bogotá's and Santiago's PBSC feeds list depot stations in São Paulo and
Madrid as installed and renting, so activating either city drew live
docks thousands of kilometres away and counted them in the layer total.
Station information is now limited to the city's registered load radius;
every other registered feed already sits inside its radius (checked live
2026-10-16), so nothing else changes.

Adds a Bogotá/Santiago registry test matching the Brazilian and Barcelona
ones: proxy allowlist, outermost-station activation and radius reach, and
the depot filter through the real ingestion path.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 11:43:48 +01:00
Said Turksever c5feec26dc Merge remote-tracking branch 'upstream/main' into feat/bikeshare-bogota-santiago
# Conflicts:
#	CHANGELOG.md
#	DATA_SOURCES.md
#	src/data/dataCredits.js
#	src/layers/bikeshare/registry.js
2026-10-16 11:41:25 +01:00
Said Turksever 1e3ba28c88 Merge branch 'main' into feat/changelog-fragments 2026-10-16 11:40:51 +01:00
Said Turksever d4492d8f8c Merge pull request #1176 from ulihhhh/feat/bikeshare-barcelona
feat(bikeshare): add Barcelona's Bicing and AMBici
2026-10-16 11:39:16 +01:00
German Khokhlov 26968f4903 fix(ui): the Cesium engine's render.sharpen() mirrors the sharpen stage
VisualEffects sends its sharpen state to engine.render.setSharpen when a
viewer exists too; the Cesium engine keeps it as state, and the Cesium
stage itself is unchanged.
2026-10-16 14:34:17 +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
German Khokhlov 1cca89adf2 fix(globe): Navara sampleHeight under a camera looking ahead
The ground under a tilted camera is outside the frustum, where Navara
hides its tiles, so the ray found nothing. When the shown tiles miss,
the loaded hidden ones answer, the finest first (within 1.5 times its
radius, which keeps out the quadtree parent). At the Austin startup
pose it agrees with Cesium within 0.12-1.04 m.
2026-10-16 14:29:31 +04:00
German Khokhlov a8a2ad5582 refactor(ui): engine-specific sharpen defaults instead of reading 49 as 80
The baseline and the style presets name ENGINE_SHARPEN_DEFAULT, which
resolves to 49 % on Cesium and 80 % on Navara. Share links and explicit
calls restore their value unchanged.
2026-10-16 14:29:31 +04:00
German Khokhlov abf16ae8af fix(globe): a daylight sky and no daytime stars in the cinematic look
The atmosphere draws physical radiance scaled for an exposure near 10,
while the baked Google tiles need exposure 1, so the noon sky came out
dark navy with the stars showing through. The cinematic look now scales
the atmosphere's radiance-to-luminance factors (sky, aerial perspective,
clouds) by 3, leaving the tiles alone, and shows its stars only from
space or with the sun below -6 degrees at the camera. Gallery
regenerated.
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
ulihhhhandClaude Opus 5.5 dec79b6fcf test(bikeshare): pin the Barcelona load radii to their outermost stations
The map range check floors every radius at 100 km, so the station test
passed with any loadRadiusKm up to that. get_bike_share uses the radius as
registered, so assert each system's radius reaches the stations it lists.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 12:25:44 +02:00
Said Turksever 709b4f78e4 Merge branch 'main' into feat/bikeshare-barcelona 2026-10-16 11:24:36 +01:00
Said Turksever ca68d7e1ae Merge pull request #690 from daikaginza/fix/gbfs-rate-limit
fix(gbfs): rate-limit /api/gbfs
2026-10-16 11:23:22 +01:00
Meir Michanie fada8b161b Merge branch 'main' into i18n-spanish 2026-10-16 12:20:47 +02:00
Said Turksever 1d34ef9260 Merge branch 'main' into fix/gbfs-rate-limit 2026-10-16 11:10:22 +01:00
Said Turksever 775872fd2c Merge pull request #1114 from rudycelekli/contrib/gods-next-31-gbfs-unknown-counts-20261013
fix(bikeshare): preserve unknown availability counts
2026-10-16 11:09:54 +01:00
Said TurkseverandClaude Opus 5.5 4d42516ffe Make changelog assembly recoverable after a partial write
`--write` wrote CHANGELOG.md and then removed fragments one by one, so a
failure after the write left surviving fragments that a rerun spliced in
a second time.

The write is now staged. The assembled file goes into
changelog.d/.assembling/, the fragments are renamed in beside it, and
renaming the assembled file over CHANGELOG.md is the single commit
point. A run that fails rolls itself back when it can; otherwise the next
`npm run changelog` reads the staging directory and either restores the
fragments (assembled file still staged) or deletes the leftovers (batch
landed). `--check` reports a staged batch instead of reporting past it.

A failure-injection test makes every filesystem call in a `--write` fail
in turn, both as a one-off error and as a crash with no in-process
rollback. It asserts the retry produces exactly the uninterrupted
CHANGELOG.md, with each entry once and an empty directory. The test
fails against the previous write-then-delete and against delete-first.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 11:01:25 +01:00
Said Turksever 792c984e63 Merge branch 'main' into contrib/gods-next-31-gbfs-unknown-counts-20261013 2026-10-16 10:57: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
German KhokhlovandClaude Opus 5.5 48e90c3780 Merge origin/main: CCTV model specs, transit Ontario, Brazil GBFS, camera packs
Conflicts:
- CCTV HUD: main's datasheet FOV, capability range and spec summary,
  with their labels translated; specFovCapabilityToken takes a format
  so the HUD can pass a translated one. Datasheet resolution labels are
  camera data (i18n-ignore).
- Transit: main's route labels and operator mode words, with the
  generic words still translated; an operator's own word stays a name.

Catalogs: es/ru 2566 messages each, complete.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 12:58:23 +04:00
ulihhhhandClaude Opus 5.5 194595e1b6 feat(bikeshare): add Barcelona's Bicing and AMBici
- Bicing (543 stations, PBSC) and AMBici (237 e-bike stations across the
  metropolitan area, nextbike) join the GBFS registry from the operators'
  keyless GBFS 2.3 feeds, both listed in MobilityData's systems catalogue.
- gbfs.nextbike.net joins the proxy allowlist as an exact host.
- Credits, DATA_SOURCES, CURRENT-STATE and CHANGELOG updated; a test pins
  each system's proxy reachability and the range check for its outermost
  stations.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 10:54:58 +02:00
Trygve Hemminghytt 2bec3d521b Merge remote-tracking branch 'origin/main' into HEAD
# Conflicts:
#	docs/CURRENT-STATE.md
#	server/providers/cctv/sources.js
2026-10-16 07:21:30 +02:00