NDW traffic: is it worth wiring in? #186

Closed
opened 2026-08-29 18:54:41 +00:00 by viberfox-agent · 14 comments
Collaborator

Problem

docs/direction.md:82 lists 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.

File gzipped uncompressed contents
trafficspeed.xml.gz 1,039,734 B 52,518,424 B 20,519 site measurements, flow + average speed, restamped every minute
measurement_current.xml.gz (site table) 11,165,196 B 388,573,075 B 101,225 site records, refreshed ~weekly
traveltime.xml.gz 2,684,268 B 73,610,258 B 80,756 sites, TravelTimeData only
actueel_beeld.xml.gz 313,927 B 3,020,799 B DATEX II v3 situations (incidents, obstructions)

The site table splits 20,519 Point records (loop detectors, lus) and 80,706 ItineraryByIndexedLocations (stretches, mostly fcd). The reporting set in trafficspeed.xml.gz is exactly the 20,519 Point records — 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 locationForDisplay coordinates:

Point ≤260 m (PLACE_RADIUS_M) ≤390 m ≤1 km
Groningen, Grote Markt (53.2194, 6.5665) 0 4 20
Amsterdam, Dam 0 0 2 (both inside the Michiel de Ruijtertunnel)
Utrecht, Domplein 0 0 0
A2 south of Utrecht (52.0578, 5.0966) 0 2 30

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 Point record locates itself with locationForDisplay lat/lng, an alertCPoint (AlertC/TMC table 6.13 code plus an offset) and an openlrExtendedPoint. No NWB wvk_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

  • Value 4, "add, don't replace." The row's own wording is "in place of invented ambient traffic". Substituting measurement for the generated cars empties every street with no loop on it, which is nearly every street in every city centre measured above.
  • Value 6, "ship the smallest thing a machine can judge." With zero reporting loops inside the placement radius at three of the four points, there is no count, placement or settle number a --dump-state assertion could hold.
  • Value 1, "measured beats plausible", is the case for it, and it loses on coverage rather than on principle. Say so in the note.

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.

  1. New note docs/notes/ndw-traffic.md, in the format docs/notes/README.md specifies (topic: / triggers: / updated: front-matter). Follow docs/notes/nwb-road-network.md as 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 Point set 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.

  2. docs/direction.md — remove the NDW traffic row 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.

  3. 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.

  4. Re-measure before dating the note. The commands under Verification are all read-only curl; trafficspeed.xml.gz is 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 its updated: date agree.

