cover: exclude LMÍ's broken sea polygon, compute a valid sea from the coastline (issue #46) #61

Merged
art merged 1 commit from night/issue-46-iceland-sea-polygon into main 2026-08-05 09:41:55 +00:00
Owner

Closes #46


Iceland landcover: Grímsey and the NE coast rendered as sea — the LMÍ sea polygon is broken, not absorption

The report

The follow-up screenshots (comment 533) show Grímsey completely vanishing at z12
and a broken coastline at z13 on the NE coast (66.50, -16.17) — with labels,
airport, contours and roads still rendering. The maintainer's point stands: a
whole-screen island is not a sub-threshold absorption candidate at z12, so this
is not the #47/#41 bug class.

Root cause

The LMÍ landcover source contains one giant sea polygon (533,000 km², CLC 523)
clipped at the dataset's 66.5°N northern edge. The clip leaves the ring broken:

  • ST_IsValid fails — self-intersection at exactly the 66.5°N cut line on the
    NE coast (-1775098, 10015050 in 3857).
  • The ring roles are inverted: the outer ring is the small loop around Grímsey
    (28.5 km²) and the "hole" is the 576,000 km² ocean shell.

Consequences, verified against the live DB:

  • z13 (source): the invalid polygon is dropped by ST_AsMVTGeom at tile
    time, so Grímsey renders as land — by accident. But at the NE coast the
    invalid ring is encoded into the tile, drawing a broken coastline at the
    data cut (the second screenshot). ST_Intersection on sea vs land even
    throws TopologyException: input geometry is invalid.
  • z12 (rollup zoom): the whole z13→z12 step is semanticTransitionSQL,
    which runs ST_Union(ST_MakeValid(geom)). MakeValid "repairs" the broken
    polygon by promoting every land loop into a solid polygon: Grímsey becomes a
    28.5 km² marine_water polygon with exactly the island's footprint (the
    island's land polygons survive underneath, but cover_water is layer #19,
    drawn after all land layers, so the water paints over the island). The NE
    peninsula loops become the 228.5/72.5/43.6 km² marine_water pieces at the
    second screenshot's location.
  • The z12 table reproduced deterministically from the current z13: 11,045 rows,
    36 marine_water polygons, same Grímsey polygon to the last digit.

The earlier #46 investigation missed it because it checked per-zoom max-Y
(which stays at Grímsey's tip because the marine_water polygon reaches it)
and feature counts, never the class of the polygon at the island — and its
zoom list skipped z12. The #47 absorb_boundary floor never sees Grímsey: it is
already water at z12, before absorption runs (z12 = z11 = z10, identical
tables).

Sibling countries are unaffected: SE (2,372), PL (41) and FI (88) sea polygons
are all valid; their z12 water/land pairs overlap only at shared edges, which
is normal for a coverage.

The fix

Exclude the sea class from Iceland's cover source query. The ocean is already
the style's background colour (hsl(205,55%,74%) — identical to the cover
water fill), so the class is redundant as well as broken: it only ever
contributed the eaten islands, the z13 coastline artifact, and a 74,936-point
polygon shipped into every coastal tile.

- query: SELECT geom, lcv_class AS cover_class, NULL AS part_key FROM is_lcv_lmi_landcoverunit
+ query: SELECT geom, lcv_class AS cover_class, NULL AS part_key FROM is_lcv_lmi_landcoverunit WHERE lcv_class <> 'sea'

Verification

