Commit Graph
1216 Commits
Author SHA1 Message Date
Cliff Flood 4ffbdda3f5 feat(transit): name YRT's local branch routes
YRT encodes a branch as a two-digit suffix on the base route id. The Viva
table already depended on that scheme — `60102` is Viva Blue's B branch —
but local routes were passed through whole, on the reasoning that a YRT
route id is already the number on the bus. For a plain route it is. For a
branch it is not: a rider watching route 83's B-branch bus saw "8301".

A 15:41 EDT sample of the live feed found four branch ids in service.
Three are published in York Region's GTFS `routes.txt` and are named here
from it, unpadded to match the local routes beside them (the file says
`083A`; the registry has always preferred `105` to `005`):

    10702 -> 107B   KEELE
    8301  -> 83A    TRENCH
    9002  -> 90B    LESLIE

The fourth, `9101`, runs in the realtime feed but appears nowhere in
`routes.txt`. The pattern all but spells "91A" and that is precisely why
it is absent: the registry translates published names, it does not infer
them. It stays opaque until York Region publishes it.

Renamed YRT_VIVA_ROUTE_NAMES to YRT_ROUTE_NAMES, since the table is no
longer only about Viva.
2026-10-15 20:18:15 -04:00
Cliff Flood a7fc5ea7c5 feat(transit): add Burlington, switch Barrie and YRT on
Burlington's route ids are internal keys with a trap in them: `351` is
route 1 while `3510` is route 10, so no prefix trim is correct and the
table is a real join against the City's own published GTFS. Its licence is
the same 2011 municipal template as Mississauga — revocable, and the terms
must travel with the data, so licenseUrl is the terms document itself.
Plain http:// has no listener at all, so https is mandatory, not preferred.

Barrie and YRT ship on. Both operators invite acceptance of their licence
through a form, and both licences also state that use is itself acceptance
("By using Barrie Transit Data, you agree to be bound by these terms";
"Your Use of Information indicates Your acceptance"). This deployment
relies on that clause, and both entries record it so the reasoning does not
have to be re-derived.

Nine Ontario feeds now, all verified live against their upstreams.
2026-10-15 20:18:15 -04:00
Cliff Flood 8ccb0cd895 feat(transit): register YRT/Viva off, named by Viva colour
York Region's feed is first-party infrastructure under an open licence that
permits commercial use and does not even require attribution. It is the one
feed that fills the gap north of Toronto, and it is switched off anyway.

The operator asks that its licence be accepted through a form collecting a
name, company, phone and email. No credential is issued and nothing gates
the endpoint — the licence body says use is itself acceptance — but a form
asking for contact details is close enough to the registration this registry
refuses that a person should decide it, not a poller. Barrie is off for the
same reason. Both are one line from on.

Viva route ids get labels: YRT publishes `601` where every station sign says
Viva Blue. The numeric local routes already read correctly and carry no
entry, so the table is six lines, not 125.

No vehicle in this feed carries a bearing; heading comes from observed
movement, as with Durham.
2026-10-15 20:18:15 -04:00
Cliff Flood 11dd914c2f feat(transit): add Guelph and Kingston, register Barrie off
Guelph's feed publishes the internal database key of a route (`2991`)
where the rider sees the number on the bus (`1`), which would have put
"Route 2991" on the label a person reads. So a feed may now carry its own
`routeLabel`, the same way it may carry its own `modeWords`. Guelph's table
is its 33 routes, taken from the City's own published GTFS; an id missing
from it renders unchanged, because a raw id you can compare against the bus
beats a blank. Every other feed is identity and carries no table.

Kingston is catalogued by the City against this exact URL. Its licence PDF
link is pinned to the canonical location rather than the legacy path that
currently redirects there — Durham's entry records what happens when such a
redirect is eventually dropped.

Barrie ships REGISTERED AND OFF. The City offers its licence for acceptance
behind a click-through gate, and nobody on this project has accepted it.
Whether to accept is a person's decision, not an assumption a poller should
make, so the feed is visible, documented, and one line from enabled.
2026-10-15 20:18:15 -04:00
Cliff Flood 849cb80375 feat(transit): add MiWay, Hamilton HSR and Durham Region Transit
Three keyless GTFS-Realtime feeds around Toronto, each verified live: all
vehicles carry real coordinates, and each is polled only near its own
region rather than across the Golden Horseshoe.

The licences differ in ways the registry now records rather than flattens:
Hamilton PRESCRIBES its attribution wording, so the credit is that exact
sentence; Durham makes credit optional and its catalogued licence PDF is
dead after a site relaunch, so licenseUrl points at the live page where the
v.1.0 text was actually read; Mississauga's licence is revocable and asks
that the terms travel with the data, so licenseUrl is the terms themselves.