Acceptance criteria

  • docs/notes/ndw-traffic.md exists with topic:, triggers: and updated: front-matter per docs/notes/README.md. triggers: includes at least NDW, trafficspeed, traveltime, measurement site table, DATEX, AlertC, OpenLR, vehicle counts, ambient traffic, opendata.ndw.nu.
  • The note records, each as a measured number with the date it was measured: the gzipped and uncompressed size of trafficspeed.xml.gz, measurement_current.xml.gz, traveltime.xml.gz; the count of reporting sites; the Point vs ItineraryByIndexedLocations split of the site table; and the finding that the reporting set is exactly the Point set.
  • The note states that opendata.ndw.nu offers no bbox or area query, so the per-cell cost is the whole national file.
  • The note carries the nearest-reporting-loop table for at least Groningen Grote Markt, Amsterdam Dam, Utrecht Domplein and one motorway point, at the 260 m / 390 m / 1 km radii, and names PLACE_RADIUS_M (crates/cartopolis/src/systems/nav/traffic.rs:77) as the reason 260 m is the radius that matters.
  • The note records the licence as CC0 with both pieces of evidence (https://www.ndw.nu/copyright and the feed's own confidentiality: noRestriction), and says explicitly that the licence was not the obstacle.
  • The note records that a Point record carries only locationForDisplay, alertCPoint and openlrExtendedPoint — no NWB wvk_id and no OSM id — and links docs/notes/nwb-road-network.md for why an NWB-keyed join is not available.
  • The note has a "pointers, with what was and was not verified" section covering at least: traveltime.xml.gz (verified: 80,756 sites, TravelTimeData with a static reference value, i.e. a congestion ratio and no vehicle counts, and city coverage far better than the loops — 662 fcd against 439 lus in 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), and actueel_beeld.xml.gz (verified: DATEX II v3 situations with plain pointByCoordinates lat/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 that mijn.ndw.nu account-gated APIs (OTM, Voertuigpassage) were not checked, and why that does not change the answer: the content behind them is the same loop network.
  • The note names the value from docs/direction.md that settled it and why value 1 ("measured beats plausible") lost to it here.
  • The note ends with a "how every number above was measured" block of runnable commands, as docs/notes/nwb-road-network.md does.
  • docs/direction.md no 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.md links the new note.
  • git diff --stat against main shows changes under docs/ only — no .rs, no Cargo.toml, no tools/.
  • The commit body says which value was applied and why, per the ticket's instruction.

Verification

No build, no test, no shot — nothing in this ticket compiles. Everything below is read-only HTTP.

# Interface: a static directory, no bbox parameter anywhere.
curl -sL https://opendata.ndw.nu/ | grep -o 'href="[^"]*\.\(gz\|zip\|xml\)"'

# Licence.
curl -sL https://www.ndw.nu/copyright | sed 's/<[^>]*>//g' | grep -i "creative commons"

# Volumes (Content-Length; trafficspeed is restamped every minute).
for f in trafficspeed.xml.gz measurement_current.xml.gz traveltime.xml.gz actueel_beeld.xml.gz; do
  printf '%-32s ' "$f"; curl -sI "https://opendata.ndw.nu/$f" | tr -d '\r' \
    | awk '/[Cc]ontent-[Ll]ength/{l=$2} /[Ll]ast-[Mm]odified/{$1="";m=$0} END{print l, m}'
done

# Reporting sites, and the uncompressed size of one minute of it.
curl -sL https://opendata.ndw.nu/trafficspeed.xml.gz -o ts.xml.gz
gzip -dc ts.xml.gz | wc -c
gzip -dc ts.xml.gz | grep -c '<siteMeasurements'

# Site table: 388 MB of XML. Split on the token `<measurementSiteRecord id="` —
# splitting on `<measurementSiteRecord` alone also cuts at the nested
# `<measurementSiteRecordVersionTime>` and silently halves every record.
curl -sL https://opendata.ndw.nu/measurement_current.xml.gz -o mst.xml.gz
# then a streaming scan pulling per record: id, <locationForDisplay> lat/lng,
# <measurementSiteLocation xsi:type>, <measurementEquipmentTypeUsed>.
# Cross-reference the ids against the <measurementSiteReference id="…"> set in
# trafficspeed to get the reporting subset, then measure great-circle distance
# from each city point.

# Travel-time feed: itinerary sites, TravelTimeData only, no flow.
curl -sL https://opendata.ndw.nu/traveltime.xml.gz | gzip -dc \
  | grep -o 'xsi:type="[A-Za-z]*"' | sort | uniq -c | sort -rn | head

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

  • Any change to systems::traffic or cartopolis_geo::traffic. The generated cars stay exactly as they are; this ticket does not tune, gate or re-weight them.
  • An incidents / roadworks layer off 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.
  • Congestion colouring from traveltime.xml.gz. Same: recorded as a pointer, not built.
  • An extractor in tools/ or in cartopy. Nothing is being wired, so there is no pipeline stage, no coverage manifest and no streamer.
  • The account-gated NDW APIs. Not checked, recorded as not checked.
  • Re-opening 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-wired

Original 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:

  • Is there an interface a client or an extractor can use, and what does it cost per cell?
  • Does the licence allow redistributing what we would cache?
  • Does it change the picture, or only the data behind it?

Not worth it is a valid answer. Record it in docs/direction.md against this row
with 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.md
exist 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 what
is 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.

## Problem `docs/direction.md:82` lists **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. | File | gzipped | uncompressed | contents | |---|---|---|---| | `trafficspeed.xml.gz` | 1,039,734 B | 52,518,424 B | 20,519 site measurements, flow + average speed, restamped every minute | | `measurement_current.xml.gz` (site table) | 11,165,196 B | 388,573,075 B | 101,225 site records, refreshed ~weekly | | `traveltime.xml.gz` | 2,684,268 B | 73,610,258 B | 80,756 sites, `TravelTimeData` only | | `actueel_beeld.xml.gz` | 313,927 B | 3,020,799 B | DATEX II v3 situations (incidents, obstructions) | The site table splits **20,519 `Point`** records (loop detectors, `lus`) and **80,706 `ItineraryByIndexedLocations`** (stretches, mostly `fcd`). The reporting set in `trafficspeed.xml.gz` is *exactly* the 20,519 `Point` records — 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 `locationForDisplay` coordinates: | Point | ≤260 m (`PLACE_RADIUS_M`) | ≤390 m | ≤1 km | |---|---|---|---| | Groningen, Grote Markt (53.2194, 6.5665) | **0** | 4 | 20 | | Amsterdam, Dam | **0** | **0** | 2 (both inside the Michiel de Ruijtertunnel) | | Utrecht, Domplein | **0** | **0** | **0** | | A2 south of Utrecht (52.0578, 5.0966) | 0 | 2 | 30 | 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 `Point` record locates itself with `locationForDisplay` lat/lng, an `alertCPoint` (AlertC/TMC table 6.13 code plus an offset) and an `openlrExtendedPoint`. **No NWB `wvk_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 - **Value 4, "add, don't replace."** The row's own wording is "*in place of* invented ambient traffic". Substituting measurement for the generated cars empties every street with no loop on it, which is nearly every street in every city centre measured above. - **Value 6, "ship the smallest thing a machine can judge."** With zero reporting loops inside the placement radius at three of the four points, there is no count, placement or settle number a `--dump-state` assertion could hold. - Value 1, "measured beats plausible", is the case *for* it, and it loses on coverage rather than on principle. Say so in the note. 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. 1. **New note `docs/notes/ndw-traffic.md`**, in the format `docs/notes/README.md` specifies (`topic:` / `triggers:` / `updated:` front-matter). Follow `docs/notes/nwb-road-network.md` as 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 `Point` set 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. 2. **`docs/direction.md`** — remove the `NDW traffic` row 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. 3. **`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. 4. **Re-measure before dating the note.** The commands under Verification are all read-only `curl`; `trafficspeed.xml.gz` is 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 its `updated:` date agree. ## Acceptance criteria - [ ] `docs/notes/ndw-traffic.md` exists with `topic:`, `triggers:` and `updated:` front-matter per `docs/notes/README.md`. `triggers:` includes at least `NDW`, `trafficspeed`, `traveltime`, `measurement site table`, `DATEX`, `AlertC`, `OpenLR`, `vehicle counts`, `ambient traffic`, `opendata.ndw.nu`. - [ ] The note records, each as a measured number with the date it was measured: the gzipped and uncompressed size of `trafficspeed.xml.gz`, `measurement_current.xml.gz`, `traveltime.xml.gz`; the count of reporting sites; the `Point` vs `ItineraryByIndexedLocations` split of the site table; and the finding that the reporting set is exactly the `Point` set. - [ ] The note states that `opendata.ndw.nu` offers no bbox or area query, so the per-cell cost is the whole national file. - [ ] The note carries the nearest-reporting-loop table for at least Groningen Grote Markt, Amsterdam Dam, Utrecht Domplein and one motorway point, at the 260 m / 390 m / 1 km radii, and names `PLACE_RADIUS_M` (`crates/cartopolis/src/systems/nav/traffic.rs:77`) as the reason 260 m is the radius that matters. - [ ] The note records the licence as CC0 with both pieces of evidence (`https://www.ndw.nu/copyright` and the feed's own `confidentiality: noRestriction`), and says explicitly that the licence was not the obstacle. - [ ] The note records that a `Point` record carries only `locationForDisplay`, `alertCPoint` and `openlrExtendedPoint` — no NWB `wvk_id` and no OSM id — and links `docs/notes/nwb-road-network.md` for why an NWB-keyed join is not available. - [ ] The note has a "pointers, with what was and was not verified" section covering at least: `traveltime.xml.gz` (verified: 80,756 sites, `TravelTimeData` with a static reference value, i.e. a congestion ratio and **no vehicle counts**, and city coverage far better than the loops — 662 `fcd` against 439 `lus` in 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), and `actueel_beeld.xml.gz` (verified: DATEX II v3 situations with plain `pointByCoordinates` lat/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 that `mijn.ndw.nu` account-gated APIs (OTM, Voertuigpassage) were **not** checked, and why that does not change the answer: the content behind them is the same loop network. - [ ] The note names the value from `docs/direction.md` that settled it and why value 1 ("measured beats plausible") lost to it here. - [ ] The note ends with a "how every number above was measured" block of runnable commands, as `docs/notes/nwb-road-network.md` does. - [ ] `docs/direction.md` no 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.md` links the new note. - [ ] `git diff --stat` against `main` shows changes under `docs/` only — no `.rs`, no `Cargo.toml`, no `tools/`. - [ ] The commit body says which value was applied and why, per the ticket's instruction. ## Verification No build, no test, no shot — nothing in this ticket compiles. Everything below is read-only HTTP. ```bash # Interface: a static directory, no bbox parameter anywhere. curl -sL https://opendata.ndw.nu/ | grep -o 'href="[^"]*\.\(gz\|zip\|xml\)"' # Licence. curl -sL https://www.ndw.nu/copyright | sed 's/<[^>]*>//g' | grep -i "creative commons" # Volumes (Content-Length; trafficspeed is restamped every minute). for f in trafficspeed.xml.gz measurement_current.xml.gz traveltime.xml.gz actueel_beeld.xml.gz; do printf '%-32s ' "$f"; curl -sI "https://opendata.ndw.nu/$f" | tr -d '\r' \ | awk '/[Cc]ontent-[Ll]ength/{l=$2} /[Ll]ast-[Mm]odified/{$1="";m=$0} END{print l, m}' done # Reporting sites, and the uncompressed size of one minute of it. curl -sL https://opendata.ndw.nu/trafficspeed.xml.gz -o ts.xml.gz gzip -dc ts.xml.gz | wc -c gzip -dc ts.xml.gz | grep -c '<siteMeasurements' # Site table: 388 MB of XML. Split on the token `<measurementSiteRecord id="` — # splitting on `<measurementSiteRecord` alone also cuts at the nested # `<measurementSiteRecordVersionTime>` and silently halves every record. curl -sL https://opendata.ndw.nu/measurement_current.xml.gz -o mst.xml.gz # then a streaming scan pulling per record: id, <locationForDisplay> lat/lng, # <measurementSiteLocation xsi:type>, <measurementEquipmentTypeUsed>. # Cross-reference the ids against the <measurementSiteReference id="…"> set in # trafficspeed to get the reporting subset, then measure great-circle distance # from each city point. # Travel-time feed: itinerary sites, TravelTimeData only, no flow. curl -sL https://opendata.ndw.nu/traveltime.xml.gz | gzip -dc \ | grep -o 'xsi:type="[A-Za-z]*"' | sort | uniq -c | sort -rn | head ``` 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 - **Any change to `systems::traffic` or `cartopolis_geo::traffic`.** The generated cars stay exactly as they are; this ticket does not tune, gate or re-weight them. - **An incidents / roadworks layer** off `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. - **Congestion colouring from `traveltime.xml.gz`.** Same: recorded as a pointer, not built. - **An extractor in `tools/` or in cartopy.** Nothing is being wired, so there is no pipeline stage, no coverage manifest and no streamer. - **The account-gated NDW APIs.** Not checked, recorded as not checked. - **Re-opening `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-wired` <details><summary>Original request</summary> From the frontier in [`docs/direction.md`](../src/branch/main/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: - Is there an interface a client or an extractor can use, and what does it cost per cell? - Does the licence allow redistributing what we would cache? - Does it change the picture, or only the data behind it? **Not worth it is a valid answer.** Record it in `docs/direction.md` against this row with 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.md` exist 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 what is drawn rather than replacing it, and no unbounded per-frame upload. <sub>Filed by the autopilot. <!-- autopilot:ndw-traffic --></sub> </details> <sub>🤖 Refined by the viberfox issue agent. Reply with **@agent refine** and what is wrong to have this rewritten.</sub>
Author
Collaborator

🤖 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.

🤖 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.
Author
Collaborator

🤖 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:skip stops me.


🤖 viberfox issue agent on the VPS — 36 turns · $3.18 · session a4a9be91-3be4-4e9a-ba86-0529f8623cd7. Reply with @agent to continue this session.

🤖 **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:skip` stops me. --- <sub>🤖 viberfox issue agent on the VPS — 36 turns · $3.18 · session `a4a9be91-3be4-4e9a-ba86-0529f8623cd7`. Reply with **@agent** to continue this session.</sub>
Author
Collaborator

🤖 Picked this up — working on branch docs/186-ndw-traffic-not-wired in a fresh worktree on the VPS. I'll report back here when I'm done.

Label an issue agent:skip to keep me off it, or driven to keep it in a session you are driving.

🤖 Picked this up — working on branch `docs/186-ndw-traffic-not-wired` in a fresh worktree on the VPS. I'll report back here when I'm done. <sub>Label an issue `agent:skip` to keep me off it, or `driven` to keep it in a session you are driving.</sub>
Author
Collaborator

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.nu at 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 (no wvk_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

  • Added 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.
  • Changed docs/direction.md — moved the NDW traffic row from "Candidates, unverified" to "Checked and not taken".
  • Changed 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 --stat is docs/ only. No crate is touched, so there is nothing to cargo check or test; cargo fmt --check is clean on this tree. No visual check is needed anywhere — nothing renders.

Branch docs/186-ndw-traffic-not-wired is 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)

5d1f8e7 docs(direction): NDW traffic checked and not taken

🤖 viberfox issue agent on the VPS — 36 turns · $3.36 · session 39d4818d-d886-4e5e-86e4-570739a9a507. Reply with @agent to continue this session.

**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.nu` at 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 (no `wvk_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 - **Added** `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. - **Changed** `docs/direction.md` — moved the NDW traffic row from "Candidates, unverified" to "Checked and not taken". - **Changed** `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 --stat` is `docs/` only. No crate is touched, so there is nothing to `cargo check` or test; `cargo fmt --check` is clean on this tree. No visual check is needed anywhere — nothing renders. Branch `docs/186-ndw-traffic-not-wired` is 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) ``` 5d1f8e7 docs(direction): NDW traffic checked and not taken ``` --- <sub>🤖 viberfox issue agent on the VPS — 36 turns · $3.36 · session `39d4818d-d886-4e5e-86e4-570739a9a507`. Reply with **@agent** to continue this session.</sub>
Author
Collaborator

🤖 Marked agent:done without a CI verdict on 5d1f8e7f — no workflow reported on this commit. The branch is waiting for review; check the run yourself before landing it.

🤖 Marked `agent:done` **without a CI verdict** on `5d1f8e7f` — no workflow reported on this commit. The branch is waiting for review; check the run yourself before landing it.
Author
Collaborator

🤖 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.
Author
Collaborator

🤖 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.
Author
Collaborator

🤖 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.
Author
Collaborator

🤖 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.
Author
Collaborator

🤖 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.
Author
Collaborator

🤖 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.
Owner

🤖 Parked by hand: pull request #190 has a real merge conflict with main (docs/direction.md and 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; ship removed until then.

🤖 Parked by hand: pull request #190 has a real merge conflict with `main` (`docs/direction.md` and 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; `ship` removed until then.
viberfox-agent 2026-08-29 20:51:12 +00:00
Author
Collaborator

🤖 Merged into main as pull request #190 (24f21692).

🤖 **Merged into `main`** as pull request #190 (`24f21692`).
Author
Collaborator

🤖 Merged, but not deployed. The deploy step exited 1:

main is at 101525c2
  waiting for CI on 101525c2
refusing: no Actions job ran for 101525c2 — nothing has verified this commit

main has the change; the public services are still on the previous build.

🤖 **Merged, but not deployed.** The deploy step exited 1: ``` main is at 101525c2 waiting for CI on 101525c2 refusing: no Actions job ran for 101525c2 — nothing has verified this commit ``` `main` has the change; the public services are still on the previous build.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jeroen/cartopolis#186
No description provided.