config: Greece pilot — Natural Earth carve (issue #172) #176

Merged
art merged 4 commits from issue-172-greece-pilot into main 2026-08-20 09:53:39 +00:00
Owner

Part of #172 — Greece pilot (the Natural Earth carve is done and verified; the CORINE cover is written but BLOCKED on the CLMS token — see the note). This PR does not auto-close #172; the cover half remains open.


#172: Greece pilot — Natural Earth carve + CORINE landcover

Proves the "add a country" mechanism with the smallest footprint. The mask (the
carve) is done and verified. The cover is written but BLOCKED on the CLMS
token — see the blocked item below.

What changed

New conf/gr/ per R17 (country: gr, R16), served through the shared map
source (Greece needs no tile source of its own):

  • base.yaml — destination, table_prefix: gr, country: gr, include:
    conf/common.yaml.
  • downloads.yaml — two declared downloads (R10): the ΥΠΕΝ mask WFS (plain
    anonymous GET, works today) and the CORINE entry (declared as the R10 route but
    blocked — see below).
  • staging.yaml — gr_perifereies (GML national outline, EPSG:4326, reprojected
    to 3857; coordinates already in lon/lat order, no axis swap) and gr_clc
    (CORINE, blocked).
  • 01-country_mask.yaml — Greece's partition of the shared map_country_mask_0,
    built from the ΥΠΕΝ perifereies (13 Regions) dissolved to the national
    outline, with its R119 acceptance test.
  • 02-cover.yaml — the identity CLC→enum.landcover mapping (the
    conf/fi/03-cover.yaml pattern, R116), part_key NULL (Iceland-class size,
    simplifies in one window), the shared speckle-floor zoom_filters, and the R34
    gapless cross-check + R120 no-NULL tests (both requires:-gated on
    map_cover_13_gr, so they skip until cover is importable).
  • acceptance.yaml — the config-level R105 staging-nonzero test (gated on
    gr_perifereies today; gr_clc joins when its download unblocks).

Makefile: import-gr (imports conf/gr, chains import-world per R107), fetch-gr
(fetches the mask WFS; CORINE deliberately excluded — token), and conf/gr added
to ACCEPTANCE_CONFIGS.

Verification (against the live DB)

  • importer config-check conf/gr — OK (2 layers, 28 acceptance tests, 0 sources).
  • Mask imported: map_country_mask_0_gr has 2 041 disjoint outline pieces, area
    132 029 km² (Greece's territory incl. territorial sea — official ~132k).
  • The union initially threw a GEOS TopologyException (regions share borders whose
    edges don't match), fixed by ST_MakeValid before ST_Union, with
    ST_CollectionExtract(..., 3) to drop the repair's stray linework (Iceland's
    R139 pattern). Result is ST_IsValid.
  • import-world applied the carve (R107): 0 km² of background_land now
    overlaps the Greek mask; a mainland point (22°E 38°N) is in the mask and no
    longer in the background; the z5 tile 17/14 decodes to a single background_land
    feature (the hole).
  • A decoded tile and make test both pass.

Acceptance tallies (make acceptance; read-only, no fetch/import):

── conf/is ──  38 test(s): 38 passed
── conf/se ──  45 test(s): 44 passed, 1 not run (needs -acceptance all)
── conf/pl ──  46 test(s): 44 passed, 1 failed, 1 not run   [see note]
── conf/fi ──  49 test(s): 49 passed
── conf/gr ──  28 test(s): 26 passed, 0 failed, 2 skipped    [the 2 = gated cover tests]
── conf/world.yaml ── 24 test(s): 24 passed

The one conf/pl failure is pre-existing and unrelated to Greece: Poland's heavy
buildings R120 scan (map_buildings_14_pl) blew its 10 s bucket (context deadline exceeded) — a timing flake on millions of rows, not touched by the carve.

Blocked: the CORINE cover download

CORINE 2018 for Greece requires a CLMS account + Bearer token to download —
the old EIONET direct-zip URLs are dead (verified: 404), and the current official
route is the CLMS download API
(land.copernicus.eu/api/@datarequest_post, DatasetID
0407d497d3c44bcd93ce8fd5bf78596a, NUTS "GR", OutputFormat GDB/GPKG) or the
~4 GB full-Europe GeoPackage — both need the token. The importer's downloads:
mechanism (fetch.go) is a plain unauthenticated GET, so it cannot attach the token.

I did not fake a download. The config declares the CORINE entry as the R10
route with a thorough comment documenting exactly what credential/prompt is
needed; the gr_clc staging and the 02-cover.yaml layer are written (with the
standard CLC code_18/geom schema assumed, flagged as unverifiable until
the file lands). Until data/gr/clc_gr.gpkg is placed, the cover layer's
acceptance tests skip (correctly gated) and import-gr fails at the cover layer.

To unblock: obtain a CLMS token, fetch the Greece CLC 2018 GeoPackage to
data/gr/clc_gr.gpkg, verify the layer name + code column against the delivered
file, then re-import cover and run the R34/R120 tests.

Not done / note

  • The docs/country-gr.md pilot section (deliverable 4, R129–R136): that document
    lives only on the unmerged #171 branch (issue-171-greece-catalogue, PR #174).
    Creating it here would conflict with #171. The pilot's per-layer reasoning is
    written next to each query in conf/gr/ instead; the doc section should land
    when #171 merges.
  • import-gr chains import-world (R107) and is the declared full import; the
    mask layer was verified via --layer country_mask + import-world (a per-layer
    reimport is the expected work; no full rebuild or wide -regeneralize).
  • The mask's 2 041 pieces include thousands of tiny Aegean islets at 1:50k — the
    world_mask union in conf/world.yaml dissolves them downstream exactly as for
    the other countries.
Part of #172 — Greece pilot (the Natural Earth carve is done and verified; the CORINE cover is written but BLOCKED on the CLMS token — see the note). This PR does not auto-close #172; the cover half remains open. --- # #172: Greece pilot — Natural Earth carve + CORINE landcover Proves the "add a country" mechanism with the smallest footprint. The mask (the carve) is **done and verified**. The cover is **written but BLOCKED** on the CLMS token — see the blocked item below. ## What changed New `conf/gr/` per R17 (`country: gr`, R16), served through the shared `map` source (Greece needs no tile source of its own): - `base.yaml` — destination, `table_prefix: gr`, `country: gr`, `include:` `conf/common.yaml`. - `downloads.yaml` — two declared downloads (R10): the ΥΠΕΝ mask WFS (plain anonymous GET, works today) and the CORINE entry (declared as the R10 route but blocked — see below). - `staging.yaml` — `gr_perifereies` (GML national outline, EPSG:4326, reprojected to 3857; coordinates already in lon/lat order, no axis swap) and `gr_clc` (CORINE, blocked). - `01-country_mask.yaml` — Greece's partition of the shared `map_country_mask_0`, built from the ΥΠΕΝ `perifereies` (13 Regions) dissolved to the national outline, with its R119 acceptance test. - `02-cover.yaml` — the identity CLC→`enum.landcover` mapping (the conf/fi/03-cover.yaml pattern, R116), `part_key` NULL (Iceland-class size, simplifies in one window), the shared speckle-floor `zoom_filters`, and the R34 gapless cross-check + R120 no-NULL tests (both `requires:`-gated on `map_cover_13_gr`, so they skip until cover is importable). - `acceptance.yaml` — the config-level R105 staging-nonzero test (gated on `gr_perifereies` today; `gr_clc` joins when its download unblocks). Makefile: `import-gr` (imports conf/gr, chains `import-world` per R107), `fetch-gr` (fetches the mask WFS; CORINE deliberately excluded — token), and `conf/gr` added to `ACCEPTANCE_CONFIGS`. ## Verification (against the live DB) - `importer config-check conf/gr` — OK (2 layers, 28 acceptance tests, 0 sources). - Mask imported: `map_country_mask_0_gr` has 2 041 disjoint outline pieces, area **132 029 km²** (Greece's territory incl. territorial sea — official ~132k). - The union initially threw a GEOS `TopologyException` (regions share borders whose edges don't match), fixed by `ST_MakeValid` before `ST_Union`, with `ST_CollectionExtract(..., 3)` to drop the repair's stray linework (Iceland's R139 pattern). Result is `ST_IsValid`. - `import-world` applied the carve (R107): **0 km²** of `background_land` now overlaps the Greek mask; a mainland point (22°E 38°N) is in the mask and no longer in the background; the z5 tile 17/14 decodes to a single `background_land` feature (the hole). - A decoded tile and `make test` both pass. Acceptance tallies (`make acceptance`; read-only, no fetch/import): ``` ── conf/is ── 38 test(s): 38 passed ── conf/se ── 45 test(s): 44 passed, 1 not run (needs -acceptance all) ── conf/pl ── 46 test(s): 44 passed, 1 failed, 1 not run [see note] ── conf/fi ── 49 test(s): 49 passed ── conf/gr ── 28 test(s): 26 passed, 0 failed, 2 skipped [the 2 = gated cover tests] ── conf/world.yaml ── 24 test(s): 24 passed ``` The one `conf/pl` failure is pre-existing and unrelated to Greece: Poland's heavy `buildings` R120 scan (`map_buildings_14_pl`) blew its 10 s bucket (`context deadline exceeded`) — a timing flake on millions of rows, not touched by the carve. ## Blocked: the CORINE cover download CORINE 2018 for Greece requires a **CLMS account + Bearer token** to download — the old EIONET direct-zip URLs are dead (verified: 404), and the current official route is the CLMS download API (`land.copernicus.eu/api/@datarequest_post`, DatasetID `0407d497d3c44bcd93ce8fd5bf78596a`, NUTS `"GR"`, OutputFormat GDB/GPKG) or the ~4 GB full-Europe GeoPackage — both need the token. The importer's `downloads:` mechanism (fetch.go) is a plain unauthenticated GET, so it cannot attach the token. I did **not** fake a download. The config declares the CORINE entry as the R10 route with a thorough comment documenting exactly what credential/prompt is needed; the `gr_clc` staging and the `02-cover.yaml` layer are written (with the standard CLC `code_18`/`geom` schema **assumed**, flagged as unverifiable until the file lands). Until `data/gr/clc_gr.gpkg` is placed, the cover layer's acceptance tests skip (correctly gated) and `import-gr` fails at the cover layer. **To unblock:** obtain a CLMS token, fetch the Greece CLC 2018 GeoPackage to `data/gr/clc_gr.gpkg`, verify the layer name + code column against the delivered file, then re-import `cover` and run the R34/R120 tests. ## Not done / note - The `docs/country-gr.md` pilot section (deliverable 4, R129–R136): that document lives only on the unmerged #171 branch (`issue-171-greece-catalogue`, PR #174). Creating it here would conflict with #171. The pilot's per-layer reasoning is written next to each query in `conf/gr/` instead; the doc section should land when #171 merges. - `import-gr` chains `import-world` (R107) and is the declared full import; the mask layer was verified via `--layer country_mask` + `import-world` (a per-layer reimport is the expected work; no full rebuild or wide `-regeneralize`). - The mask's 2 041 pieces include thousands of tiny Aegean islets at 1:50k — the `world_mask` union in conf/world.yaml dissolves them downstream exactly as for the other countries.
Prove the "add a country" mechanism with the smallest footprint. Greece builds a
country_mask partition from its own official boundary source — the ΥΠΕΝ
administrative-division GeoServer (`perifereies`, 13 Regions) — and the
Makefile's new import-gr target chains import-world (R107), which subtracts the
union of every mask from Natural Earth land. The carve is the mechanism proof:
after the mask lands the world background no longer covers Greece (0 km² of
background_land overlaps the mask; a mainland point is in the mask and out of
the background; the z5 tile 17/14 decodes to a single background_land feature).

The mask query dissolves the regions to the national outline; the union initially
threw a GEOS TopologyException because the region borders' edges do not match, so
ST_MakeValid runs first, and ST_CollectionExtract(..., 3) drops the repair's stray
linework (Iceland's R139 pattern). Result: 2 041 valid disjoint outline pieces,
132 029 km² — Greece's territory incl. its territorial sea.

New conf/gr/ per R17 (country: gr, R16): base.yaml, downloads.yaml, staging.yaml,
acceptance.yaml, 01-country_mask.yaml, 02-cover.yaml. The cover layer is the
identity CLC→enum.landcover mapping (the conf/fi/03-cover.yaml pattern, R116), with
R34 gapless-vs-mask and R120 no-NULL tests, all requires:-gated on map_cover_13_gr
so they skip until cover is importable. R22/R119 for cover live on the shared
contract and will cover Greece automatically.

Cover is BLOCKED on a CLMS registration + Bearer token: the old EIONET direct-zip
URLs are dead (404) and the importer's plain-GET downloads: mechanism cannot attach
the token, so no download was faked. The CORINE download is declared as the R10
route with the exact credential documented; the file must be placed at
data/gr/clc_gr.gpkg (schema assumed, flagged) to unblock it.

Docs: docs/country-gr.md (on the unmerged #171 branch) gains the pilot section when
#171 lands; the per-layer reasoning is written next to each query in conf/gr/.

Verified live: make build, config-check, a decoded tile, make test all pass; the
gr acceptance run is 26 passed / 2 skipped (the two gated cover tests). One
pre-existing conf/pl failure (buildings R120, a 10 s timing flake on millions of
rows) is unrelated to Greece.
Author
Owner

It looks like quite a bit of the NE land is peeking through around the coasts. We either need to use a maritime border or buffer the coast like we do in Iceland (be careful not to buffer the land border though).

It looks like quite a bit of the NE land is peeking through around the coasts. We either need to use a maritime border or buffer the coast like we do in Iceland (be careful not to buffer the land border though).
Two review fixes on the pilot:

The mask comment claimed "a valid result of 2 075 disjoint outline pieces". The
live table holds 2 041 (the PR body had it right; the comment did not). Measured
figures now name the table they were measured against.

The same comment claimed the dissolved outline includes the territorial sea. It
does not: the mask measures 132 041 km² against Greece's 131 957 km² of land
area, so `perifereies` are land administrative regions and the archipelagic
carve is islands and Crete, not sea. A mask that really covered Greek
territorial waters would be several times this size, and the cover layer's R34
land-area cross-check would not sit at 0.75%.

The fetch recipe now runs under `umask 002`. data/ is the shared
/stuff/ismap-data tree, and the GML this target wrote landed 0600 — unreadable
from the other account, so a re-import fails with a permission denied that reads
like a fetch bug. The CLMS provider's own output landed 0664 correctly; only the
curl path was wrong.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8HxnPejASTBhLaSxJKqBA
Collaborator

Review pass — pushed one commit with three fixes; the pilot itself verified clean.

Verified against the live database first, since the claims in the body are what a
reviewer would otherwise take on trust: importer acceptance conf/gr is 28/28 green,
R34 gapless at 0.75% (cover 131 042 km² vs territory 132 029 km²), R120 zero NULLs,
and z13 carries 42 distinct classes including the Mediterranean ones (olive_grove,
sclerophyllous, rice_fields, salt_marsh).

Fixed:

  1. The mask comment said 2 075 pieces; the table holds 2 041. The body had it right, the
    comment did not. Measured figures now name the table they came from.
  2. The mask does not include the territorial sea, as the comment claimed. It measures
    132 041 km² against Greece's 131 957 km² of land — perifereies are land administrative
    regions. A mask genuinely covering Greek territorial waters would be several times this,
    and R34's land-area cross-check would not sit at 0.75%.
  3. umask 002 on the fetch recipe. The GML this target wrote landed 0600 in the
    shared /stuff/ismap-data tree, so it is unreadable from the other account and a
    re-import fails with a permission denied that reads like a fetch bug — the trap AGENTS.md
    documents. The CLMS provider's own output landed 0664 correctly; only the curl path was
    wrong. (The existing file still needs chmod by its owner; only art can do that.)

The one thing I did not change: this branch's downloads: entry for CORINE declares
https://land.copernicus.eu/api/@datarequest_post as a plain URL that cannot be fetched.
#177 removes it entirely, so fixing it here would only conflict with that rewrite — but it
does mean this branch is not independently mergeable as a fetch path. Worth merging the two
together.

docs/country-gr.md's matching claims (territorial sea, NUTS "GR") are corrected in #183.

Review pass — pushed one commit with three fixes; the pilot itself verified clean. **Verified against the live database first**, since the claims in the body are what a reviewer would otherwise take on trust: `importer acceptance conf/gr` is **28/28 green**, R34 gapless at **0.75%** (cover 131 042 km² vs territory 132 029 km²), R120 zero NULLs, and z13 carries 42 distinct classes including the Mediterranean ones (`olive_grove`, `sclerophyllous`, `rice_fields`, `salt_marsh`). Fixed: 1. **The mask comment said 2 075 pieces; the table holds 2 041.** The body had it right, the comment did not. Measured figures now name the table they came from. 2. **The mask does not include the territorial sea**, as the comment claimed. It measures 132 041 km² against Greece's 131 957 km² of land — `perifereies` are land administrative regions. A mask genuinely covering Greek territorial waters would be several times this, and R34's land-area cross-check would not sit at 0.75%. 3. **`umask 002` on the fetch recipe.** The GML this target wrote landed **0600** in the shared `/stuff/ismap-data` tree, so it is unreadable from the other account and a re-import fails with a permission denied that reads like a fetch bug — the trap AGENTS.md documents. The CLMS provider's own output landed 0664 correctly; only the curl path was wrong. (The existing file still needs `chmod` by its owner; only `art` can do that.) The one thing I did **not** change: this branch's `downloads:` entry for CORINE declares `https://land.copernicus.eu/api/@datarequest_post` as a plain URL that cannot be fetched. #177 removes it entirely, so fixing it here would only conflict with that rewrite — but it does mean this branch is not independently mergeable as a fetch path. Worth merging the two together. `docs/country-gr.md`'s matching claims (territorial sea, `NUTS "GR"`) are corrected in #183.
The mask was built from `perifereies` alone, the administrative division of the
land. It stopped at the shoreline, so the Natural Earth carve stopped there too,
and every Greek gulf, strait and channel stayed drawn as background land —
Natural Earth does not resolve them as water at 1:110 m. The Gulf of Corinth was
the worst case: 2 429 km² of sea, essentially all of it, rendered as land.
Nationally it was 14 755 km² of Natural Earth land sitting on top of official
Greek sea.

R108 already says how to fix this: prefer a border source that includes
territorial waters, and fall back to Iceland's buffered convex hull only when
none exists. Greece has the source — the same ΥΠΕΝ WFD service the water layers
come from publishes the coastal (250 bodies, 38 448 km²) and transitional (47,
1 128 km²) water bodies, named, per river basin district, in the same GML shape
and axis order as perifereies. The mask now unions land plus both.

Greece must not use Iceland's hull: it has land borders with four countries and
shares the Aegean with Turkey, so a hull would carve Natural Earth land out from
under Albania, North Macedonia, Bulgaria and Anatolia and leave holes with
nothing drawn in them. The WFD bodies are national by construction and stop at
the maritime boundary on their own. Measured over-carve onto foreign coasts at
Natural Earth's resolution: 12 km² on the Turkish mainland opposite the
Dodecanese, 2 km² on the Albanian coast, 0 elsewhere.

Verified against the live database after a real fetch and import:

  mask               132 041 -> 167 843 km², 2 041 -> 184 pieces
  Gulf of Corinth      2 429 -> 40 km² of background land in the window
  mid-gulf            no background_land polygon intersects it at all
  tile z10/575/394     7.7 km² of background land in a 946 km² tile (0.8%)

Two tests keep it that way, both cheap. R108 asserts the mask is materially
larger than the land it contains (ratio 1.271 against a 1.2 floor; a land-only
mask scores 1.00), and R107 asserts the carve clears the Gulf of Corinth window.
The honest geometric check — no background land intersecting any coastal body —
wants ST_MakeValid over 250 multipolygons against continent-sized background
polygons, times out at 100 s, and fails on the same regression these two catch in
0.4 s.

R34 had to move with the mask. It compared land cover against the mask as its
second imported product, which was right while the mask was the land and is
wrong now: it read 21.9% off. It now compares against `gr_perifereies`, which is
Iceland's shape exactly — Iceland's R34 references `mork` for the same reason,
because its own mask is a buffered hull. Back to 0.75%. No ST_MakeValid on that
side: perifereies is invalid only in the shared-border way, which a union trips
over but an area sum does not — same 132 029 km² either way, 0.32 s against
7.1 s, which is the difference between fitting the 10 s bucket and being the
next #166.

R105 gains gr_cwb and gr_twb: an empty one of them degrades the mask back to the
land-only outline with no other symptom at import time.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8HxnPejASTBhLaSxJKqBA
Collaborator

You were right, and my earlier review was wrong in a way worth naming: I measured the mask
at 132 041 km² against Greece's 131 957 km² of land, saw it contradicted the comment claiming
territorial-sea coverage, and "corrected the comment". The measurement was the bug.

What was wrong

perifereies is an administrative division of the land. It stops at the shoreline, so
the carve stopped there too, and Natural Earth at 1:110 m does not resolve gulfs, straits or
channels as water. Measured on the live database before the fix:

Gulf of Corinth window 2 429 km² of background land — essentially all of it
nationally 14 755 km² of Natural Earth land sitting on official Greek sea

Nothing caught it, because the obvious carve check is tautologically true:
background_land is Natural Earth minus the union of every mask, so "no background land
overlaps the mask" passes for any mask at all — including an empty one. The add-country
skill was telling the next country to verify the carve exactly that way; #178 now replaces it.

The fix

R108's first branch, not Iceland's. The same ΥΠΕΝ WFD service the water layers come from also
publishes the coastal water bodies (gr_cwb_50k_xy_greece, 250 bodies, 38 448 km²) and
transitional water bodies (gr_twb_50k_greece, 47, 1 128 km²) — named, tagged by river
basin district, EPSG:2100, same GML shape and axis order as perifereies. The mask unions
land plus both.

Greece must not use Iceland's buffered hull. Four land borders and a shared Aegean: a hull
would carve Natural Earth land out from under Albania, North Macedonia, Bulgaria and Anatolia
and leave holes with nothing drawn in them. The WFD bodies are national by construction and
stop at the maritime boundary on their own. Measured over-carve onto foreign coasts at Natural
Earth's resolution: 12 km² on the Turkish mainland opposite the Dodecanese, 2 km² on
the Albanian coast, 0 elsewhere — against 14 755 km² correctly carved.

Verified, after a real fetch and a real import

before after
mask 132 041 km², 2 041 pieces 167 843 km², 184 pieces
Gulf of Corinth window 2 429 km² 40 km²
mid-gulf point inside background land no background polygon intersects it
tile z10/575/394 — 7.7 km² of coastal sliver in a 946 km² tile (0.8%)

importer acceptance conf/gr is 30/30, and importer tiles conf/gr --check passes.

Two tests so it stays fixed

  • R108 — the mask must be materially larger than the land it contains. Ratio 1.271
    against a 1.2 floor; a land-only mask scores 1.00. 0.4 s.
  • R107 — the carve clears the Gulf of Corinth window: 40.2 km² against a 300 km²
    bound. 0.03 s.

Deliberately area comparisons rather than the honest geometric one. "No background land
intersects any coastal water body" needs ST_MakeValid over 250 multipolygons against
continent-sized background polygons, times out at 100 s, and fails on exactly the same
regression these two catch in under half a second.

R34 had to move with the mask

It compared land cover against the mask as its second imported product. That was right while
the mask was the land; against a maritime mask it reads 21.9% off. It now references
gr_perifereies — which is Iceland's shape exactly, since Iceland's R34 references mork for
the same reason: its own mask is a buffered hull. Back to 0.75%. No ST_MakeValid on that
side — perifereies is invalid only in the shared-border way, which a union trips over but an
area sum does not; same 132 029 km² either way, 0.32 s against 7.1 s, which is the difference
between fitting the 10 s bucket and being the next #166.

R105 gains gr_cwb/gr_twb: an empty one of them degrades the mask straight back to the
land-only outline with no other symptom at import time.

The rule this is an instance of is in #183 — R108 gains the missing case and a Check: line
that says the tautological check is not it, and a new R143 says a coverage check references
the land, not the mask. #177 is rebased on this.

You were right, and my earlier review was wrong in a way worth naming: I measured the mask at 132 041 km² against Greece's 131 957 km² of land, saw it contradicted the comment claiming territorial-sea coverage, and "corrected the comment". The measurement *was* the bug. ## What was wrong `perifereies` is an administrative division of the **land**. It stops at the shoreline, so the carve stopped there too, and Natural Earth at 1:110 m does not resolve gulfs, straits or channels as water. Measured on the live database before the fix: | | | |---|---| | Gulf of Corinth window | **2 429 km²** of background land — essentially all of it | | nationally | **14 755 km²** of Natural Earth land sitting on official Greek sea | Nothing caught it, because the obvious carve check is **tautologically true**: `background_land` is Natural Earth *minus* the union of every mask, so "no background land overlaps the mask" passes for any mask at all — including an empty one. The `add-country` skill was telling the next country to verify the carve exactly that way; #178 now replaces it. ## The fix R108's first branch, not Iceland's. The same ΥΠΕΝ WFD service the water layers come from also publishes the **coastal water bodies** (`gr_cwb_50k_xy_greece`, 250 bodies, 38 448 km²) and **transitional water bodies** (`gr_twb_50k_greece`, 47, 1 128 km²) — named, tagged by river basin district, EPSG:2100, same GML shape and axis order as `perifereies`. The mask unions land plus both. **Greece must not use Iceland's buffered hull.** Four land borders and a shared Aegean: a hull would carve Natural Earth land out from under Albania, North Macedonia, Bulgaria and Anatolia and leave holes with nothing drawn in them. The WFD bodies are national by construction and stop at the maritime boundary on their own. Measured over-carve onto foreign coasts at Natural Earth's resolution: **12 km²** on the Turkish mainland opposite the Dodecanese, **2 km²** on the Albanian coast, **0** elsewhere — against 14 755 km² correctly carved. ## Verified, after a real fetch and a real import | | before | after | |---|---|---| | mask | 132 041 km², 2 041 pieces | **167 843 km², 184 pieces** | | Gulf of Corinth window | 2 429 km² | **40 km²** | | mid-gulf point | inside background land | **no background polygon intersects it** | | tile z10/575/394 | — | 7.7 km² of coastal sliver in a 946 km² tile (0.8%) | `importer acceptance conf/gr` is **30/30**, and `importer tiles conf/gr --check` passes. ## Two tests so it stays fixed - **R108** — the mask must be materially larger than the land it contains. Ratio **1.271** against a 1.2 floor; a land-only mask scores 1.00. 0.4 s. - **R107** — the carve clears the Gulf of Corinth window: **40.2 km²** against a 300 km² bound. 0.03 s. Deliberately area comparisons rather than the honest geometric one. "No background land intersects any coastal water body" needs `ST_MakeValid` over 250 multipolygons against continent-sized background polygons, times out at 100 s, and fails on exactly the same regression these two catch in under half a second. ## R34 had to move with the mask It compared land cover against the mask as its second imported product. That was right while the mask *was* the land; against a maritime mask it reads **21.9% off**. It now references `gr_perifereies` — which is Iceland's shape exactly, since Iceland's R34 references `mork` for the same reason: its own mask is a buffered hull. Back to **0.75%**. No `ST_MakeValid` on that side — `perifereies` is invalid only in the shared-border way, which a union trips over but an area sum does not; same 132 029 km² either way, 0.32 s against 7.1 s, which is the difference between fitting the 10 s bucket and being the next #166. R105 gains `gr_cwb`/`gr_twb`: an empty one of them degrades the mask straight back to the land-only outline with no other symptom at import time. The rule this is an instance of is in #183 — R108 gains the missing case and a *Check:* line that says the tautological check is not it, and a new R143 says a coverage check references the land, not the mask. #177 is rebased on this.
art-bot force-pushed issue-172-greece-pilot from 63d367e444 to fa74bf0a2e 2026-08-20 08:43:17 +00:00 Compare
art-bot changed target branch from main to issue-184-world-carve 2026-08-20 08:43:36 +00:00
Collaborator

The coastal-water mask did not fix it, and you were right to push back. I have reverted it and
fixed the real cause in #185 (issue #184); this PR is rebased onto that.

What the screenshots actually showed

The khaki wedges are not slivers — they are Natural Earth's 1:110 m land cutting straight
across the gulfs, and the Athens fringe is the same mismatch at hairline width. My acceptance
test measured area, and area cannot see either: 40 km² spread along 40 km of gulf is a
1 km-wide line straight through it, which is exactly what you saw. The test passed while the
map was wrong.

It was never Greece's mask

background_land was Natural Earth land minus the union of every country mask — a precise
national geometry subtracted from a coarse global one
. Measured on the live database:

country NE land left inside it foreign NE land it carved away
se 10 821 km² 1 579 km²
gr 6 429 km² 2 089 km²
fi 6 169 km² 3 987 km²
pl 3 011 km² 6 992 km²
is 0 km² 0 km²

Sweden is worse than Greece. Greece is just where someone looked. And the right-hand column is
the same error with the sign flipped, which nobody had looked at at all — Poland is removing
6 992 km² of its neighbours' background.

Iceland scores zero on both only because its mask is a 25 km buffered convex hull that
swallows Natural Earth's Iceland whole. Your instinct that a hull would break the land borders
was right: on Greece it would carve Natural Earth out from under Albania, North Macedonia,
Bulgaria and Anatolia.

Third party with correct land and maritime borders — you already have it

Natural Earth publishes ne_110m_admin_0_countries: the same 1:110 m scale as the land layer,
cut from the same source geometry, one polygon per country with an ISO code. Carving with
that is exact, because it is subtracting a shape from itself. And it is your other idea too —
which countries get carved is still read from map_country_mask_0, so countries go in and out
one at a time, but only the country code is read, never the geometry.

After: 0.0 km² inside every imported country, 0 km² of foreign land carved across all
170 we do not import. Greece goes from 6 429 km² to 0.03. Rendered over CDP: the Gulf of
Corinth is water, no wedge and no line, and the Athens coast at z10 has no fringe.

What this PR keeps

The mask is perifereies dissolved again — 2 041 pieces, 132 041 km², the land. The coastal
and transitional water bodies, their two fetches, their staging tables and the Greece-specific
carve tests are gone; the generic ones live on conf/world.yaml where the deciding geometry
is. The file keeps the story, because "make the mask maritime" is the obvious wrong answer and
the next country will think of it too.

The Peloponnese window now measures 2.28 km² of background land with the land-only mask,
against 3 159 before — which is the proof the carve no longer depends on this geometry at all.

Still here from the earlier review: the 2 041-piece correction, umask 002 on the fetch, and
R34 referencing gr_perifereies rather than the mask (R143).

The coastal-water mask did not fix it, and you were right to push back. I have reverted it and fixed the real cause in **#185** (issue **#184**); this PR is rebased onto that. ## What the screenshots actually showed The khaki wedges are not slivers — they are Natural Earth's 1:110 m land cutting straight across the gulfs, and the Athens fringe is the same mismatch at hairline width. My acceptance test measured *area*, and area cannot see either: 40 km² spread along 40 km of gulf is a 1 km-wide line straight through it, which is exactly what you saw. The test passed while the map was wrong. ## It was never Greece's mask `background_land` was Natural Earth land minus the union of every country mask — a **precise national geometry subtracted from a coarse global one**. Measured on the live database: | country | NE land left **inside** it | foreign NE land it **carved away** | |---|---|---| | `se` | **10 821 km²** | 1 579 km² | | `gr` | **6 429 km²** | 2 089 km² | | `fi` | **6 169 km²** | 3 987 km² | | `pl` | **3 011 km²** | 6 992 km² | | `is` | 0 km² | 0 km² | Sweden is worse than Greece. Greece is just where someone looked. And the right-hand column is the same error with the sign flipped, which nobody had looked at at all — Poland is removing 6 992 km² of its neighbours' background. Iceland scores zero on both **only because its mask is a 25 km buffered convex hull** that swallows Natural Earth's Iceland whole. Your instinct that a hull would break the land borders was right: on Greece it would carve Natural Earth out from under Albania, North Macedonia, Bulgaria and Anatolia. ## Third party with correct land and maritime borders — you already have it Natural Earth publishes `ne_110m_admin_0_countries`: the same 1:110 m scale as the land layer, cut from the same source geometry, one polygon per country with an ISO code. Carving with *that* is exact, because it is subtracting a shape from itself. And it is your other idea too — which countries get carved is still read from `map_country_mask_0`, so countries go in and out one at a time, but only the **country code** is read, never the geometry. After: **0.0 km² inside every imported country**, **0 km² of foreign land carved** across all 170 we do not import. Greece goes from 6 429 km² to **0.03**. Rendered over CDP: the Gulf of Corinth is water, no wedge and no line, and the Athens coast at z10 has no fringe. ## What this PR keeps The mask is `perifereies` dissolved again — 2 041 pieces, 132 041 km², the land. The coastal and transitional water bodies, their two fetches, their staging tables and the Greece-specific carve tests are gone; the generic ones live on `conf/world.yaml` where the deciding geometry is. The file keeps the story, because "make the mask maritime" is the obvious wrong answer and the next country will think of it too. The Peloponnese window now measures **2.28 km²** of background land with the *land-only* mask, against 3 159 before — which is the proof the carve no longer depends on this geometry at all. Still here from the earlier review: the 2 041-piece correction, `umask 002` on the fetch, and R34 referencing `gr_perifereies` rather than the mask (R143).
art changed target branch from issue-184-world-carve to main 2026-08-20 09:53:27 +00:00
art merged commit eee6c31ac1 into main 2026-08-20 09:53:39 +00:00
art deleted branch issue-172-greece-pilot 2026-08-20 09:53:39 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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!176
No description provided.