CBS neighbourhood statistics: is it worth wiring in? #192
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jeroen/cartopolis#192
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:83lists CBS neighbourhood statistics — "how many people are actually there" under Candidates, unverified. Nobody had confirmed the interface, the licence or the volume. This pass did that, read-only, from the container on 2026-08-29.The answer is no, on the same grounds Kadaster parcels and NWB lost. The deliverable is therefore a
docs/notes/note plus a row moved indocs/direction.md— not a layer.What was checked
https://service.pdok.nl/cbs/wijkenbuurten/2024/wfs/v1_0(WFS 2.0), three feature types:buurten,wijken,gemeenten. A 2023 service exists at the same shape.ows:AccessConstraintsishttps://creativecommons.org/publicdomain/zero/1.0/deed.nl,ows:Feesisnone. The licence was never the obstacle.bbox=6.555,53.207,6.580,53.222(the ~1.7 × 1.7 km Groningen box the Kadaster and NWB checks used, comparable to a z14 surveyed cell): 23 buurten, 6 wijken, 1 gemeente; the buurten GeoJSON is 257 KB, of which the geometry is 27.6 KB (696 coordinate pairs across 22 features — heavily generalised admin outlines). So ~89% of the payload is the statistics table: 244 attributes per feature, of which 30% (1,614 of 5,368 values) are the suppression sentinel-99997.PagingIsTransactionSafe = FALSE,CountDefault = 1000.resultType=hitssays 23 buurten; page 0 returned 22 andstartIndex=22returnedSterrebosbuurtagain — the last feature of page 0, i.e. a duplicate rather than the missing 23rd. Any extractor built on this would have to de-duplicate on id and reconcile againsthits, exactly asdocs/notes/kadaster-parcels.md:75-79records.Why it loses: there is no picture in it, and every consumer already has a finer source
The only geometry is the buurt/wijk/gemeente outline, which is an administrative line with no physical object standing on it — the precise reason the cadastre was rejected (
docs/notes/kadaster-parcels.md:25-38, value 1). Everything else is a table.docs/notes/kadaster-parcels.md:96-97already ruled out plot area on this ground: "Data behind the picture, not picture; value 6 rules it out."The strongest counter-case is value 1 — ground an invented density in a measured one. It fails on all three candidate consumers:
MAX_CARS = 180(crates/cartopolis/src/systems/nav/traffic.rs:69) placed insidePLACE_RADIUS_M = 260.0(:77), and the constant's own doc comment says it is "Sized for 'the street I am looking at is alive', not for a traffic census" (:59), with the module note stating the question as "is this street alive" (:43). Arithmetic on those two cited constants: 180 cars over π·0.26² km² ≈ 850/km², already below the 1,971personenautosPerKm2measured in Binnenstad-Noord — so a measured number could only ever place fewer cars. And CBS's car figure is a stock of registered vehicles, not a flow: the layer draws moving cars, and the frontier row that carries traffic flow is NDW, one line above this one atdocs/direction.md:82.BLOCK_M = 25.0m (crates/cartopolis/src/systems/map/building_windows.rs:99) — a buurt is one to two orders of magnitude coarser. The occupancy channel it multiplies (assets/shaders/building_windows.wgsl:163-164) is already fed per building by the BAG's use bits throughosm_buildings::night_lit_scale(crates/cartopolis/src/systems/map/osm_buildings.rs:515).percentageOnbewoondis a neighbourhood average of the same fact the client already reads per object; value 1 prefers the finer measured source.MAX_WALKERS = 220(crates/cartopolis/src/systems/nav/traffic.rs:508). CBS measures where people live, not who is on the pavement — the shops, the station and the university are. Turning a residence count into a street-presence count is an inference wearing a measurement's clothes, which is the one thing value 1 exists to stop.Parked cars are already derived from the lot's own surveyed area (
crates/geo/src/parking.rs:11-12), which is finer than a buurt aggregate.The cheaper thing nobody has read
The frontier row asks "how many people are actually there". The vector tile already carries a population figure and nothing reads it:
place_labels, 21 features on the Groningen z14 fixture, propertieskind/name/population, listed as used for nothing indocs/notes/vector-tile-unused-layers.md, and the onlyplace_labelsmention anywhere in the crates is an unrelated buffer-width comment (crates/geo/src/vector_tiles.rs:282). It costs no request and no extra decode, and unlike CBS it is global rather than Dutch-only. That is a pointer for the note, not scope for this ticket.One stale premise, worth correcting in passing
The ticket body says "an extractor under
tools/". Since the port recorded indocs/notes/where-the-map-data-is-built.md, the Dutch extractors live injeroen/cartopy(server/pipeline/*.ts), nottools/. Nothing is being built here, so it changes no work — but a future frontier ticket copying that sentence would send someone to the wrong repository.Approach
Documentation only. Two files, both in this repo.
New
docs/notes/cbs-neighbourhood-statistics.md, following the shape ofdocs/notes/kadaster-parcels.mdanddocs/notes/nwb-road-network.md:topic:/triggers:/updated: 2026-08-29front-matter perdocs/notes/README.md. Trigger words should include at least:CBS,wijkenbuurten,buurten,wijken,gemeenten,"neighbourhood statistics","population density",aantalInwoners,bevolkingsdichtheid,personenautosPerKm2,PDOK,place_labels.hits= 23 vs 22 unique with a duplicate atstartIndex=22).docs/notes/kadaster-parcels.md:81-104, which is the section that stops the check being redone. At minimum: the unreadplace_labelspopulation; and the honest counter-argument (below).:106-135.docs/direction.md— delete line 83 from the Candidates, unverified table and append a row to the Checked and not taken table after line 93, in the established three-column form (Source | Checked | Why not), dated2026-08-29, linking[notes/cbs-neighbourhood-statistics.md](notes/cbs-neighbourhood-statistics.md).Commit body must name the values applied and why, per value 7 and the ticket's own instruction. The two that decide it are value 6 (Ship the smallest thing a machine can judge — a statistics table changes no count, placement, settle time, draw call or byte the harness can gate on) and value 1 (Measured beats plausible — a buurt-average density substituted for a per-building BAG fact, or for a fixed pool whose stated job is liveliness, is coarser than what is already read and would be an inference presented as a survey).
The honest counter-argument, to be written into the note so it is not re-derived: CBS is the only source here for how many people live in a specific block, and if this project ever grows a layer whose subject is residents rather than streets — occupancy that varies by neighbourhood rather than by hash, or a readout that names the buurt you are standing in — the data exists, is CC0, and is small. The number that would settle that is whether a buurt-resolution figure visibly beats the per-building BAG occupancy already in the shader, and nobody has measured it.
Acceptance criteria
docs/notes/cbs-neighbourhood-statistics.mdexists, withtopic:,triggers:andupdated:front-matter in the formdocs/notes/README.mdspecifies.ows:AccessConstraints,ows:Fees) — not a secondary source.bbox=6.555,53.207,6.580,53.222box: 23/6/1 features, 257 KB of buurten GeoJSON, 27.6 KB of geometry, 244 attributes per feature, 1,614 of 5,368 values suppressed as-99997.traffic.rs:59and:69and:77and:508,building_windows.rs:99,building_windows.wgsl:163-164,osm_buildings.rs:515,parking.rs:11-12.docs/notes/kadaster-parcels.md's paging section rather than restating it.place_labelspopulationand the counter-argument above.docs/direction.mdno longer lists CBS under Candidates, unverified (line 83 gone).docs/direction.md's Checked and not taken table has a CBS row dated 2026-08-29, in the same three-column form as the two rows above it, linking the new note.docs/is touched; no extractor, no streamer, no coverage manifest, noCargo.tomlchange.Verification
Read-only, all runnable in this container:
Nothing here needs a workstation, a GPU or a build. No
cargocommand is required or expected: the change compiles nothing and the repo has no docs linter. Do not run--shotorcargo shots— there is no rendered artefact to check, and this container cannot render anyway.The one thing that cannot be re-verified from a shell is whether the judgement is right, and that is what the note's pointers section exists to make cheap to revisit.
Out of scope
place_labels'population. Named as a pointer in the note because it is the cheap answer to the same question, but it is a change tocartopolis_geo/systems::placesand belongs to its own ticket offdocs/notes/vector-tile-unused-layers.md.docs/direction.md:82). It is the row that carries real vehicle flow, and this note should say so rather than pre-empting it.tools/" phrasing in the autopilot's ticket template. Recorded above so it is not lost; fixing the template is a change totools/autopilot.ts, not todocs/.docs/direction.mdbeyond the one row. Value-list edits and frontier reordering are a maintainer decision — the file says a session may propose, not rewrite.Open questions
None. The interface, the licence and the volume were all confirmed from the live service, and the ambiguity the ticket flagged is settled by values 6 and 1 as recorded above.
Branch:
docs/192-cbs-statistics-not-takenOriginal request
From the frontier in
docs/direction.md: CBS neighbourhood statistics — how many people are actually there.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 — 29 turns · $2.42 · session
3eb84cf3-de4e-48b6-bff2-de8c5cd1ea16. Reply with @agent to continue this session.🤖 Picked this up — working on branch
docs/192-cbs-statistics-not-takenin 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.Result: not worth wiring in. CBS neighbourhood statistics is now recorded as checked-and-not-taken. Two files changed, both documentation.
docs/notes/cbs-neighbourhood-statistics.md(141 lines) — the service, the CC0 evidence read off its own GetCapabilities, the volume table, why it lost, the paging trap, a pointers section, and a command block that reproduces every number in it.docs/direction.mdby one line: the CBS row moved out of Candidates, unverified into Checked and not taken.Why it lost. I re-ran every measurement against the live service rather than trusting the ticket, and all of them reproduce exactly. Over the Groningen box, the neighbourhoods response is 257,021 bytes of which only 27,582 is geometry — 696 coordinate pairs across 22 heavily generalised administrative outlines. The other 89 percent is a statistics table: 244 attributes per feature, of which 30 percent of the values are the suppression marker
-99997. So there is no object to draw, and nothing a headless capture can assert (value 6). The strongest case for taking it — ground the invented densities in a measured one — fails at all three places that could use it (value 1): window occupancy is already fed per building from the building register, and the car and walker pools are fixed, sized for "is this street alive", and already denser than the registered-car figure CBS measures.How I verified it. The issue's own verification block, all read-only: the three feature counts (23, 6, 1), the byte counts, the attribute and suppression counts, the duplicate page, and the link checks. No Rust file was touched, so there is nothing to compile;
cargo fmt --checkacross the workspace is clean. No visual check is needed or possible — nothing renders.One thing you should know.
origin/mainmoved while I worked: NDW traffic was checked and rejected the same day, which conflicted with my edit. I rebased, resolved it, and rewrote the sentence that had pointed at NDW as an open row — the note now says the two failed for different reasons and neither rescues the other.Worth your decision, not mine: the vector tiles this client already downloads carry a
populationfigure in theplace_labelslayer that nothing reads. It costs no request, it is global rather than Dutch-only, and it is the cheaper answer to the same question. I left it as a pointer in the note rather than acting on it — it is a code change and belongs to its own ticket.Branch
docs/192-cbs-statistics-not-taken(pushed)🤖 viberfox issue agent on the VPS — 35 turns · $3.22 · session
de6053d9-d82a-4839-8c4e-72d4f5c222fa. Reply with @agent to continue this session.🤖 Marked
agent:donewithout a CI verdict on923e255a— no workflow reported on this commit. The branch is waiting for review; check the run yourself before landing it.🤖 Merged into
mainas pull request #194 (923e255a).🤖 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" #220place_labels.populationas the cheaper answer, and it is a per-kindconstant #246crates/geoand was never updated #248docs/qa/targets.md— 14 of 14 passes re-verified a shipped ticket, 9 of them documentation-only verdicts #280