All three are bus-only and set no route hint, so their modes default rather
than resolve. Durham publishes no bearing on any vehicle — heading comes
from observed movement, as with any feed that omits it.

GO Transit / Metrolinx stays out: its feed requires an API key.
2026-10-15 20:18:15 -04:00
Cliff Flood 9fc4a5ac04 feat(transit): let a feed name its own modes, and call TTC trams streetcars
A Toronto 501 and a Helsinki 1010 are both the `tram` mode. Only one of
them is called a tram by the people who ride it, and the detection label
is documented as "what a person standing at the stop would call it".

So the mode stays shared — same silhouette, same speed limits — and a
registry entry may carry `modeWords` naming the modes it runs. Everything
without an override falls through to the shared table, so this renames one
word for one operator and nothing else.

Threading the feed to the detection metric needed a resolver in queries.js,
since the cached-contact path has only the entry. It answers from live feeds
first, so a feed disabled mid-session stops supplying vocabulary.
2026-10-15 20:18:15 -04:00
Cliff Flood c5f7fe285b feat(transit): add the TTC Toronto surface feed
TTC BusTime publishes keyless GTFS-Realtime VehiclePositions with real
coordinates for the surface network: ~1,180 buses and streetcars with
bearing and per-vehicle timestamps. Registering it is one registry row.

The subway is deliberately absent. BusTime covers surface vehicles only
and the subway publishes no positions, so Lines 1/2/4 are left out rather
than inferred from schedules — the same rule that keeps NYCT out.

Route numbers establish the mode: the three-digit 500-series is streetcar,
as are the four Blue Night routes on streetcar trackage. A leading 5 alone
would misclassify bus routes 50-59, so the rule is anchored and tested.

Licensing is the portal-wide Open Government Licence - Toronto; the CKAN
record for the BusTime NVAS dataset leaves its own licence field blank,
and the terms note says so rather than overstating the grant.

Refs #70
2026-10-15 20:18:15 -04:00
Mazeyar Moeini Feizabadi a0400b52fb Merge pull request #741 from bilawalsidhu/fix/cyclone-advisory-time
fix(cyclones): accept advisories NHC publishes before their nominal time
2026-10-16 02:16:22 +02:00
Mazeyar Moeini FeizabadiandClaude Opus 5.5 6d6ce364c6 docs(cyclones): document the early-advisory window and pin its bounds
Maintainer follow-up for #741, no runtime change.

- CURRENT-STATE: advisory issue and position times up to and including
  90 minutes ahead of the server clock are accepted because NHC can release
  advisories before their nominal time; 90 minutes is the app's tolerance,
  not an NHC limit. Later, non-canonical and over-12-hour-old times still
  reject the snapshot.
- Tests: pin exact 90-minute acceptance and +1 ms rejection, and exact
  12-hour acceptance and +1 ms rejection, for issuance and lastUpdate
  independently; the early-publication test now asserts
  invalid_cyclone_data.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 02:03:16 +02:00
Mazeyar Moeini FeizabadiandClaude Opus 5.5 ef8a98fe0b Merge main (dde2b9d1) into fix/cyclone-advisory-time
Resolve the CHANGELOG.md conflict by keeping both sides: this PR's cyclone
entry stays above main's newer entries; no text changed.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 02:02:01 +02:00
Mazeyar Moeini Feizabadi dde2b9d186 Merge pull request #1110 from Rohilalala/fix/flights-unpositioned-rows-complete
fix(flights): don't treat OpenSky's position-less rows as a partial snapshot
2026-10-16 01:42:21 +02:00
Mazeyar Moeini FeizabadiandClaude Opus 5.5 2e4beb9418 Merge main (82022306) into fix/flights-unpositioned-rows-complete
Resolve conflicts with main by keeping both sides:
- CHANGELOG.md: keep this PR's flights entry above main's newer entries.
- src/layers/flights/records.test.mjs: keep this PR's position-less
  retention test alongside main's civil emergency-status test.

