airports: pavement over ground — Finland's and Poland's blobs become Keflavík #159

Merged
art merged 1 commit from airports-movement-area-split into main 2026-08-18 19:06:50 +00:00
Collaborator

Keflavík is what an airport should look like: a pale ground from the landcover with the dark aviation pavement — runways, the taxiway network, aprons — drawn on top. Finland and Poland each drew one dark-grey blob instead, and Arlanda drew runways floating on a meadow. All three now render like Keflavík.

This supersedes !158, which fixed the blobs by deleting geometry: Finland kept runways only (no taxiways, and 5 airfields lost their polygon entirely, needing a widened name-join radius to compensate) and Poland's layer was removed outright. Nothing had to be given up.

The contract: surface_class

The shared airports layer gains one attribute — 1 = aviation pavement, 2 = the airport ground, drawn pale beneath it. Ground rows are emitted only where the country's landcover cannot draw them: Iceland (CORINE) and Finland (CLC 124) already have an airport cover class and emit none, Sweden's Topo 10 mark and Poland's BDOT10k have no aviation class at all. New style layer airport_grounds paints class 2 in hsl(300, 7%, 84%) — the same grey cover_industrial uses for the airport class — so an airport does not change colour between countries.

Finland — the ladder splits pavement from ground exactly

This is what issue #110's all-835-areas import missed. Rendered by class, the ladder is unambiguous:

kohdeluokka n
32411 / 32412 87 / 23 pavement — runways, paved / unpaved
32415 29 pavement — paved aprons, platforms
32417 229 pavement — the taxiway network, aprons, hardstands
32418 38 pavement — unpaved movement area; at Wredeby the grass strip itself
32416 429 ground — the airfield infield, the grass between the runways

412 rows imported. The excluded 32416 is left to CLC 124, which covers 222.8 of its 249.6 km². The polygons turn out to be a planar partition — nothing contains or overlaps anything, they only touch (measured with ST_Contains / ST_Overlaps over the whole airport) — which is exactly why the runway and taxiway outlines were visible through the old blob, and why the split is a filter rather than a difference.

