gap: a navigation mark is only readable from about mast height, and gone by 450 m #215

Closed
opened 2026-08-30 08:05:45 +00:00 by viberfox-agent · 1 comment
Collaborator

Found by the QA pass on #210, walking what #206/#207 shipped.

docs/notes/navigation-marks.md closes by leaving this exact question open:

Whether the tones read correctly, and whether a 1.4 m buoy is legible at the distance the Furniture fade starts, wants an eye on real hardware.

The tones half still does. The size half does not — apparent size in pixels is geometry, and geometry is trustworthy on lavapipe. So I measured it.

What I did

Three settled headless captures over the same stretch of the Waal at Nijmegen, at 60 m, 150 m (FURNITURE_FADE_START) and 400 m, framing a line of buoys. Every one reports nav_marks=115, settled=true — the geometry is present in all three.

cargo run -p cartopolis -- --shot /tmp/fade.png \
  --at 51.85242,5.85917,150 --look=-30,90 \
  --at 51.85242,5.85917,400 --look=-40,90 \
  --size 1280x720 --time 12 --clouds 0 --rain 0 --hide-ui \
  --wait 120 --settle 2 --dump-state /tmp/fade.json

What happened

altitude furniture_factor what a mark is on screen
60 m 0.00 a recognisable can with its lantern — shape and band pattern both read
150 m 0.00 a 2–3 px coloured speck; shape gone, pattern gone
400 m 0.93 one or two pixels, easy to miss entirely
450 m 1.00 hidden

So the mark is legible for roughly the first 50–80 m of altitude, and the fade band — 150 m to 450 m — is entirely below the size at which anything about the mark can be read. By the time the client starts fading them out they have already stopped saying anything.

Why that is a gap rather than a setting

Every other member of the Furniture group is scenery: a bench that is a grey speck at 150 m has lost nothing, because it was never carrying information. This layer is the exception, and its own note says so:

A navigation mark is the one form in the group where the colour is the datum: a red can and a green cone mean opposite things and differ in nothing else at 40 m.

The layer was built to draw shape, band pattern, topmark and lantern colour correctly — and inherited a size rule under which none of those four can be seen except from about the height of a ship's mast. The obvious thing a user does after switching a layer on is fly up far enough to see the river it applies to, and that is exactly the altitude at which it disappears.

What a user would expect instead

Either of the two things this codebase already does elsewhere when world geometry falls below a pixel:

  • Scale the metres with altitude in bands, the way navigation::route_width_scale does for the route line — same problem, same shape ("world geometry six metres wide is a comfortable fifteen pixels at 1 km and a third of a pixel at 30 km"), and the same reason screen space is unavailable: a mark lies in the water and takes the sun, the fog and the depth ladder with it. The banded key matters here too, or a zoom would re-triangulate every mark every frame.
  • Or a screen-space symbol above the fade, the way the place pins are drawn — a mark on a chart is a symbol, so this would arguably be more honest than a scaled model.

A third option is to leave it and record that this layer is a walk-up-to-it detail rather than something you read from the air; that is a legitimate answer, but it should be a decision rather than an inherited constant.

Where the seam is

  • crates/geo/src/furniture.rs — FURNITURE_FADE_START (150 m), FURNITURE_FADE_END (450 m)
  • crates/cartopolis/src/systems/map/street_furniture.rs — update_furniture_lod, which the marks ride unchanged
  • crates/cartopolis/src/systems/nav/navigation.rs — route_width_scale, the in-tree precedent for the fix

Not evidence here: the tones. Colour, exposure and lighting are not judgeable on this container's software rasteriser (docs/notes/headless-shots-software-renderer.md), so whether a red can reads as red still wants a real GPU. Apparent size does not.

Filed by the QA pass on #210. Not autonomous — a person decides whether this becomes work.

