Selecting a vessel asks Open Vessel Photos (a free, keyless API) for its
photo by MMSI, once per MMSI. The CONTACTS view shows it under the vessel's
name and MMSI with the photographer, the photo's open licence and a link to
the vessel's page; only images on Open Vessel Photos' own host are shown, and
an answer about another MMSI is no photo. The vessel source exposes the lookup
as getEnrichment({kind: 'photo'}) and fetchOpenVesselPhoto for applications
with their own vessel source.
The core layer lifecycle imported the whole Data Library manifest
contract only to check the lib- prefix, so the layer-lifecycle package
owned a feature module. The namespace rule (prefix, entry id pattern,
the two id helpers) now lives in src/data/libraryLayerIds.js, which the
lifecycle owns; the manifest contract imports and re-exports it, so
library callers are unchanged.
The dialog is now DataLibraryDialog.vue (with DataLibraryCard.vue for each
entry), placed with the Browse button by DataLibraryBrowse.vue in
LayerPanelsView and teleported to the body. It renders what the
gev-data-library store holds and sends clicks and typing to the owner.
src/ui/libraryBrowser.js is now the framework-free owner: it presents the
open state, filters, the rows they show, the stat summary and Remove all
through `publish`, and its controls answer the view. Its text helpers are
what the view renders with t(), so every label follows the locale without
rebuilding anything. Only showModal/close and focus moves stay imperative;
focus returns to Browse also when the browser closes the dialog on Escape
itself.
Component tests mount the view over the real owner (open, filters, Add to
Remove keeping focus, Zoom, Remove all, a language switch, clean view).
A link check of every manifest found 404 licence or provider pages for
OpenSeaMap seamarks, EMODnet bathymetry and the PDOK, basemap.at, Geneva
SITG, ICGC Catalunya, Luxembourg and Vlaanderen aerial photos, and no
answer from the SEDAC GPW, Minnesota and Bangor hosts. Drop those 11
entries, renumber the unpublished share-token ledger, point the tests at
remaining entries and regenerate DATA_SOURCES.md.
The City of Amsterdam's verkeersinformatiesystemen register lists where
its ~500 traffic and ANPR cameras stand, with type and asset number, but
publishes no pictures. That fits a Data Library dataset better than a
CCTV pack: the register answers GeoJSON in WGS84 with CORS headers, so
the entry fetches it browser-direct with no proxy code.
The register names no licence ("public unless stated otherwise"), so the
entry stays operator opt-in, as #251 had it. The dataset and the
position-only camera idea come from @Maher-Reven's #251, which this
replaces; the entry lists them as maintainer and in its attribution.
Co-authored-by: Maher Kurdi <[email protected]>
DATA LAYERS keeps its curated core layers; regional and niche sources
live in the Data Library, opened from Browse data library at the foot
of the panel. Each entry is a JSON manifest in src/library/entries/
pointing at a live upstream (query API, provider-hosted file or tile
endpoint), so nothing is bundled and a self-hosted instance hosts no
data. Sources without CORS go through one allowlisted /api/library
route. Kinds: dataset (GeoJSON, CSV, JSON records, ArcGIS, partitioned
GeoParquet), tiles (XYZ, WMS/ArcGIS bbox export, terrarium relief) and
pack. Selections persist and travel in share links as lib=, separate
from the core layer tokens. Library rows get Zoom and Remove icon
chips that work while the layer is off, rendered through RailChips.
Strings are translated in every locale.
Live trains from 30 keyless operator feeds: GPS positions from 29 and
Irish Rail's station reports, shown as the operators publish them. 28
work out of the box; Renfe long and medium distance and KTMB appear once
their pack is built, because their vehicles name no route.
Packs are opt-in (scripts/build-rail-paths.mjs): the operator's own
route lines, stop names, next stops and trip-update delays for live
trains. Nothing downloads when the app starts, no train is placed from a
timetable, and no request carries a deployer's key.
Translate the 148 strings main added since (mostly the News layer) and
prune the 5 it no longer uses; keep both Hebrew and Swedish in the
language lists.
A live source may now declare a timeline: it can answer a snapshot at a
given time (source.timeline = { coverage(), stepMs, subscribe? }, read by
readSnapshotTimeline). While such a layer is on it follows the world clock
and registers the coverage. At the live edge it polls exactly as before;
away from it the layer asks for getSnapshot({ ...query, atMs }) at the step
at or before the clock time and shows that snapshot instead of polling.
src/time/snapshotTimeline.js owns the following: a seek, time going
backwards or leaving live replaces every contact with the snapshot at the
new time (play waits on it, for at most 8 s); playing on keeps one
snapshot ahead and gates play on it, applying those snapshots as polls so
contacts move smoothly; outside coverage the layer is empty; back at live
it is cleared and polls at once. One load at a time, and a load the
source drops for a newer one is not a failure.
While following, aircraft are drawn at the clock time with no render
delay, records and absences are judged at the clock, track requests carry
atMs, and vessels move between fixes along the great circle. The world
clock counts jumps (seeks and returns to live) in getState().
A source with no timeline never follows the clock, and every layer stays
live, unchanged.
The detection overlay only hid a contact behind the Earth's curve, so a
street vehicle behind or under a building, a 3D tile or a hill still had
its bracket and "VEH-nnnn" callout painted over that geometry, while its
own dot was depth-tested and correctly hidden.
A contact a layer marks `depthTested: true` (only Street Traffic does) is
now tested against the depth the scene drew and left out of the overlay
while a drawn surface stands in front of it by more than max(3 m, 2% of
its range). Other layers are untouched: their markers are drawn over the
scene on purpose.
The test runs on the GPU and never waits for it. Each tested contact is
one point drawn inside an occlusion query; the vertex shader looks up the
frame's packed pick depth at the contact's pixel, decodes it and drops the
point when the surface is nearer than the contact's threshold. Query
results are collected on later frames. Reading depth back was measured
and rejected: `scene.pickPosition` per contact costs 0.2 to 1.4 ms each,
and Chromium answers `getBufferSubData` with a round trip to its GPU
process even behind a signalled sync object (1.5 ms on a quiet machine,
15 to 25 ms on a busy one).
- Contacts are retested every 100 to 200 ms, spread across frames, at
most 256 a frame; with more in view the pace stretches so none starves.
- Hiding is immediate. A hidden contact returns 600 ms after its last
"hidden" answer, so one on a silhouette edge does not blink, and a
contact new to the view is held back until its first answer (400 ms at
most), so orbiting does not flash brackets behind buildings.
- A callout whose contact goes behind something is dropped at once, not
carried to the next solve and not faded out on the facade.
- These times run on painted frames, so a hidden tab does not expire them.
- A control point in every batch must draw; if it does not, the batch is
ignored and after three the probe stands down for ten seconds. With no
pick depth (2D, Columbus view, depth picking off) nothing is hidden.
A WebGL 1 context reads depth back synchronously about 8 times a second.
The renderer internals this depends on are read in one function,
`readSceneDepthSource`, with the engine version they were checked against.
Main added the News layer at the same places: both layers are kept in the
layer names, panel order (News stays last of Events), standalone sources,
voice manifest, credits, DATA_SOURCES and the CURRENT-STATE table. News took
share-link token 00 first, so Shipwrecks moves to 01 (ledger, registry,
CURRENT-STATE and the token tests). The catalog now has 40 layers and the
share-link registry 38. Shipwreck strings are added to the new Hebrew
catalog; the other catalogs keep both sides' strings.
They were listed as owned by the ghostmaps and ghostmaps-source packages,
which they are not part of; ghostmaps-source is meant to stay free of
rendering code, and shipwrecks/index.js imports Cesium. The app groups that
own every layer keep them.
The trail no longer joins a day of history into one line. It lifts the pen
across a gap of more than 48 minutes, an implied speed over 1,500 kt, or a
report without altitude (a parked aircraft within 0.1 nm for under 3 h stays
joined), and every history point is kept as its own segment. History never
gets an invented altitude. The live end is the path the aircraft was drawn
along, thinned to its shape, so it no longer cuts corners through turns; a
late report trims what was drawn after it. Old segments are frozen in chunks,
so a 2,000-point trail costs about 0.1 ms a frame.
A tracked aircraft that misses an update shows STALE and is dimmed. It keeps
flying on its last speed and heading for five minutes (at most 1,500 kt),
then stops, dimmed, labelled SIGNAL LOST with the time since its last report,
and is kept for ten minutes after that report so the camera keeps its target.
Aircraft on or near known ground hold at their last report instead of
coasting. Military flights follow the same rules.
- A pasted or start-up link opens its photos only when its restore's camera
landed: a drag or search during the restore flight keeps the camera.
- A posted view whose photo step was cancelled or superseded no longer closes
the viewer or flies; one that found a photo it could not load keeps the error.
- openNearest gains `replace` (keep the open photo while it looks, close it
only when nothing opens, so EXPAND stays) and `proceed()` (asked before a
found photo loads); the action uses both, so a navigation, a gesture or a
hiding mode during the lookup loads no billed panorama.
- Mapillary's timeout message is a msg() string with all eleven translations.
Every camera find_alpr_cameras returns comes from the tile source, which builds
it through normalizeAlprNode, so the tags are already one line and capped.
Drop the second pass in the tool, its duplicate test and the boundary entry,
and the function check normalizeDisplayText never sees.
Conflicts:
- docs/CURRENT-STATE.md: keep main's hidden-tab polling paragraph and the
display-text normalization paragraph.
- scripts/package-boundaries.json: keep main's voice panel and text chat
modules and add src/data/displayText.js.
- src/voice/gevActions.test.mjs: keep main's set_panel_open and partial
analyst card tests and the display-text tests.
Tracking a civil flight asks Planespotters for that aircraft's photo, once
per aircraft, straight from the browser. The CONTEXT panel's CONTACTS view
shows it under the flight's name with the photographer's credit, as a plain
link to the photo's page. Only images on plnspttrs.net and pages on
www.planespotters.net are ever shown or linked. Military aircraft and a
converted TR-3B are never looked up.
Flight lookups (type, route, photo) whose source failed temporarily are
asked again after a 60-second cool-down; an answer, found or not, and a
permanent refusal stay final for the session. A route belongs to the
callsign it was looked up for, so a callsign change drops the old route.
Browser QA scripts that track a flight answer the photo lookup themselves,
so they never call the third party.
- Add the full catalog (2940 entries keyed by the English text; 75 plural
entries carry one/two/other per Intl.PluralRules('he')), reviewed for
meaning, terms, separators and label length.
- Register he: he-IL and he map to it; it joins RTL_LOCALES so <html
dir=rtl> mirrors the chrome through logical properties and rtl.css.
- rtl.css: Noto Sans Hebrew (loaded with the other Google fonts) leads the
mono and sans stacks, tracking released like Arabic, HUDs keep JetBrains
Mono for Latin telemetry.
- same-as-english: instrument codes and HUD readouts stay as printed
(HDG/BRG/CRS/FOV/CRT/NVG/FLIR/CAL/SRC, MW units, Sentinel-2, km/h...).
- docs/translations/he.md: style, plurals and glossary.
- docs: TRANSLATORS.md and CURRENT-STATE.md updated; normalizeLocale test
covers he-IL.
The orbital Context card is gone and the Contact card stays hidden in orbit
again. Live Signals page 1 had nothing in orbit but TRACK ACQUIRED, because
orbit feeds no Contacts snapshot; it now lists what is around the
satellite, built the way the aircraft rows are, five rows with stable keys
rebuilt once a second. Rows already shown keep their places while they
still qualify, so a site passing under a low orbit is not pulled from under
the pointer.
Site rows come from the Mapped Installations layer (getNearby, asked only
as far as the reach extends) and focus their site, or release it on the
focused site's row, which reads as the subject does. A site the layer drops
during its frequent reloads stays listed for 10 s, since sites do not move.
With the layer off nothing is listed and a SITES STANDBY row says why.
Satellite rows come from the satellites layer's bounded nearest query,
measured from the satellite itself, and ride the satellite; RETURN goes
back to the one before, skipping one that has left the catalogue. While
the camera hops to a new satellite only TRACK ACQUIRED and RETURN show, so
nothing is measured from the old position. An arrow pressed while a focus
eases back takes the arrows' own clock. Site and satellite rows name their
own actions.
In the satellite orbital view a single click on a mapped ground point (a
ground point of a layer that is on, such as a Mapped Installations site, a
fire or a local data pack feature) keeps the camera on it. Each frame the
look is aimed from the satellite's position to the point's Earth-fixed
position in the track-up nadir frame the arrows tilt, so the point stays
at the centre as the satellite moves. The swing onto the point, or between
points, uses the arrows' easing on a slower clock and then holds the point
exactly; reduced motion cuts.
A click elsewhere, FOCUS on the focused point, NADIR, Home or 0 ends the
focus and the view eases back to nadir; an arrow ends it and tilts on from
where the view is. The focus also ends, easing back, when the point leaves
the ±45° reach or the satellite's horizon, when its layer is turned off or
its record is dropped, and with the view. While focused the readout gives
the single off-nadir angle to the point (OFF-NADIR 23.4°), names it in a
polite live region, and the swath reads approximate as for a tilt. Clicks
still reach Cesium's own handler and never untrack the satellite; a click
a tool owns is ignored.
Under the readout, IN REACH lists the mapped points facing the satellite
that are within the reach or in the frame, nearest to nadir first and at
most 12, rebuilt once a second. A row opens the point's position, source
and OpenStreetMap tags (or its layer's properties); FOCUS focuses it,
switches to it, or ends the focus held on it, and is unavailable for a
point in frame but beyond the reach.
An imaging satellite with a known sensor can now zoom past 8x, up to 128x,
as far as the view stays at least as wide as the sensor's narrowest
standard swath at its live altitude; only swaths confirmed on a cited page
open those steps. The top step follows the altitude and window shape and
steps back when it falls below the zoom shown.
The view draws the sensor's swath to scale along the ground track with a
SENSOR SWATH readout (approximate off nadir), and a SENSOR button zooms to
exactly that width when it lies in range at nadir. From 16x the imagery
tiles the view will need 5 to 20 s ahead along the ground track are
pre-loaded at the matching level. The mouse wheel steps the zoom one notch
at a time, and the identity line shows how old the TLE is. The sensor
figures come from a new bundled pack of 46 Earth-observation platforms,
each value cited to its operator or agency, with its own data credit.
With a satellite tracked and no aircraft tracked, Cockpit can ride the
satellite. Entry needs only the Satellites layer; debris can be tracked but
not entered, and voice stays aircraft-only. Double-clicking the tracked
satellite or the entry buttons (reading ORBIT) enter it.
The camera sits on the satellite's per-frame SGP4 position and looks
straight down, turned to the ground track (north-up for a satellite with
none). The optical zoom steps 1x to 8x with a focus pull, the arrows tilt
the view up to 45 degrees off nadir, and Home or 0 returns to nadir. Off
nadir the tilt reads its total angle first and the swath readout becomes an
approximation marked with ≈. While in orbit the map leases flat Esri
imagery, the cloud pass stays off and the view opens in the optical style.
The orbital HUD names the satellite with its owner's flag (from the
flag-icons package, loaded per country) or an organisation badge, its
operator, class, NORAD and COSPAR ids and TLE epoch with the element lines
on request, altitude, orbital speed and the sub-satellite point. CURRENTLY
OVER names the country and its first-level division, or the sea, and from
8x the most prominent populated place nearby, looked up offline from the
bundled Natural Earth packs (populated places are a new pack). The
satellites layer hides the ridden satellite's own marks, dims the other
orbit rings and ignores scene clicks and Escape while held. Exit restores
the camera's field of view and the satellite's follow camera.
C no longer enters or leaves Cockpit, bridge or orbit: it is left to the
app-wide CCTV shortcut, and Escape or the exit button leave first person.
Only catalog and the tracked satellite were saved for the layer, so a class
chip lasted until the next reload and no share link carried it. The choice
is now the satellites option `classes`: one letter per class shown
(`sitpd` by default, `0` for none), with Starlink left to `catalog`, and
the layer takes the letters back through setParams. A partial request
(`{debris: false}`) hides only what it names.
The satellites layer now sorts every object into Stations, Imaging,
Telecom, Starlink, Probes or Debris, each with a chip and a live count on
the layer row. Stations and Imaging show by default; the Starlink chip is
the former DENSE catalog switch. Hidden classes keep their points, so a
toggle needs no reload, and they leave the detection overlay and the voice
position lists.
A class comes, strongest evidence first, from CelesTrak SATCAT (object type
and status, served by a new /api/celestrak-satcat/<group> proxy route and
cached for a day), the bundled UCS Satellite Database pack (operator, owner
country and purpose), name patterns for later launches, and the CelesTrak
group. The core catalog also reads the resource, weather, planet, sar and
science groups. Payloads with no evidence of life launched 20 or more years
ago are filed as debris. The UCS pack, its build script and its data credit
are added.
The ship bridge rules were appended to cockpit.css, taking it to 2705
lines; they move unchanged to cockpitBridge.css, imported right after it so
the cascade is the same. cockpitBridge.css and cockpitRadar.css join the
format scope and the styles package.
Adds a complete Swedish catalog (2,961 messages) and registers sv in the
locale list, catalogs, language picker and package boundaries. Browsers set
to Swedish get it at once; ?lang=sv or the language picker choose it
elsewhere. Numbers and dates come out Swedish from Intl.
- docs/translations/sv.md: style (imperative without a pronoun, closed
compounds, decimal comma, space before units and %) and the glossary.
- Entries that stay identical to English on purpose (codes such as HDG and
FOV, product, place and source names) are listed under sv in
same-as-english.json.
- The picker-order test places Svenska after Português.
- docs/CURRENT-STATE.md and docs/TRANSLATORS.md list the new language.
The translation comes from God's Eye View Sverige, a Swedish fork, and was
brought up to date with main (wildlife sightings, event age filter, traffic
styles and the rest).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
With no aircraft tracked, Cockpit can ride the selected AIS vessel. Entry
needs only Live AIS Vessels and a selected vessel; C, double-click and the
entry buttons (reading BRIDGE with a ship icon) enter it, while voice stays
aircraft-only.
The eye sits on the AIS position at a representative bridge height for the
vessel's type family, looks along true heading and moves along course over
ground. The last fix is dead-reckoned for up to 180 s between the layer's
refreshes, and on rivers and lakes the waterline is read from the rendered
surface around the ship rather than the geoid.
The bridge HUD shows speed and course over ground, a straight heading
tape, window mullions, the AIS destination as a reported voyage and a
heading-up radar of nearby ships (3 to 24 NM). The arrow keys look around,
Home or 0 looks ahead again. The vessel layer hides the ridden ship's
sprite and card, ignores scene clicks while held, and returns the camera
to the vessel's map view on exit.