Commit Graph
601 Commits
Author SHA1 Message Date
Sameh Khamis 39c89d563b Draw transit vehicles as trolleybuses, cable cars, funiculars, gondolas and monorails
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.
2026-11-14 12:03:12 -08:00
Sameh Khamis 52507a0755 Draw no buoy for a virtual aid to navigation
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.
2026-11-14 11:07:13 -08:00
Sameh Khamis 6c7fe48f40 Show an aid to navigation's kind, not a speed or heading
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.
2026-11-14 10:02:59 -08:00
Sameh Khamis 279aedb2f4 Draw aids to navigation as buoys
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.
2026-11-14 09:54:01 -08:00
Sameh Khamis 7b93045895 feat(vessels, transit, rail): draw fleet models under the 3D toggle
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.
2026-11-13 07:16:25 -08:00
Said Turksever 24ccdd4dc3 Fix Data Library share restore, dense tile layers and EPC partition reads
- 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.
2026-11-12 22:37:34 +00:00
Said Turksever db80d82b8d Add the Data Library: live regional datasets, tiles and packs
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.
2026-11-12 12:26:45 +00:00
Denis Groshev 9b3ca481ce Merge upstream main into denis/rail-layer 2026-11-11 23:23:58 +01:00
German Khokhlov 94ff78be3b refactor(routes): inline the single-use 502 responder
The outage branch is the only caller, so it writes its 502 directly.
Same status, headers and body.
2026-11-11 21:23:35 +04:00
German Khokhlov 82e2b716bc docs(routes): tighten the outage comments and notes
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.
2026-11-11 21:21:07 +04:00
German Khokhlov a29611ca17 Merge branch 'main' into upstream/route-outage-unavailable 2026-11-11 21:16:50 +04:00
Denis Groshev 13818d0b9f Merge upstream main into denis/rail-layer 2026-11-11 18:02:52 +01:00
Denis Groshev 830c080803 feat(rail): add a Rail layer with keyless live trains
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.
2026-11-11 16:06:22 +01:00
Said Turksever f878efe22d Merge commit '0ceee8df40b97c6c693e86a4443794cd4ed1ff35' into HEAD 2026-11-11 08:54:28 +00:00
Sameh Khamis bc786f8c55 fix(news): Conflicts reports of the same time come in a fixed order
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.
2026-11-10 22:40:15 -08:00
Sameh Khamis b89f81b729 fix(voice): keyless Gemini voice drops the microphone hint
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.
2026-11-10 17:06:50 -08:00
Said Turksever 09d02b6e7a Keyless voice: say where to add the OpenAI key instead of blaming the microphone 2026-11-10 16:11:17 -08:00
Sameh Khamis 8ff22f1a12 Merge main into feat/shipwrecks
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.
2026-11-10 14:03:39 -08:00
Sameh Khamis 7704de5075 fix(shipwrecks): the page expires each source's wrecks by its own clock, from the age the proxy reports
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.
2026-11-10 13:38:31 -08:00
Sameh Khamis 66fba3da47 fix(shipwrecks): a source refresh with less than half of its last good wrecks is refused until the old set expires
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.
2026-11-10 13:36:18 -08:00
Sameh Khamis 38ba8a3b59 perf(shipwrecks): draw the wreck markers from one billboard collection, on the wreck, and rebuild them only on new data
- 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.
2026-11-10 13:25:03 -08:00
Sameh Khamis 3b52e24140 fix(shipwrecks): a failed or empty source refresh keeps its last good wrecks, and waits 15 minutes before the next try
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.
2026-11-10 13:20:49 -08:00
Mazeyar Moeini FeizabadiandClaude Fable 5.2 671a256a6a fix(news): follow-up review of the fixes
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]>
2026-11-10 21:34:58 +01:00
Mazeyar Moeini FeizabadiandClaude Fable 5.2 7cd2af46e4 fix(news): one-hour World News API retention, refunded reads, pins, disputed areas, counts
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]>
2026-11-10 21:26:48 +01:00
Mazeyar Moeini FeizabadiandClaude Fable 5.2 49ac289d00 fix(news): gate /api/news same-site, answer 400 on a bad request line, relay key states
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]>
2026-11-10 21:26:48 +01:00
Mazeyar Moeini FeizabadiandClaude Fable 5.2 3342d7a07a Merge origin/main into review/pr-1593
Conflicts: docs/CURRENT-STATE.md layer table, src/ui/layerPanel.js imports,
src/data/dataCredits.test.mjs imports; both sides kept.