src/sources/live/aircraft.js merged cleanly: openSkySnapshot keeps main's
emergencyByIcao normalizer and passes openSkyUnpositionedId to admitRecords.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-16 01:30:21 +02:00
Sameh Khamis 8202230683 Merge pull request #644 from ch-bas/feat/wsdot-camera-pack
feat(cctv): WSDOT highway camera source pack (Washington State)
2026-10-15 16:21:45 -07:00
Sameh Khamis 7366a46eba Merge branch 'main' into feat/wsdot-camera-pack 2026-10-15 16:17:22 -07:00
Sameh Khamis ffc6e0f84f Merge pull request #595 from ulihhhh/feature/catalonia-cctv-cameras
feat(cctv): add the Open Catalonia camera pack
2026-10-15 16:16:42 -07:00
Sameh Khamis 05d8529edc Merge branch 'main' into feat/wsdot-camera-pack 2026-10-15 16:10:59 -07:00
Sameh Khamis ee10b2ce1c Merge branch 'main' into feature/catalonia-cctv-cameras 2026-10-15 16:10:47 -07:00
Sameh Khamis 584a95fbec Merge pull request #607 from Reloded/feat/lithuania-cctv
feat(cctv): add Lithuania Eismoinfo road cameras
2026-10-15 16:10:15 -07:00
Sameh Khamis 52d26b0577 Merge branch 'main' into feat/wsdot-camera-pack 2026-10-15 16:05:47 -07:00
Sameh Khamis a800d28ddf Merge branch 'main' into feature/catalonia-cctv-cameras 2026-10-15 16:05:20 -07:00
Sameh Khamis d3cedbaaaf Merge branch 'main' into feat/lithuania-cctv 2026-10-15 16:04:24 -07:00
Sameh Khamis 28501ddee3 Merge pull request #645 from ch-bas/feat/king-county-camera-pack
feat(cctv): King County road camera source pack (Washington State)
2026-10-15 16:03:02 -07:00
Sameh Khamis a75550024d Merge branch 'main' into feat/king-county-camera-pack 2026-10-15 15:57:27 -07:00
Sameh Khamis 4deafbb589 Merge pull request #640 from cliph/feat/cctv-toronto
feat(cctv): add the City of Toronto RESCU camera pack
2026-10-15 15:55:44 -07:00
Sameh Khamis 248a658713 fix(cctv): serve only live feeds on /media
/api/cctv/media relayed still-image cameras with an ordinary fetch: it followed
redirects to any host, passed through any content type and had no body cap.
Stills are served by /api/cctv/frame, which keeps redirects on the registered
origin, accepts only image/* and caps the body, and no client requests a still
through /media. /media now answers 404 for anything but HLS, MP4/WebM and
MJPEG, before any upstream request.
2026-10-15 15:53:09 -07:00
Sameh Khamis 3f239f1173 fix(cctv): give Catalonia cameras stable ids and pin their frame paths
Ids were built from the feed's fid, which embeds an upstream server-start stamp,
so every upstream restart renamed all cameras (it already had since the PR
fixture). Ids now come from the validated frame URL (cat-sct-<stem>, or host
and path for partner cameras), which also collapses the five duplicate rows.
Frame links must sit directly in each host's frame directory with no explicit
port, and sctidcam must be a bare image filename. Numeric entity decoding
handles hex references and leaves invalid code points as written, so one bad
name no longer empties the pack. Counts in the docs are now ~130.
2026-10-15 15:21:34 -07:00
Sameh Khamis 76bfd74a78 fix(cctv): harden the WSDOT catalog fetch and describe its real direction codes
The catalog fetch now refuses redirects, cancels rejected bodies and reads
through the shared capped reader before the windows-1252 decode. The feed sends
cardinal codes plus B (both), O and null, not BW or diagonals; tests, comments
and docs now say so, with unchanged id-hash fallbacks. Documents the 8-1,200
per-pack range, drops the unverified low-volume compliance claim for facts and
a link to WSDOT's terms, and stops claiming the per-pack defaults fit under
the 4,000 cap: it is filled round-robin.
2026-10-15 15:17:59 -07:00
Sameh Khamis bcfba77196 fix(cctv): make the Lithuania pack opt-in until its licence is confirmed
The camera endpoints are the eismoinfo.lt website backend, which the Via
Lietuva open-data statement does not list, so the pack now follows the NYC
opt-in pattern (CCTV_LITHUANIA_ENABLED=1) and the docs say why. A failed
catalog request now aborts its sibling instead of leaving it open until the
timeout, and a redirect is logged as not followed.
2026-10-15 15:17:31 -07:00
Sameh Khamis 4887162d76 fix(cctv): harden the King County catalog fetch and dedupe before the cap
The catalog fetch now refuses redirects, cancels rejected bodies and reads
through the shared capped JSON reader. Duplicate ids are dropped before
prioritization so they cannot use up the per-pack cap. Vendor-generic model
strings (PTZ, PTZ IP, BOSCH/PTZ) are treated as no model. Comments and credits
say why WSDOT rows are skipped (their frames are WSDOT-hosted, outside the
pinned origin) and name Newcastle as the partner city that appears.
2026-10-15 15:17:11 -07:00
Sameh Khamis 7b1032bfc7 docs(cctv): document the Toronto pack and the shared camera cap
Adds the Toronto settings to .env.example and a current-state section for the
pack, restores an approximate camera count in the README, and corrects the
claim that adding a pack never trims another region: with the shared
4,000-camera cap, the largest packs give way round-robin. Fixes two stale
comments about the list's refresh and the ground-height sidecar.
2026-10-15 15:15:49 -07:00
Sameh Khamis 7561a5ab82 Merge branch 'main' into feat/cctv-toronto 2026-10-15 14:23:19 -07:00
Sameh Khamis 24f14a77a3 Merge branch 'main' into feat/wsdot-camera-pack
Keeps main's 4,000 catalog-wide camera cap, which main pins in tests; WSDOT
shares it round-robin with the other packs. The branch's earlier cap raise and
its removal of main's cap tests are reverted in this merge.
2026-10-15 14:22:22 -07:00
Sameh Khamis dca24996d5 Merge branch 'main' into feat/king-county-camera-pack 2026-10-15 14:21:10 -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 79b9de07d8 Merge pull request #208 from SM-26/docker-support
feat: add a Docker install path
2026-10-15 14:10:36 -07:00
Sameh Khamis da0934e2af Merge branch 'main' into docker-support 2026-10-15 13:59:20 -07:00
Sameh Khamis a5a361f79e Merge pull request #568 from rileygramlich/feat/aqhi
feat(air-quality): add the Air quality layer to the Weather group
2026-10-15 13:59:17 -07:00
Sameh Khamis ac13acfe3d Merge branch 'main' into feat/aqhi
# Conflicts:
#	CHANGELOG.md
2026-10-15 13:47:21 -07:00
Sameh Khamis c6f92da962 Merge pull request #641 from cliph/feat/bikeshare-toronto-and-failure-fix
feat(bikeshare): add Bike Share Toronto, and fix the layer-wide failure it exposes
2026-10-15 13:47:08 -07:00
Sameh Khamis 48ee073469 Merge branch 'main' into feat/bikeshare-toronto-and-failure-fix 2026-10-15 13:34:50 -07:00
Sameh Khamis 1710c83463 Merge pull request #581 from Mikejr314159/fix/cap-remaining-upstream-reads
fix(providers): bound the remaining upstream body reads
2026-10-15 13:34:45 -07:00
Sameh Khamis 129174c3ab fix(air-quality): show the Weather card, add voice support and bound the feed
The card never appeared: the Weather panel only passed five fixed ids and only
rendered lists and categorical legends for cyclones. Air quality now reaches
the panel, the legend style comes from the layer's descriptor, and any layer
with list items gets its list. Adds the voice manifest entry main now requires,
reads ECCC through the shared capped JSON reader with a 15 s deadline and the
row limit enforced, reports stale when every reading was too old, adds source
tests, links the ECCC licence and moves the changelog entry into Unreleased.
2026-10-15 13:13:17 -07:00
Sameh Khamis 51e32a30f2 fix(bikeshare): scope city failures to range and report partial outages
A failure now ends when its city leaves range or the camera rises above the
activation gate, so an empty map no longer reads LOAD FAILED. Late activation
and status rejections are ignored like late successes. Partial failure reads
DEGRADED, every in-range city failing over cached points reads STALE, and
freshness advances only on a refresh that succeeded. Behavioural tests drive
the real proximity, update and enable paths. Docs say no licence is published
for Toronto and list Social Bicycles and PBSC. The merge also restored the #61
GBFS v3 changelog entry that an earlier merge on main dropped.
2026-10-15 13:08:45 -07:00
Sameh Khamis a02f729191 fix(opensky): cool down before reading a 429, and test the six caps
A 429 now starts the credit cooldown from its headers and drops the body
unread, so a stalled or oversized 429 cannot skip the governor; an oversized
snapshot is not retried. A 429 without retry-after now waits the intended
2 minutes (headers.get() returns null, which Number() read as 0 s). Adds
oversized-stream tests for OpenSky, adsb.lol, CelesTrak and TomTom that fail
on main, names the six reads in the changelog, and corrects the docs on
what was already bounded.
2026-10-15 13:08:37 -07:00
Sameh Khamis 3f6fe69c57 build(docker): keep keys out of the image and run as non-root by default
The build context excluded only .env, so .env.local and the like or
pinokio/ENVIRONMENT would be baked into image layers; exclude every dotenv
file except .env.example, plus local state and QA output. Pin the base to
CI's Node 24.14.0 by digest, default to the image's node user, and correct
the HOST comment. Log the trusted peers as the operator's when set. README
notes that MCP and ChatGPT sign-in voice are unavailable under Docker, and
how to trust no gateway when widening the publish.
2026-10-15 13:07:00 -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 c9f43e8517 Merge branch 'main' into feat/bikeshare-toronto-and-failure-fix
# Conflicts:
#	CHANGELOG.md
#	DATA_SOURCES.md
#	docs/CURRENT-STATE.md
2026-10-15 12:35:18 -07:00
Sameh Khamis a4e2325ded Merge branch 'main' into docker-support 2026-10-15 12:34:50 -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