Reimported Iceland's cover layer from the new config (staging strandlina → GPKG
land classes → computed sea → cascade → swap). All checks against the live DB
and a real-browser render (CDP, swiftshader, no virtual time):

  • The sea row in map_cover_13_is is now the computed polygon: valid
    (ST_IsValid true), 6 245 rings (one per coastline land polygon), the old
    533 000 km² invalid polygon is gone (17 632 land rows + 1 sea row).
  • Grímsey bbox per zoom: land at z13 (sparse_vegetation, natural_grassland,
    discontinuous_urban, moor, airport), z12–z10 (sparse_open, scrub_grassland,
    urban_fabric, industrial_commercial_transport), z9 (nature), z8–z6 (nature).
    No water class anywhere on the island. The water mask's hole at Grímsey
    survives to z9–z6 (ST_Contains(mask, island_centroid) = false); at z5 the
    mask covers it, same as the pre-fix build — no regression.
  • Decoded tiles: z13 NE coast (13/3727/2048) = sparse_vegetation + moor + sea
    (valid, no TopologyException); z12 Grímsey (12/1843/1023) = the four land
    classes + marine_water (hole); z9 Grímsey (9/230/127) = artificial + nature +
    water. Tile sizes unchanged in magnitude (z12 Grímsey ≈ 5.8 KB binary).
  • Real-browser renders at both report locations (:8080/?map=is#12.71/… and
    #13.18/66.50164/-16.17706): queryRenderedFeatures shows cover_water (sea)
    around land classes — sparse/scrub/industrial/urban_dense at Grímsey, and
    sea + water_body as water with moor/sparse_vegetation as land on the NE
    coast. The original z9 report location renders land + water normally.
  • The earlier "drop the sea entirely" attempt (first reimport) was rejected
    because it unmasked the LMI landcover's coast-slop: the land polygons run
    over bays and beyond the coast (the z13 table overlaps 5.6× Iceland's area;
    one moor polygon covers a point 80 km offshore in Faxaflói bay), so without
    a mask the z9 rollup merged the touching classes into a 426 000 km² nature
    blob painting the ocean as land. The computed sea keeps the mask (and is
    valid, so MakeValid never promotes its island holes).
  • Sibling countries unaffected: SE/PL/FI sea polygons are all valid (2 372 /
    41 / 88 rows); their z12 water–land pairs overlap only at shared edges.
Closes #46 --- # Iceland landcover: Grímsey and the NE coast rendered as sea — the LMÍ sea polygon is broken, not absorption ## The report The follow-up screenshots (comment 533) show Grímsey completely vanishing at z12 and a broken coastline at z13 on the NE coast (66.50, -16.17) — with labels, airport, contours and roads still rendering. The maintainer's point stands: a whole-screen island is not a sub-threshold absorption candidate at z12, so this is not the #47/#41 bug class. ## Root cause The LMÍ landcover source contains one giant `sea` polygon (533,000 km², CLC 523) clipped at the dataset's 66.5°N northern edge. The clip leaves the ring broken: - `ST_IsValid` fails — self-intersection at exactly the 66.5°N cut line on the NE coast (`-1775098, 10015050` in 3857). - The ring roles are inverted: the outer ring is the small loop around Grímsey (28.5 km²) and the "hole" is the 576,000 km² ocean shell. Consequences, verified against the live DB: - **z13 (source)**: the invalid polygon is dropped by `ST_AsMVTGeom` at tile time, so Grímsey renders as land — by accident. But at the NE coast the invalid ring *is* encoded into the tile, drawing a broken coastline at the data cut (the second screenshot). `ST_Intersection` on sea vs land even throws `TopologyException: input geometry is invalid`. - **z12 (rollup zoom)**: the whole z13→z12 step is `semanticTransitionSQL`, which runs `ST_Union(ST_MakeValid(geom))`. MakeValid "repairs" the broken polygon by promoting every land loop into a solid polygon: Grímsey becomes a 28.5 km² `marine_water` polygon with *exactly* the island's footprint (the island's land polygons survive underneath, but `cover_water` is layer #19, drawn after all land layers, so the water paints over the island). The NE peninsula loops become the 228.5/72.5/43.6 km² marine_water pieces at the second screenshot's location. - The z12 table reproduced deterministically from the current z13: 11,045 rows, 36 marine_water polygons, same Grímsey polygon to the last digit. The earlier #46 investigation missed it because it checked per-zoom max-Y (which stays at Grímsey's tip *because the marine_water polygon reaches it*) and feature counts, never the class of the polygon at the island — and its zoom list skipped z12. The #47 absorb_boundary floor never sees Grímsey: it is already water at z12, before absorption runs (z12 = z11 = z10, identical tables). Sibling countries are unaffected: SE (2,372), PL (41) and FI (88) sea polygons are all valid; their z12 water/land pairs overlap only at shared edges, which is normal for a coverage. ## The fix Exclude the `sea` class from Iceland's cover source query. The ocean is already the style's background colour (`hsl(205,55%,74%)` — identical to the cover water fill), so the class is redundant as well as broken: it only ever contributed the eaten islands, the z13 coastline artifact, and a 74,936-point polygon shipped into every coastal tile. ```diff - query: SELECT geom, lcv_class AS cover_class, NULL AS part_key FROM is_lcv_lmi_landcoverunit + query: SELECT geom, lcv_class AS cover_class, NULL AS part_key FROM is_lcv_lmi_landcoverunit WHERE lcv_class <> 'sea' ``` ## Verification Reimported Iceland's cover layer from the new config (staging strandlina → GPKG land classes → computed sea → cascade → swap). All checks against the live DB and a real-browser render (CDP, swiftshader, no virtual time): - The sea row in `map_cover_13_is` is now the **computed** polygon: valid (`ST_IsValid` true), 6 245 rings (one per coastline land polygon), the old 533 000 km² invalid polygon is gone (17 632 land rows + 1 sea row). - Grímsey bbox per zoom: land at z13 (sparse_vegetation, natural_grassland, discontinuous_urban, moor, airport), z12–z10 (sparse_open, scrub_grassland, urban_fabric, industrial_commercial_transport), z9 (nature), z8–z6 (nature). No water class anywhere on the island. The water mask's hole at Grímsey survives to z9–z6 (`ST_Contains(mask, island_centroid)` = false); at z5 the mask covers it, same as the pre-fix build — no regression. - Decoded tiles: z13 NE coast (13/3727/2048) = sparse_vegetation + moor + sea (valid, no TopologyException); z12 Grímsey (12/1843/1023) = the four land classes + marine_water (hole); z9 Grímsey (9/230/127) = artificial + nature + water. Tile sizes unchanged in magnitude (z12 Grímsey ≈ 5.8 KB binary). - Real-browser renders at both report locations (`:8080/?map=is#12.71/…` and `#13.18/66.50164/-16.17706`): queryRenderedFeatures shows cover_water (sea) around land classes — sparse/scrub/industrial/urban_dense at Grímsey, and sea + water_body as water with moor/sparse_vegetation as land on the NE coast. The original z9 report location renders land + water normally. - The earlier "drop the sea entirely" attempt (first reimport) was rejected because it *unmasked* the LMI landcover's coast-slop: the land polygons run over bays and beyond the coast (the z13 table overlaps 5.6× Iceland's area; one moor polygon covers a point 80 km offshore in Faxaflói bay), so without a mask the z9 rollup merged the touching classes into a 426 000 km² nature blob painting the ocean as land. The computed sea keeps the mask (and is valid, so MakeValid never promotes its island holes). - Sibling countries unaffected: SE/PL/FI sea polygons are all valid (2 372 / 41 / 88 rows); their z12 water–land pairs overlap only at shared edges.
LMÍ's landcover `sea` polygon (CLC 523) is clipped at the dataset's 66.5°N
northern edge, and the clip leaves the ring self-intersecting with inverted
ring roles (outer ring = the Grímsey loop, hole = the ocean shell). The z12
rollup's ST_Union(ST_MakeValid(...)) "repairs" it by promoting every land loop
into solid marine_water — Grímsey and the NE peninsula render as sea from z12
down — and at z13 the invalid ring draws a broken coastline at the cut.

The cover layer now has two sources: the GPKG land classes (the broken 523
excluded — the WHERE filters on the integer code) plus a valid sea computed
from the official coastline (strandlina land minus a box around Iceland). The
sea is load-bearing: the LMI landcover's own coastlines are sloppy (the z13
table overlaps 5.6x Iceland's area; polygons run over bays and beyond the
coast), and the sea, drawn last, is the mask that keeps them off the ocean.
The computed sea is valid, so MakeValid never promotes its island holes and
the rendered coast follows the IS 50V coastline. Sibling countries are
unaffected — their sea polygons are valid.

Verified against the live DB and in a real browser at both report locations
(z12.71 Grímsey, z13.18 NE coast): Grímsey renders land at every zoom, the NE
coast renders sea + land with no invalid-geometry artifact, and the coarse
zooms keep the island hole in the water mask.

Co-Authored-By: Hermes Agent <noreply@nousresearch.com>
art merged commit 94d2580c6c into main 2026-08-05 09:41:55 +00:00
art deleted branch night/issue-46-iceland-sea-polygon 2026-08-05 09:41:55 +00:00
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
art/ismap!61
No description provided.