Found by the QA pass on #210, walking what #206/#207 shipped. `docs/notes/navigation-marks.md` closes by leaving this exact question open: > Whether the tones read correctly, and whether a 1.4 m buoy is legible at the distance the Furniture fade starts, wants an eye on real hardware. The **tones** half still does. The **size** half does not — apparent size in pixels is geometry, and geometry is trustworthy on lavapipe. So I measured it. ## What I did Three settled headless captures over the same stretch of the Waal at Nijmegen, at 60 m, 150 m (`FURNITURE_FADE_START`) and 400 m, framing a line of buoys. Every one reports `nav_marks=115`, `settled=true` — the geometry is present in all three. ``` cargo run -p cartopolis -- --shot /tmp/fade.png \ --at 51.85242,5.85917,150 --look=-30,90 \ --at 51.85242,5.85917,400 --look=-40,90 \ --size 1280x720 --time 12 --clouds 0 --rain 0 --hide-ui \ --wait 120 --settle 2 --dump-state /tmp/fade.json ``` ## What happened | altitude | `furniture_factor` | what a mark is on screen | |---|---|---| | 60 m | 0.00 | a recognisable can with its lantern — shape and band pattern both read | | 150 m | 0.00 | a **2–3 px** coloured speck; shape gone, pattern gone | | 400 m | 0.93 | one or two pixels, easy to miss entirely | | 450 m | 1.00 | hidden | So the mark is legible for roughly the first 50–80 m of altitude, and the *fade band* — 150 m to 450 m — is entirely below the size at which anything about the mark can be read. By the time the client starts fading them out they have already stopped saying anything. ## Why that is a gap rather than a setting Every other member of the Furniture group is scenery: a bench that is a grey speck at 150 m has lost nothing, because it was never carrying information. This layer is the exception, and its own note says so: > A navigation mark is the one form in the group where the colour is the datum: a red can and a green cone mean opposite things and differ in nothing else at 40 m. The layer was built to draw shape, band pattern, topmark and lantern colour correctly — and inherited a size rule under which none of those four can be seen except from about the height of a ship's mast. The obvious thing a user does after switching a layer on is fly up far enough to see the river it applies to, and that is exactly the altitude at which it disappears. ## What a user would expect instead Either of the two things this codebase already does elsewhere when world geometry falls below a pixel: - **Scale the metres with altitude in bands**, the way `navigation::route_width_scale` does for the route line — same problem, same shape ("world geometry six metres wide is a comfortable fifteen pixels at 1 km and a third of a pixel at 30 km"), and the same reason screen space is unavailable: a mark lies in the water and takes the sun, the fog and the depth ladder with it. The banded key matters here too, or a zoom would re-triangulate every mark every frame. - **Or a screen-space symbol above the fade**, the way the place pins are drawn — a mark on a chart *is* a symbol, so this would arguably be more honest than a scaled model. A third option is to leave it and record that this layer is a walk-up-to-it detail rather than something you read from the air; that is a legitimate answer, but it should be a decision rather than an inherited constant. ## Where the seam is - `crates/geo/src/furniture.rs` — `FURNITURE_FADE_START` (150 m), `FURNITURE_FADE_END` (450 m) - `crates/cartopolis/src/systems/map/street_furniture.rs` — `update_furniture_lod`, which the marks ride unchanged - `crates/cartopolis/src/systems/nav/navigation.rs` — `route_width_scale`, the in-tree precedent for the fix **Not** evidence here: the tones. Colour, exposure and lighting are not judgeable on this container's software rasteriser (`docs/notes/headless-shots-software-renderer.md`), so whether a red can reads as red still wants a real GPU. Apparent size does not. <sub>Filed by the QA pass on #210. Not `autonomous` — a person decides whether this becomes work.</sub>
Author
Collaborator

🤖 Considered by the 3-day retrospective (#218) and deliberately left unpromoted. It was the closest call of the four, and the reasoning is worth having on the ticket.

For it: the measurement is geometry, which is trustworthy on this hardware, and the fix has an exact in-tree precedent — navigation::route_width_scale solved the identical problem for the route line, in bands, for the same reason screen space was unavailable.

Against it, and this is what decided it: the ticket's third option is to leave it, and it says so itself — "a legitimate answer, but it should be a decision rather than an inherited constant". That is a request for a decision, not a spec. And the part a build would actually have to choose — how many metres a buoy should be at 300 m so that it reads without becoming a lollipop over the water — is judged by looking, which docs/direction.md puts out of bounds for the unattended lane. Apparent size in pixels is not taste; choosing the apparent size is.

If the maintainer picks a direction — banded world scale like the route line, or a screen-space symbol like the place pins — the rest is machine-judgeable and this becomes a straightforward promotion.

One thing the retrospective would flag: #215 and #214 are the same problem seen from two distances. A mark you cannot identify at 40 m and cannot see at 200 m is a layer that is drawn and not delivered. #214 was promoted; if that lands and this does not, the readout will be the only way the layer speaks.

Left for the maintainer by the retrospective on #218.

🤖 **Considered by the 3-day retrospective (#218) and deliberately left unpromoted.** It was the closest call of the four, and the reasoning is worth having on the ticket. For it: the measurement is geometry, which is trustworthy on this hardware, and the fix has an exact in-tree precedent — `navigation::route_width_scale` solved the identical problem for the route line, in bands, for the same reason screen space was unavailable. Against it, and this is what decided it: **the ticket's third option is to leave it**, and it says so itself — "a legitimate answer, but it should be a decision rather than an inherited constant". That is a request for a decision, not a spec. And the part a build would actually have to choose — how many metres a buoy should be at 300 m so that it reads without becoming a lollipop over the water — is judged by looking, which `docs/direction.md` puts out of bounds for the unattended lane. Apparent size in pixels is not taste; *choosing* the apparent size is. If the maintainer picks a direction — banded world scale like the route line, or a screen-space symbol like the place pins — the rest is machine-judgeable and this becomes a straightforward promotion. One thing the retrospective would flag: #215 and #214 are the same problem seen from two distances. A mark you cannot identify at 40 m and cannot see at 200 m is a layer that is drawn and not delivered. #214 was promoted; if that lands and this does not, the readout will be the only way the layer speaks. <sub>Left for the maintainer by the retrospective on #218.</sub>
Sign in to join this conversation.
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#215
No description provided.