NDW traffic: is it worth wiring in? #186
Labels
No labels
agent
agent:ci
agent:done
agent:failed
agent:needs-input
agent:refined
agent:refining
agent:running
agent:shipped
agent:skip
autonomous
autopilot
driven
local
plan
proposal
qa
qa-gap
research
retro
ship
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jeroen/cartopolis#186
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
docs/direction.md:82lists NDW traffic as a candidate that would give "real vehicle counts in place of invented ambient traffic". Nobody had confirmed the interface, the licence or the volume. This ticket is the check, and the check has now been done — read-only, from this container, on 2026-08-29.The answer is no, and this ticket is a documentation change recording why.
What the ambient traffic layer actually needs
Cars are placed on drivable centrelines within 260 m of the camera (
crates/cartopolis/src/systems/nav/traffic.rs:77), recycled at 390 m (:78), 180 of them (:69), and hidden above 700 m altitude (:88). Their speed is the road's class constant,cartopolis_geo::traffic::drive_speed(crates/geo/src/traffic.rs:108), and placement is weighted by that same speed as a stand-in for road importance (traffic.rs:661-669). So the question NDW has to answer is: is there a measurement on the street 200 m in front of the camera?What NDW carries
https://opendata.ndw.nu/is a plain directory of national bulk files. There is no area or bbox query anywhere on it — every consumer downloads the whole country.trafficspeed.xml.gzmeasurement_current.xml.gz(site table)traveltime.xml.gzTravelTimeDataonlyactueel_beeld.xml.gzThe site table splits 20,519
Pointrecords (loop detectors,lus) and 80,706ItineraryByIndexedLocations(stretches, mostlyfcd). The reporting set intrafficspeed.xml.gzis exactly the 20,519Pointrecords — cross-referenced by id, no others report. So "vehicle counts" means the loops and nothing else.The coverage is absent where the layer runs
Distance from a city centre to the nearest reporting loop, measured against the site table's own
locationForDisplaycoordinates:PLACE_RADIUS_M)Over the Groningen centre box the NWB note used (
bbox=6.555,53.207,6.580,53.222, ~1.7 × 1.7 km): 97 site records, of which 22 report.The loops are dense on motorway mainlines and near-absent on city streets — which is the inverse of where the pool puts cars, and the motorway case is the one the existing speed weighting already prefers.
The join has no key either
A
Pointrecord locates itself withlocationForDisplaylat/lng, analertCPoint(AlertC/TMC table 6.13 code plus an offset) and anopenlrExtendedPoint. No NWBwvk_id, no OSM id. Attaching a loop to a drawn Shortbread centreline is a nearest-line-plus-heading match or an OpenLR decode against a road network this client does not hold — NWB was rejected yesterday (docs/notes/nwb-road-network.md).The licence is not the obstacle
https://www.ndw.nu/copyright: CC0 unless stated otherwise, images excepted. The feeds also carry<headerInformation><confidentiality>noRestriction</confidentiality>in the payload itself. Same shape as NWB — rejected on content, not on terms.Which value settles it
--dump-stateassertion could hold.There is also a cost the surveyed layers do not have: with no area query, a per-cell NDW service means re-slicing a national 52 MB XML every minute. Every surveyed layer is a static per-cell file built once by cartopy's pipeline (
docs/notes/where-the-map-data-is-built.md), and the one live feed that exists, OVapi transit, is polled by the client directly because it answers a batched query by stop code (crates/cartopolis/src/systems/nav/transit_live.rs:5-18, 30 s cadence). NDW offers no equivalent query, so nothing in this project's shape fits it.Approach
Documentation only. No crate is touched.
New note
docs/notes/ndw-traffic.md, in the formatdocs/notes/README.mdspecifies (topic:/triggers:/updated:front-matter). Followdocs/notes/nwb-road-network.mdas the model — it is the sibling decision from the same frontier table and has the right sections: what it actually carries, why the gap it would fill is already closed, what it would cost, the pointers with what was and was not verified, and a "how every number above was measured" block with the literal commands.Content to carry: the file inventory and volumes; the 20,519 / 80,706 site-table split and the fact that only the
Pointset reports; the coverage table above; the AlertC/OpenLR-only location referencing; the CC0 finding; the per-minute national re-slice cost; and the value that settled it.docs/direction.md— remove theNDW trafficrow from the Candidates, unverified table (:82) and add a row to Checked and not taken (:91-93), matching the NWB row's shape: source, date checked, one-paragraph reason, link to the note.docs/notes/ambient-traffic.md— one sentence plus a link in the sibling-notes line near the top (docs/notes/ambient-traffic.md:20-23), saying the real counts were checked and are not coming. That file is where the next person asks "why aren't these real cars?", and it currently says only that the traffic is invented.Re-measure before dating the note. The commands under Verification are all read-only
curl;trafficspeed.xml.gzis restamped every minute so its byte count will differ from the figures above, and the note should say the size is a per-minute figure rather than a constant. The coverage table needs the ~2-minute site-table scan re-run so the note's numbers and itsupdated:date agree.Acceptance criteria
docs/notes/ndw-traffic.mdexists withtopic:,triggers:andupdated:front-matter perdocs/notes/README.md.triggers:includes at leastNDW,trafficspeed,traveltime,measurement site table,DATEX,AlertC,OpenLR,vehicle counts,ambient traffic,opendata.ndw.nu.trafficspeed.xml.gz,measurement_current.xml.gz,traveltime.xml.gz; the count of reporting sites; thePointvsItineraryByIndexedLocationssplit of the site table; and the finding that the reporting set is exactly thePointset.opendata.ndw.nuoffers no bbox or area query, so the per-cell cost is the whole national file.PLACE_RADIUS_M(crates/cartopolis/src/systems/nav/traffic.rs:77) as the reason 260 m is the radius that matters.https://www.ndw.nu/copyrightand the feed's ownconfidentiality: noRestriction), and says explicitly that the licence was not the obstacle.Pointrecord carries onlylocationForDisplay,alertCPointandopenlrExtendedPoint— no NWBwvk_idand no OSM id — and linksdocs/notes/nwb-road-network.mdfor why an NWB-keyed join is not available.traveltime.xml.gz(verified: 80,756 sites,TravelTimeDatawith a static reference value, i.e. a congestion ratio and no vehicle counts, and city coverage far better than the loops — 662fcdagainst 439lusin the Groningen city box; not verified: whether the AlertC/OpenLR itineraries can be placed on a drawn street at acceptable cost, and what a VILD location-table decode would take), andactueel_beeld.xml.gz(verified: DATEX II v3 situations with plainpointByCoordinateslat/lng, so directly placeable with no location table; not verified: volume over a city, refresh cadence, or whether an incidents layer is wanted at all — it is a different frontier row, not this one). Note thatmijn.ndw.nuaccount-gated APIs (OTM, Voertuigpassage) were not checked, and why that does not change the answer: the content behind them is the same loop network.docs/direction.mdthat settled it and why value 1 ("measured beats plausible") lost to it here.docs/notes/nwb-road-network.mddoes.docs/direction.mdno longer lists NDW traffic under Candidates, unverified, and lists it under Checked and not taken with the date and a link to the note.docs/notes/ambient-traffic.mdlinks the new note.git diff --statagainstmainshows changes underdocs/only — no.rs, noCargo.toml, notools/.Verification
No build, no test, no shot — nothing in this ticket compiles. Everything below is read-only HTTP.
Watch the disk: the container was at 98 % on
/when this was refined, and the three feeds expand to ~515 MB together. Stream them or delete as you go.The only workstation-side check is that the two edited docs render — there is nothing to run, and no capture is involved.
Out of scope
systems::trafficorcartopolis_geo::traffic. The generated cars stay exactly as they are; this ticket does not tune, gate or re-weight them.actueel_beeld.xml.gz. It is placeable and cheap, and it is a different feature from vehicle counts — if it is wanted it is a new frontier row and a new ticket, recorded as a pointer in the note and not built here.traveltime.xml.gz. Same: recorded as a pointer, not built.tools/or in cartopy. Nothing is being wired, so there is no pipeline stage, no coverage manifest and no streamer.docs/notes/nwb-road-network.md. Link it; do not restate it.Open questions
None. The interface, the licence and the volume were all confirmed from public endpoints, and
docs/direction.md's values decide the rest.Branch:
docs/186-ndw-traffic-not-wiredOriginal request
From the frontier in
docs/direction.md: NDW traffic — real vehicle counts in place of invented ambient traffic.The first half of this ticket is deciding whether it is worth doing at all:
Not worth it is a valid answer. Record it in
docs/direction.mdagainst this rowwith the reason, and close this ticket — that is the result, not a failure.
Decide it yourself. This ticket is not being watched, so a question asked
here is a ticket that stops. The seven values at the top of
docs/direction.mdexist to settle exactly this kind of ambiguity — pick the reading they support,
say in the commit body which one you applied and why, and build. Only a decision
that would need something nobody can derive from the repository — a credential, a
licence somebody must agree to, a choice about what the project is for — is a
reason to stop.
If it is worth doing, follow the shape the surveyed layers already use:
an extractor under
tools/, a coverage manifest, a streamer that adds to whatis drawn rather than replacing it, and no unbounded per-frame upload.
Filed by the autopilot.
🤖 Refined by the viberfox issue agent. Reply with @agent refine and what is wrong to have this rewritten.
🤖 Refining this before anything gets built — reading the code, no branch, no commits. If it turns out I'd have to guess at the requirements I'll stop and ask instead.
🤖 Refined. The description above is now the spec; I'll pick it up for implementation on the next poll.
If the direction is wrong, say so now — @agent refine with a correction rewrites it, and
agent:skipstops me.🤖 viberfox issue agent on the VPS — 36 turns · $3.18 · session
a4a9be91-3be4-4e9a-ba86-0529f8623cd7. Reply with @agent to continue this session.🤖 Picked this up — working on branch
docs/186-ndw-traffic-not-wiredin a fresh worktree on the VPS. I'll report back here when I'm done.Label an issue
agent:skipto keep me off it, ordrivento keep it in a session you are driving.The answer is no — NDW's real vehicle counts are not worth wiring in. I checked the feeds from this container on 2026-08-29 and wrote the check down. Nothing compiles: the change is three files under
docs/.Why not
The ambient cars are placed within 260 metres of the camera. NDW's counts come from 20,519 loop detectors, and the number of them inside that radius is 0 at Groningen's Grote Markt, 0 at the Dam, 0 at Domplein and 2 at Rotterdam's Coolsingel. The loops sit on motorway mainlines. Swapping the generated cars for measurement would empty nearly every city street — that is value 4 in
docs/direction.md, "add, don't replace", and it is what I applied. Value 1, "measured beats plausible", is the argument in favour and loses on coverage; the note says so explicitly.Three supporting findings: there is no area query on
opendata.ndw.nuat all, so a per-cell service would mean re-slicing a 52 MB national file every minute; a loop record carries no road identifier to join on (nowvk_id, no OpenStreetMap id anywhere in the 388 MB site table); and the licence is Creative Commons Zero, so it was never the obstacle.Two things beyond what the ticket assumed, both measured here: only 1,833 of the 20,519 loops (8.9%) carry an OpenLR reference, so even an OpenLR decode would not reach most of them; and Rotterdam is a genuine counterexample, which I recorded rather than dropped.
What changed
docs/notes/ndw-traffic.md— the file sizes, the site-table split, the coverage table, the location-referencing fields, the licence evidence, the two pointers (travel times and incidents), and the commands to re-run every number.docs/direction.md— moved the NDW traffic row from "Candidates, unverified" to "Checked and not taken".docs/notes/ambient-traffic.md— four lines saying the real counts were checked and are not coming, with a link.Verification
Every number in the note is one I measured today, not one copied from the ticket.
git diff --statisdocs/only. No crate is touched, so there is nothing tocargo checkor test;cargo fmt --checkis clean on this tree. No visual check is needed anywhere — nothing renders.Branch
docs/186-ndw-traffic-not-wiredis pushed.For you to decide
The incidents feed (
actueel_beeld.xml.gz) is cheap and directly placeable — 391 roadworks and obstructions with plain latitude/longitude, 3.1 MB, no location table needed. It is a different feature from vehicle counts, so I left it out. If you want it, it wants its own frontier row and its own ticket.Branch
docs/186-ndw-traffic-not-wired(pushed)🤖 viberfox issue agent on the VPS — 36 turns · $3.36 · session
39d4818d-d886-4e5e-86e4-570739a9a507. Reply with @agent to continue this session.🤖 Marked
agent:donewithout a CI verdict on5d1f8e7f— no workflow reported on this commit. The branch is waiting for review; check the run yourself before landing it.🤖 Could not ship this. Forgejo never reported the pull request as mergeable
The branch is intact; nothing was merged.
🤖 Could not ship this. Forgejo never reported the pull request as mergeable
The branch is intact; nothing was merged.
🤖 Could not ship this. Forgejo never reported the pull request as mergeable
The branch is intact; nothing was merged.
🤖 Could not ship this. Forgejo never reported the pull request as mergeable
The branch is intact; nothing was merged.
🤖 Could not ship this. Forgejo never reported the pull request as mergeable
The branch is intact; nothing was merged.
🤖 Could not ship this. Forgejo never reported the pull request as mergeable
The branch is intact; nothing was merged.
🤖 Parked by hand: pull request #190 has a real merge conflict with
main(docs/direction.mdand the frontier notes moved under it — #188/#189 landed first). The autopilot cannot tell a conflict from Forgejo still computing mergeability, so it was retrying and posting this failure every 15 minutes. The branch needs a rebase before it can land;shipremoved until then.🤖 Merged into
mainas pull request #190 (24f21692).🤖 Merged, but not deployed. The deploy step exited 1:
mainhas the change; the public services are still on the previous build.agent:donenever means "CI agreed" #220docs/qa/targets.md— 14 of 14 passes re-verified a shipped ticket, 9 of them documentation-only verdicts #280