The transit layer knew five modes, so a trolleybus drew as a bus, a
funicular as a train and a coach as a city bus. The GTFS basic route types
are now modes of their own (trolleybus, cable_tram, monorail, funicular,
aerial_lift), each with an icon, colour, detection tier, card word, voice
name, speed ceiling and fleet model.
A vehicle record may carry its route's GTFS route_type as routeType. It
settles the mode after the feed's fleet hint and before its route hint, and
picks the finer model (a coach for 200, a high-speed set for 101) when the
type is a kind of the vehicle's mode. Retained history classifies epochs
through transitModeFor too, so trails join the live vehicle.
Route hints from each operator's routes.txt: Prague's trolleybus lines
(51-59, 232, 275) and Petrin funicular, and the trams, trolleybuses and
metro lines of Bucharest, Cluj-Napoca and Turin. A feed may also name
route corrections that outrank the route type: Le Havre's routes.txt types
its funicular (8-FUNI) as a bus, so it is corrected to a funicular.
A virtual aid (AIS message 21's VirtualAtoN flag) is a mark broadcast
where nothing physical stands, so drawing it as a buoy showed a mark
that is not there. The vessel source record now carries `virtual`
(false unless a source says true; any source may set it), AISStream
sets it from VirtualAtoN, and the vessel layer keeps such an aid's
disc: vesselModelSpec returns null for it and the layer gathers no
model item for it. Its card, selected card and readout add VIRTUAL
after its kind.
A buoy's card, selected card and HUD readout showed "0.0KT · 135°" from
whatever its source reported. A fixed mark has neither, so they now show
its kind alone, whole ("CARDINAL MARK N", which the 14-character type cut
read as "CARDINAL MARK "), in the bone white of its disc.
isAidToNavigation now knows every kind an aid report names ("Light, with
sectors", "Fixed structure off shore" had no aid word), leaves out a
"Buoy Layer" as it does a tender, and matches each type text once: the
vessel layer asks per vessel on its rotation pass, which now skips the
aid discs it never turns.
Aid rows no longer cost ships their place: aisStreamRows lists every
vessel ahead of every aid, so the layer's 12,000-row request never drops
a ship for a buoy, and vessels_in_area (and the brief built on it)
counts ships only. An aid's name runs on into its NameExtension.
A buoy, beacon, RACON, light or IALA mark in the vessel feed was drawn
as a ship. A vessel record whose type names one, or whose MMSI is an
aid-to-navigation station's (99MIDxxxx), is now isAidToNavigation and
vesselModelSpec draws it as the fleet set's buoy of its mark (cardinal
N/E/S/W, port or starboard hand, safe water, isolated danger, special
mark; a red can when the type names no mark), at its built size and
flagged fixed so its model does not turn to a heading. Its icon is a
disc instead of a chevron.
AISStream now subscribes to AidsToNavigationReport by default, and the
server names each report's aid type code (0 to 31) as its kind instead
of reading it as a ship type.
Vessels, Transit and Rail turn nearby icons into fleet models at the
aircraft layers' distances. A vessel is drawn at its AIS length and beam
(static dimensions now reach vessel rows), standing at the sea surface; a
transit vehicle or train stands on its road or rail, raised by its type's
elevation, and is painted in its mode or category colour where the model
marks it.
- Share links: the compact /share/<code> format carries the switched-on
library entries (field 29), so Copy Link keeps them; a share opened in the
same tab restores them; any share decides what is on, so one with every
library entry off no longer switches on the recipient's stored entries.
- A tile entry too dense at its minimum zoom (Norway cadastre z14, depth z11)
is hidden below that scale via minimumTerrainLevel instead of allocating
millions of tiles when zoomed out.
- EPC partitions keep the full extent for exclusion and the 98 % extent for
ranking only; the old trimmed-only extents are retired.
- Partition index footers that fail transiently are retried; an incomplete
index is never cached as complete, a complete prior index is kept, and it
is rebuilt after five minutes.
- Views that leave touching partitions unread are partial; listing and
footer bodies are capped and ranges validated.
- The Browse dialog relabels after a language switch.
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.
Shorter comments, changelog and CURRENT-STATE text for the 502 outage
path; the CURRENT-STATE paragraph now also names the 429 exception and
which budget expiry answers busy and which answers 502. Text only.
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.
GDELT exports are read in parallel, so two reports with the same time
arrived in whichever order their exports finished, and the feed test that
pages through them failed now and then. Same-time reports now order by
most mentions, then by id.
The Gemini token route now answers a missing key with code API_KEY_MISSING,
the session carries it to the error state as missingKey, and the voice
control hides "Check microphone permission" for that error, as it already
does for OpenAI. The OpenAI controller sends the same flag, so the shared
control and the controller agree whichever updates last.
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.
The page compared the proxy's fetchedAt with its own clock, so a skewed
browser clock cleared or kept wrecks wrongly; a snapshot without a timestamp
never expired; and one old source cleared the other's fresh wrecks. The proxy
now reports each source's age on its own clock and its seven-day limit; the
page turns them into per-source deadlines when the snapshot arrives (seven
days from arrival when no age is given) and, on a failed refresh, removes only
the sources past theirs. A refused short refresh is logged.
A 200 answer that was valid but cut short (30 Wikidata wrecks instead of
about 1,450) replaced the last good set for a day. It now counts as a failed
refresh: the old records are served as stale and the source is retried after
15 minutes; once the old set is past seven days, the new one is taken.
- The ~8,000 markers were one Cesium entity each; they are now one
BillboardCollection (as FIRMS, vessels and wildlife sightings do), ordered
at the bottom of the shared sprite stack so moving traffic draws over it.
- eyeOffset.z was the death toll in metres, so a 1,500-death wreck's marker
sat 1.5 km toward the camera and clipped or drifted when close. It is gone;
markers are added by rising toll, so a bigger toll still draws on top.
- The page asks every 6 h but the proxy changes data at most daily: the proxy
now sends a version that changes with either source's data, and the layer
rebuilds only when it does.
- Adds a controller test: enable/disable, teardown, draw order, unchanged
snapshots, a late response after turning off, and the period closing an
open card.
A refresh with one source down (or Wikidata answering 200 with no results)
was cached as the day's snapshot, replacing the full set; and once the
snapshot was a day old, every request after a failure fetched both sources
again. Each source now keeps its own last good records, served as stale for
up to seven days, and a source that failed is not asked again for 15 minutes,
even when there is no data yet. A Wikidata answer without results, or with no
wreck in it, counts as a failure.
Deleted World News API news is not restored by a refresh that fails; a
refund goes only to the day the read was counted in, and not to a request
that timed out; a cockpit brief from another place is dropped, not kept for
a language change; a feed list started over lets scrolling ask again.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
Keep World News API results an hour at most and say caching needs its
permission; refund a read that never reached it; match Local by any pin;
assign a disputed area by a point inside it; untitled GDELT stories are
minor; hotspot counts leave out minor reports.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
A malformed request line crashed the server, any web page could spend the
news keys, and the forwarding proxy turned the news server's 503 key and
configuration states into a generic 502.
Co-Authored-By: Claude Fable 5.2 <[email protected]>
The catalog source 'NASA FIRMS · LIVE' still led the row; getStats() now
names the source 'NASA FIRMS' for such a snapshot. get_active_fires says
"in the 24 hours to" its end and reports window_end. The end is checked
to be a printable time, cleared under KEY REQUIRED, the test moves next
to the layer, and the changelog and CURRENT-STATE describe it.
A FIRMS source may answer the 24 hours to a set time, naming its end as
`at` (epoch ms). The layer row then reads "24 h to MM-DD HH:MM UTC"
instead of "LIVE · updated ...", and getStats() reports `at`. A
snapshot without `at` (the trailing 24 hours to now, as the dev proxy
serves) keeps the LIVE label. Translated in every catalog.
OpenAI Realtime now answers 400 invalid_function_parameters for a whole
session whose tool schema uses Unicode property classes, as resolve_area's
area-id pattern does, so voice never connects ("Failed to create Realtime
token"). The session sends its tools through chatSchema, the filter typed
chat adopted in #1616, which leaves out only \p{…} patterns; area ids are
still checked against the action's own contract before the tool runs.
Fixes#1618
Both CelesTrak routes shared one cooldown, and any failure set it: one bad
SATCAT answer made /api/celestrak return 502 "cooling down" for a minute,
failing the Starlink load and stalling every TLE group past its cache.
Throttling (403/429) still holds both routes off, since CelesTrak throttles
the client; any other failure holds off only the route that failed.
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.
Main now ships an Iceland pack that loads the full live Vegagerðin list
(about 500 views) with its CC BY 4.0 credit. The World catalog's 166
Vegagerðin rows point at the same frames under different ids, so every one
of those cameras would be drawn twice. Drop them from the World export and
its loader policy; World is now 2,230 rows and the default catalog limit
7,350 (4,000 shared slots plus 3,350 reserved).
Register Data attribution entries for Ottawa, Québec, TripCheck and MLIT,
as every other CCTV pack has. MLIT's terms ask for the source in the form
"出典:国土交通省 川の防災情報" with the page link, and ODOT asks for
"Camera courtesy of ODOT"; TripCheck partner cameras keep their owner's
name in the label.
The Houston guard test from main checked the old 4,000 default ceiling;
check the 4,000 shared slots instead, like the other pack tests.