Rijkswaterstaat vaarwegmarkeringen, PDOK OGC API: is it worth wiring in? #207

Merged
viberfox-agent merged 1 commit from feat/206-navigation-marks-layer into main 2026-08-30 07:02:18 +00:00
Collaborator

Closes #206

Merged by the autopilot (docs/direction.md) after every Actions job passed on the branch head.

Closes #206 Merged by the autopilot (docs/direction.md) after every Actions job passed on the branch head.
feat(map): draw the surveyed buoys and beacons on the waterways
All checks were successful
CI / test cartopolis (pull_request) Successful in 7m52s
CI / wasm & android targets (pull_request) Has been skipped
38c2387c08
The only thing standing on open water in this client was the *inferred*
moored boats of `crates/geo/src/moorings.rs`, whose own note is explicit
about being a guess read off the shape of a polygon. Rijkswaterstaat
surveys every navigation mark in the country individually, with the shape
and colour pattern that say which side of it to pass, the topmark that
says what it marks, and the colour of the light it shows. This draws them.

CC0 1.0, keyless OGC API Features at
`api.pdok.nl/rws/vaarwegmarkeringen-nederland/ogc/v1` (the path the
register's own name suggests is a 404). 43 marks in the densest z14 cell
measured — the Waal at Nijmegen — and zero over Groningen, Amsterdam
centre and Utrecht centre, which is the degrade-to-nothing shape the
streamers already have. Value 1 in `docs/direction.md` decided it:
measured beats plausible.

Three decisions worth stating, because each departs from the shape the
neighbouring layers use.

**No cartopy extractor, and no coverage manifest.** The other four Dutch
registers are extracted over there and served per cell by our own host,
and `docs/notes/where-the-map-data-is-built.md` says plainly that nothing
but a person keeps the two repositories in step. That seam is worth its
cost for a 2 MB-per-cell dataset needing reprojection and triangulation;
it is not worth it for 18,474 points behind a keyless bbox query with CORS
`*` and an hour of cache control. The precedent for reaching PDOK from the
client is `systems::height` and `systems::aerial`; for a per-cell
third-party streamer it is `systems::tall_structures`. `CoverageSlot`
answers "which cells does our host serve", which is not a question here —
coverage is an envelope, as `aerial` decided for the same service.

**The water gate reads the undecoded tile body.** Most of the planet is
neither Dutch nor wet, and an empty answer still costs a round trip and
1.9 KB. The envelope stops the world outside the Netherlands; the cell's
water stops the dry two thirds inside it. But `map_geometry` fetches a
tile on the I/O pool and parses it on a worker (ADR-033), so a fetch
decision cannot wait for the parse without moving a ~120 ms decode onto
the frame loop on wasm. `vector_tiles::body_mentions_water` is a substring
search for the layer names instead, and its asymmetry is what makes it
safe: MVT stores a layer's name verbatim, so a false negative is
impossible and a false positive costs one request. The Furniture toggle
is the third gate, so a run with the layer off asks for nothing.

**Not new `FurnitureKind` variants.** `FurnitureInstance` carries exactly
one `paint`, and a mark's colour is a pattern of up to three bands plus a
separate topmark colour plus a light colour; widening that struct would
touch `parking`, `traffic` and `moorings` to add a field one form reads.
`nav_marks` builds its own round forms — the shape *is* the datum, a cone
and a can mean opposite things — into `furniture`'s own `MeshBuf`, so the
marks land in `SurfaceGroup::Furniture` and inherit its shared material,
its altitude fade and its Layers row for free. No collider, which is where
a moored boat also ends up by the other route: an invisible wall in a
fairway is worse than a buoy you can walk through.

Two more that are about being right rather than complete. The register
also holds verge lighting, sign floodlighting and shore cabinets; those
are not navigation marks, and the BGT surveys street lighting column by
column already, so drawing them would stand two lampposts on one spot —
33 of 260 sampled fixed features are dropped. And a mark whose colour did
not parse is not drawn at all, because the colour is the meaning and a
grey buoy asserts a mark that says nothing.

Censused 998 floating and 260 fixed features on 2026-08-30, and three
things in there fail *silently* when read wrong, each producing nothing at
all rather than an error: the geometry is `MultiPoint` and never `Point`;
the fixed collection writes `v_toptek` and `licht_kl` where the floating
one writes `tt_toptek` and `licht_klr`; and absent is spelled three ways
(`X`, `Niet toegewezen`, `#`). Two verbatim captures under
`crates/cartopolis/tests/fixtures/` pin all three against the register's
own answers rather than against my idea of them. `obj_vorm` also carries a
fifth value, `Pilaar`, that the ticket's census missed.

`ShotMetrics::nav_marks` is the number that separates "the layer arrived"
from "this water has none", since empty water is what the client always
drew. Colour and the legibility of a 1.4 m buoy at the fade distance are
not judgeable on a software rasteriser and want an eye on real hardware.

Refs #206
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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!207
No description provided.