Co-Authored-By: Claude Fable 5.2 <[email protected]>
2026-11-10 21:20:15 +01:00
Sameh Khamis 4ef0869a87 fix(firms): drop LIVE from the row's source and the voice answer for a snapshot that ends at a set time
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.
2026-11-10 08:19:40 -08:00
Sameh Khamis 43706b8dd0 fix(firms): label a snapshot that ends at a set time with that time, not LIVE
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.
2026-11-10 08:11:58 -08:00
Said Turksever 5370b8a268 Merge upstream main into pr/street-level-show-action
gevActions.test.mjs: keep the PR's street-level tests and main's analyst display tests.
2026-11-10 14:59:39 +00:00
Mazeyar Moeini Feizabadi ddd07f13f0 Merge branch 'main' into fix/realtime-voice-unicode-patterns 2026-11-09 19:44:29 +01:00
German Khokhlov fce5ee6381 docs(firms): tighten the Suomi NPP notes 2026-11-09 21:44:09 +04:00
German Khokhlov d90da158ea Merge branch 'main' into fix/firms-drop-snpp
# Conflicts:
#	docs/CURRENT-STATE.md
2026-11-09 21:41:01 +04:00
quanticsoul4772 f7b0367d4b Merge remote-tracking branch 'origin/main' into upstream/route-outage-unavailable
# Conflicts:
#	docs/CURRENT-STATE.md
2026-11-09 08:14:35 -08:00
German Khokhlov c3738c0fdb docs(server): tighten the method-guard comments and changelog 2026-11-09 19:07:14 +04:00
German Khokhlov ace88172d3 Merge branch 'main' into fix/api-method-guard 2026-11-09 18:51:02 +04:00
Denis Groshev 0fdde159cb Merge branch 'main' into denis/gev-news-layer
# Conflicts:
#	DATA_SOURCES.md
#	docs/CURRENT-STATE.md
#	src/data/local_data/natural_earth/README.md
#	src/i18n/same-as-english.json
#	style.css
2026-11-09 15:35:56 +01:00
German Khokhlov 9d7fe8fcaa Merge branch 'main' into feat/opensky-compress-cached-body 2026-11-09 18:30:32 +04:00
German Khokhlov caad795fa2 refactor(server): drop the null and String paths from gzipBody 2026-11-09 18:15:13 +04:00
German Khokhlov 09bf01630b docs(server): tighten compression comments and docs 2026-11-09 18:14:30 +04:00
Andrés Valdez 9f2dfdf820 fix(voice): send OpenAI Realtime tool schemas the API accepts
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
2026-11-09 07:34:44 +00:00
Said Turksever 7efcf37c62 Merge upstream main into pr/street-level-show-action
Locale conflicts: main's catalogs plus the PR's 10 street-level strings.
2026-11-09 00:22:26 +00:00
Sameh Khamis 8ae56587c3 fix(satellites): a SATCAT failure no longer stops TLE refreshes
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.
2026-11-08 15:16:01 -08:00
Denis Groshev c56f098d07 feat(satellites): file satellites under six classes with a toggle each
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.
2026-11-08 23:34:55 +01:00
Denis Groshev a3902590cd Merge branch 'main' into denis/gev-news-layer 2026-11-07 23:19:12 +01:00
Itallo BrandãoandClaude Opus 5.5 584138decf Merge main into feat/shipwrecks
Keeps main's reformatted layer table in docs/CURRENT-STATE.md and adds the Shipwrecks row to it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 18:37:28 -03:00
daika 83bd48102a Merge remote-tracking branch 'upstream/main' into rb/opensky-scope 2026-11-08 04:02:08 +07:00
Said Turksever e5aee68e1d Merge upstream main into pr/street-level-show-action
Locale conflicts: main's catalogs plus the PR's 10 street-level strings.
2026-11-07 18:45:57 +00:00
Said Turksever 084a63ab5f Merge upstream/main into fix/taiwan-highway-cctv-fallback 2026-11-07 17:54:15 +00:00
Said Turksever 6af14f7f13 fix(cctv): leave Iceland to its own pack, credit World and Canada packs
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.
2026-11-07 17:53:55 +00:00
Said Turksever cc3d390911 Merge upstream/main into fix/taiwan-highway-cctv-fallback 2026-11-07 17:46:53 +00:00