The 32416 fallback, 6 polygons. A pure grass airfield is sometimes surveyed as one 32416 area and nothing else — Mäntsälä, Lapinlahti, Savikko, Torbacka (Wredeby's identical strip is a 32418, the inconsistency that gives the rule away) and two unnamed fields. There the ground is the landing surface, and at 6–27 ha it is below CORINE's 25 ha MMU as well, so excluding it erases the airfield rather than moving it to the cover. The rule is "a 32416 whose airfield maps no pavement within 500 units", not a list of five names.

Because 32417 is the apron the terminal label stands on, all 88 register names survive at the original 500-unit join radius (measured: the 54 lentoasema label points lie 0–120 m, p90 62 m, from the nearest pavement) and airport_points goes back to the shared derivation unchanged. !158 needed 1000 and still lost 5 labels.

Poland — the runways were on the map the whole time

KUKO_A alone is a land-use blob: Chopin was one 15.38 km² dark smear with no runway visible, while the runways rendered one layer down as cover_class = transport. They live in OT_PTKM_A, and splitting them out needs the KUKO polygon as a spatial mask, not the RODZAJ column PTKM does not have. The long-standing note in plmap.yaml was answering the wrong question.

Measured: Chopin's ground holds 1.97 km² of PTKM in 2 polygons, and both its runway centrelines — built independently from OurAirports thresholds — lie 100% inside them (computed lengths 2799 / 3686 m against published 2800 / 3690). Nationwide, 40 of the 53 Polish runways with published thresholds are ≥90% inside the clip, 85% of total centreline length. The 13 misses are almost all military fields BDOT10k redacts (EPMI, EPSN, EPRU, EPML …) — see #157.

An intersection restricted to ~140 airport footprints is not #118's carve: nothing is subtracted from cover, which still maps PTKM to transport. What #118 would eventually change is the ground half.

Sweden — the ground was already in the database

flygplatsomrade (183 areas) has been staged since #103 for the class ladder's iata/icao codes and never drawn. The 182 areas holding a live runway become the ground; Arlanda stops being three strips on 76.85 km² of natural_grassland. Taxiways remain absent — Topo 10 surveys nothing between runway and perimeter. That is a source limit, not a filter.

Iceland

Unchanged geometry, now labelled surface_class 1 and documented as the contract's target rendering.

Tests

R141, new (docs/country-requirements.md): pavement is mandatory, ground is optional — a partition holding only ground is the blob defect, and every count- and NULL-based check passes while it is broken. Poland additionally pins every pavement polygon inside a ground, so a widened clip cannot paint the national road network in the airports grey. Finland's R6 pins the exact pavement set and keeps its count(*) > 0 half, so an empty source still fails rather than satisfying 0 = 0.

Verified

  • rows: fi 412 pavement, is 67, pl 206 + 301 ground, se 190 + 182 ground
  • label points: fi 88 (back from the 83 a partition-derived set gave under a runway-only filter), is 67, pl 140, se 48 — all unchanged from before the blobs were touched
  • decoded tiles carry surface_class at z10 and z13 (R121, every config)
  • queryRenderedFeatures in a real headless MapLibre returns class 1 only for is/fi and both classes for se/pl — the ["in", attr, N] filter form matches, and screenshots of all four airports confirm the rendering
  • largest airports tile 3.4 kB across z10–z13
  • make test, config-check and acceptance all pass: is 34, se 37 (+1 slow not-run), fi 42, pl 38, world 19

Docs updated: the four country files, country-requirements.md (R141 + the capability table now splits pavement from ground per country), schema.md, style.md (the two-grey invariant) and workstream-a.md.

Keflavík is what an airport should look like: a pale ground from the landcover with the dark aviation pavement — runways, the taxiway network, aprons — drawn on top. Finland and Poland each drew **one dark-grey blob** instead, and Arlanda drew runways floating on a meadow. All three now render like Keflavík. This supersedes !158, which fixed the blobs by deleting geometry: Finland kept runways only (no taxiways, and 5 airfields lost their polygon entirely, needing a widened name-join radius to compensate) and Poland's layer was removed outright. Nothing had to be given up. ## The contract: `surface_class` The shared `airports` layer gains one attribute — 1 = aviation pavement, 2 = the airport ground, drawn pale beneath it. Ground rows are emitted **only where the country's landcover cannot draw them**: Iceland (CORINE) and Finland (CLC 124) already have an `airport` cover class and emit none, Sweden's Topo 10 `mark` and Poland's BDOT10k have no aviation class at all. New style layer `airport_grounds` paints class 2 in `hsl(300, 7%, 84%)` — the *same* grey `cover_industrial` uses for the `airport` class — so an airport does not change colour between countries. ## Finland — the ladder splits pavement from ground exactly This is what issue #110's all-835-areas import missed. Rendered by class, the ladder is unambiguous: | kohdeluokka | n | | |---|---|---| | 32411 / 32412 | 87 / 23 | **pavement** — runways, paved / unpaved | | 32415 | 29 | **pavement** — paved aprons, platforms | | 32417 | 229 | **pavement** — the taxiway network, aprons, hardstands | | 32418 | 38 | **pavement** — unpaved movement area; at Wredeby the grass strip itself | | 32416 | 429 | **ground** — the airfield infield, the grass between the runways | 412 rows imported. The excluded 32416 is left to CLC 124, which covers **222.8 of its 249.6 km²**. The polygons turn out to be a **planar partition** — nothing contains or overlaps anything, they only touch (measured with ST_Contains / ST_Overlaps over the whole airport) — which is exactly why the runway and taxiway outlines were visible *through* the old blob, and why the split is a filter rather than a difference. **The 32416 fallback, 6 polygons.** A pure grass airfield is sometimes surveyed as one 32416 area and nothing else — Mäntsälä, Lapinlahti, Savikko, Torbacka (Wredeby's identical strip is a 32418, the inconsistency that gives the rule away) and two unnamed fields. There the ground *is* the landing surface, and at 6–27 ha it is below CORINE's 25 ha MMU as well, so excluding it erases the airfield rather than moving it to the cover. The rule is "a 32416 whose airfield maps no pavement within 500 units", not a list of five names. Because 32417 is the apron the terminal label stands on, **all 88 register names survive at the original 500-unit join radius** (measured: the 54 lentoasema label points lie 0–120 m, p90 62 m, from the nearest pavement) and `airport_points` goes back to the shared derivation unchanged. !158 needed 1000 and still lost 5 labels. ## Poland — the runways were on the map the whole time KUKO_A alone is a land-use blob: Chopin was one 15.38 km² dark smear with no runway visible, while the runways rendered one layer down as `cover_class = transport`. They live in **OT_PTKM_A**, and splitting them out needs the KUKO polygon as a **spatial mask**, not the RODZAJ column PTKM does not have. The long-standing note in `plmap.yaml` was answering the wrong question. Measured: Chopin's ground holds **1.97 km² of PTKM in 2 polygons**, and both its runway centrelines — built independently from OurAirports thresholds — lie **100% inside them** (computed lengths 2799 / 3686 m against published 2800 / 3690). Nationwide, **40 of the 53** Polish runways with published thresholds are ≥90% inside the clip, **85% of total centreline length**. The 13 misses are almost all military fields BDOT10k redacts (EPMI, EPSN, EPRU, EPML …) — see #157. An intersection restricted to ~140 airport footprints is **not** #118's carve: nothing is subtracted from `cover`, which still maps PTKM to `transport`. What #118 would eventually change is the ground half. ## Sweden — the ground was already in the database `flygplatsomrade` (183 areas) has been staged since #103 for the class ladder's iata/icao codes and never drawn. The 182 areas holding a live runway become the ground; Arlanda stops being three strips on 76.85 km² of `natural_grassland`. **Taxiways remain absent** — Topo 10 surveys nothing between runway and perimeter. That is a source limit, not a filter. ## Iceland Unchanged geometry, now labelled `surface_class` 1 and documented as the contract's target rendering. ## Tests **R141, new** (`docs/country-requirements.md`): pavement is mandatory, ground is optional — a partition holding only ground *is* the blob defect, and every count- and NULL-based check passes while it is broken. Poland additionally pins every pavement polygon inside a ground, so a widened clip cannot paint the national road network in the airports grey. Finland's R6 pins the exact pavement set **and keeps its `count(*) > 0` half**, so an empty source still fails rather than satisfying `0 = 0`. ## Verified * rows: fi 412 pavement, is 67, pl 206 + 301 ground, se 190 + 182 ground * label points: fi **88** (back from the 83 a partition-derived set gave under a runway-only filter), is 67, pl 140, se 48 — all unchanged from before the blobs were touched * decoded tiles carry `surface_class` at z10 and z13 (R121, every config) * `queryRenderedFeatures` in a real headless MapLibre returns class 1 only for is/fi and **both** classes for se/pl — the `["in", attr, N]` filter form matches, and screenshots of all four airports confirm the rendering * largest airports tile 3.4 kB across z10–z13 * `make test`, `config-check` and acceptance all pass: is 34, se 37 (+1 slow not-run), fi 42, pl 38, world 19 Docs updated: the four country files, `country-requirements.md` (R141 + the capability table now splits pavement from ground per country), `schema.md`, `style.md` (the two-grey invariant) and `workstream-a.md`.
Keflavík is what an airport should look like: a pale ground from the landcover
with the dark aviation pavement — runways, taxiways, aprons — on top. Finland
and Poland each drew one dark-grey blob instead, and Arlanda drew runways
floating on a meadow. All three now render like Keflavík.

The shared `airports` contract gains `surface_class`: 1 = pavement, 2 = the
airport ground, drawn pale beneath it. Ground rows are emitted only where the
country's landcover cannot draw it — Iceland (CORINE) and Finland (CLC 124) take
theirs from the `cover` cascade, Sweden and Poland have no aviation landcover
class at all and carry it here. New style layer `airport_grounds` paints class 2
in hsl(300, 7%, 84%), the same grey `cover_industrial` uses for the `airport`
class, so an airport does not change colour between countries.

Finland: the kohdeluokka ladder splits pavement from ground exactly, which is
what issue #110's all-835-areas import missed. 32411/32412 runways, 32415/32417/
32418 taxiway network, aprons, hardstands and overruns → pavement (412 rows);
32416 alone is the grass infield → left to CLC 124, which covers 222.8 of its
249.6 km². Verified by rendering the classes separately at Helsinki-Vantaa,
where the polygons turn out to be a planar partition — nothing contains or
overlaps anything, they only touch, which is why the runway outlines were
visible through the old blob. A 32416 whose airfield maps no pavement of its own
counts as pavement (6 polygons — Mäntsälä, Lapinlahti, Savikko, Torbacka and two
unnamed fields): there the ground IS the landing surface, and at 6–27 ha it is
below CORINE's 25 ha MMU too. Because 32417 is the apron the terminal label
stands on, all 88 register names survive at the original 500-unit join radius and
airport_points goes back to the shared derivation unchanged.

Poland: KUKO_A alone is a land-use blob — Chopin was one 15.38 km² smear with no
runway visible, while the runways were on the map the whole time one layer down,
as cover_class=transport. The runways are in OT_PTKM_A; splitting them out needs
the KUKO polygon as a spatial MASK, not the RODZAJ column PTKM does not have.
Measured: Chopin's ground holds 1.97 km² of PTKM in 2 polygons and both its
runway centrelines (built independently from OurAirports thresholds) lie 100%
inside them; nationwide 40 of the 53 Polish runways with published thresholds are
≥90% inside the clip, 85% of total centreline length. The misses are military
fields BDOT10k redacts (#157). An intersection over ~140 footprints is not
#118's carve — nothing is subtracted from `cover`.

Sweden: flygplatsomrade was already staged for the class ladder's iata/icao codes
and never drawn. 182 of its areas hold a live runway and become the ground.
Taxiways remain absent — Topo 10 surveys nothing between runway and perimeter.

Iceland: unchanged, now labelled surface_class 1 and documented as the target.

R141, new: pavement is mandatory, ground is optional — a partition holding only
ground is the blob defect, and every count- and NULL-based check passes while it
is broken. Poland additionally pins every pavement polygon inside a ground, so a
widened clip cannot paint the national road network in the airports grey.
Finland's R6 pins the exact pavement set and keeps its `count(*) > 0` half.

Verified: 412/67/206+301/190+182 rows, 88/67/140/48 label points (fi back to 88
from the 83 a partition-derived label set gave under the blob-only filter);
decoded tiles carry surface_class at z10 and z13; queryRenderedFeatures in a real
headless MapLibre returns class 1 only for is/fi and both classes for se/pl;
largest airports tile 3.4 kB. make test, config-check and acceptance all pass —
is 34, se 37 (+1 slow not-run), fi 42, pl 38, world 19.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
art merged commit f68b3e1a7a into main 2026-08-18 19:06:50 +00:00
art deleted branch airports-movement-area-split 2026-08-18 19:06:50 +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!159
No description provided.