gap: grass still grows on the half of an arable parcel that crosses a cell boundary #224

Closed
opened 2026-08-30 08:37:36 +00:00 by viberfox-agent · 0 comments
Collaborator

Found while QA-ing what #203 shipped (#217).

What I did

Served the crop tree from a stand-in extractor (see #217) for two adjacent z14
cells, 8449/5374 and 8450/5374, then stood on the boundary between them, inside a
single registered seed-potato parcel (gewascode 2015, Bouwland) that crosses
it with a 316 m frontage, and looked each way:

cargo run -p cartopolis -- --shot /tmp/edge.png \
    --at 52.498443,5.668945,20 --at 52.498443,5.668945,20 \
    --look=-25,90 --look=-25,270 --time 12 --clouds 0 --rain 0 --hide-ui

(yaw 90 = west, into the cell that owns the parcel; yaw 270 = east, into the
neighbour it overhangs.)

What happened

Looking west, the field is bare — the keep-out #203 added is working. Looking
east, over the same registered parcel, the ground is a dense carpet of
near-field grass. The seam runs down the middle of one potato field, on the z14
cell boundary, and nothing in the world corresponds to it.

The cause is that the two halves of the layer bin differently, by design and only
half-way:

  • crops::crop_parcels deliberately projects and draws a parcel whole, past
    its own cell — the note measures 22–24 % of parcels straddling and says
    dropping them would delete a quarter of the country's fields.
  • turf::GrassArea::build(&tile, tile_m, &parcels) is handed only that cell's
    parcels, so the neighbour's ArableMask has a hole exactly where the
    overhang lies.

Confirmed in the data as well as on screen: the point 18 m east of the cell edge
is inside parcel 2015 (which cell 8449/5374 owns and draws) and inside no parcel
at all in cell 8450/5374's own file — the file whose mask governs blades there.

Measured over the same live-register cell: 19 of 72 arable parcels binned to
8449/5374 reach outside it, one of them by 434 m. So roughly a quarter of arable
fields keep a lawn on the far side of a cell line, and the cell lines are ~1.4 km
apart across the whole country.

What a user would expect

Ploughed ground to be ploughed ground the whole way across the field. There is no
visible reason for grass to start in the middle of it.

Where the seam is

  • crates/cartopolis/src/systems/map/map_geometry.rs ~757 —
    GrassArea::build(&tile, tile_m, &parcels), where parcels is this cell's
    crop_parcels(&crop_cell, …) and nothing else.
  • crates/geo/src/turf.rs — ArableMask::build.
  • crates/cartopolis/src/systems/map/crops.rs — crop_parcels, whose doc comment
    explains the whole-parcel rule that the mask does not follow.

The cheapest fix is symmetry: build the mask from the parcels of this cell and
its eight neighbours
(the surfaces job already awaits per-cell files, so the
ring is a fetch of eight cached JSONs), or have the extractor repeat a straddling
parcel into every cell it touches for masking while keeping one owner for drawing.
Either way the test to add is the mirror of the existing
a_parcel_straddling_the_boundary_survives_with_all_its_vertices: a parcel owned
by A must keep blades off ground in B.

Found while QA-ing what #203 shipped (#217). ## What I did Served the crop tree from a stand-in extractor (see #217) for two adjacent z14 cells, 8449/5374 and 8450/5374, then stood on the boundary between them, inside a single registered seed-potato parcel (`gewascode` 2015, `Bouwland`) that crosses it with a 316 m frontage, and looked each way: ``` cargo run -p cartopolis -- --shot /tmp/edge.png \ --at 52.498443,5.668945,20 --at 52.498443,5.668945,20 \ --look=-25,90 --look=-25,270 --time 12 --clouds 0 --rain 0 --hide-ui ``` (yaw 90 = west, into the cell that owns the parcel; yaw 270 = east, into the neighbour it overhangs.) ## What happened Looking west, the field is bare — the keep-out #203 added is working. Looking east, over the *same registered parcel*, the ground is a dense carpet of near-field grass. The seam runs down the middle of one potato field, on the z14 cell boundary, and nothing in the world corresponds to it. The cause is that the two halves of the layer bin differently, by design and only half-way: * `crops::crop_parcels` deliberately projects and draws a parcel **whole**, past its own cell — the note measures 22–24 % of parcels straddling and says dropping them would delete a quarter of the country's fields. * `turf::GrassArea::build(&tile, tile_m, &parcels)` is handed **only that cell's** parcels, so the neighbour's `ArableMask` has a hole exactly where the overhang lies. Confirmed in the data as well as on screen: the point 18 m east of the cell edge is inside parcel 2015 (which cell 8449/5374 owns and draws) and inside no parcel at all in cell 8450/5374's own file — the file whose mask governs blades there. Measured over the same live-register cell: **19 of 72** arable parcels binned to 8449/5374 reach outside it, one of them by 434 m. So roughly a quarter of arable fields keep a lawn on the far side of a cell line, and the cell lines are ~1.4 km apart across the whole country. ## What a user would expect Ploughed ground to be ploughed ground the whole way across the field. There is no visible reason for grass to start in the middle of it. ## Where the seam is * `crates/cartopolis/src/systems/map/map_geometry.rs` ~757 — `GrassArea::build(&tile, tile_m, &parcels)`, where `parcels` is this cell's `crop_parcels(&crop_cell, …)` and nothing else. * `crates/geo/src/turf.rs` — `ArableMask::build`. * `crates/cartopolis/src/systems/map/crops.rs` — `crop_parcels`, whose doc comment explains the whole-parcel rule that the mask does not follow. The cheapest fix is symmetry: build the mask from the parcels of this cell **and its eight neighbours** (the surfaces job already awaits per-cell files, so the ring is a fetch of eight cached JSONs), or have the extractor repeat a straddling parcel into every cell it touches for masking while keeping one owner for drawing. Either way the test to add is the mirror of the existing `a_parcel_straddling_the_boundary_survives_with_all_its_vertices`: a parcel owned by A must keep blades off ground in B.
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#224
No description provided.