# #110: Finland airports + airport_points from MTK-muut lentokenttaalue #147

Closed
art wants to merge 0 commits from issue-110-fi-airports into main
Owner

Closes #110


#110: Finland airports + airport_points from MTK-muut lentokenttaalue

What changed

Finland's airports (+ derived airport_points) land from the already-fetched
lentokenttaalue layer in MTK-muut — no fetch change, only staging + two layers,
exactly as the issue scoped it. fi becomes the third per-country tile source to come
back for this pair (se #103, pl #106).

conf/fimap.yaml

  • fi_lentokentta staging — the 835 airport-grounds polygons. kohdeluokka is the
    area-type ladder, per MML's kohdemalli (read from the official 2023-01 kohdemalli
    workbook): 32411/32412 paved/unpaved runways, 32415–32418 other airport / other
    air-traffic areas, paved/unpaved; the delivered counts are in the staging comment.
    paallyste is redundant with the ladder (measured, no NULLs).
  • fi_paikannimi_cased gains 32401 — the traffic-area names ("Liikennealueen nimi",
    airport AND harbour names share the class) — so airport labels go through the shared
    R94 display-casing pass, the one copy of the rule. The hyphen-rule's known limit
    gains the airport genitive case, documented: HELSINKI-VANTAAN LENTOASEMA →
    Helsinki-vantaan lentoasema (the register's own cased convention, cf. its cased twin
    Kiuruveden lentokenttä). The pass also marks regnames AS MATERIALIZED so the
    hyphen-rule probes a materialised set instead of re-scanning the register per part.
  • airports layer (z10–13, filter_only): all 835 areas — runways plus grounds, the
    whole-complex shape Poland's KU* areas give. Names via the register join (R92):
    32401 label point within 500 units, Finnish preferred, biggest font first, longer
    name on a font tie (Kokkola-Pietarsaari's two same-font rows — the truncated
    KRUUNUPYYN LENTOASEMA ELI fragment loses to the full name). airport_class is the
    register's own airport tier
    — LENTOASEMA → 1, LENTOKENTTÄ/KEVYTLENTOPAIKKA → 2 —
    the Finnish analogue of Poland's TYPLOTNISKA port-lotniczy ladder, because MTK's
    kohdeluokka does not carry a scheduled/other tier (a paved runway is a kohdeluokka,
    not a traffic class; measured: every named airport has exactly one runway area).
    26 lentoasema airports = Finland's 21 scheduled-service airports + the military
    bases (Halli, Utti) + Malmi, Mikkeli, Varkaus; 62 airfields class 2.
  • airport_points (z8–13, filter_only, z9 keeps class 1): one centroid per named
    airport (R140, reads fi_airports_13), 88 labels.
  • sources: [fi] — the config's first own source (it never had one; every Finland
    layer was shared until now).
  • Acceptance tests: R6 populated, R119 per-zoom no-empty, R120 no-NULL-class /
    no-invalid / class-1-exactly-lentoasema, on both layers, plus an R94 casing test
    on airport_points. R105 staging list grows to 17.

importer/staging.go — ANALYZE after staging builds. The cased rebuild exposed a
real landmine: a query-kind staging table reading a just-loaded load-kind table plans
on NO statistics (bulk-loaded tables have none until autoanalyze fires a minute
later), and the planner's no-stats guess for fi_paikannimi_cased was a nested loop
over the 1.17M-row register — 551k × 308k lower() comparisons, measured: the build
ran over an hour; the same query runs ~5 s once ANALYZEd
. Both staging build paths
now end with ANALYZE, so every downstream query plans on real stats. This would have
hit any full import-fi (and #113's original build); documented in the code comment
and docs/architecture.md.

conf/mapsrv.yaml — sources: [map, is, fi]. html/style.json — fi source +
airports_fi + airport_labels_fi (the same two blocks se/pl got). Docs:
country-finland.md (catalogue line, known gaps), style.md, workstream-a.md,
country-requirements.md R13, unification.md, architecture.md.

Verification

  • make build, make test — ok.
  • importer/importer import --layer airports,airport_points conf/fimap.yaml — 22 s
    (cased staging rebuild now ~6 s), all layer acceptance tests pass.
  • make acceptance — every config rc 0. fimap tally:
    acceptance: 40 test(s): 40 passed, 0 failed, 0 skipped, 0 not run
  • Live numbers: fi_airports_13 835 rows (107 class-1 / 728 class-2, 345 named /
    490 unnamed); fi_airport_points_13 88 rows (26 class-1 / 62 class-2); z8/z9 hold
    the 26 lentoasema airports. Every register airport name is on the map except
    GENBÖLEN (13 km from any grounds — the survey names an airfield it does not map as
    airport area) and KRUUNUPYYN LENTOASEMA ELI (a second row for Kokkola-Pietarsaari,
    merged by the tie-break).
  • importer/importer tiles --check conf/fimap.yaml — R121: airports z10/z13 PASS,
    airport_points z8/z13 PASS, keys [name airport_class country].
  • Real-browser CDP render (never virtual time): z10 over Helsinki-Vantaa renders 77
    airports_fi polygons + the label "Helsinki-vantaan lentoasema"; z8/z9 render only
    class-1 labels (Jyväskylän lentoasema at the cursor), class-2 labels never appear
    below z10. Qwen2.5-VL corroborates the z10 screenshot (grey grounds + label).
    The only console error is the page's pre-existing /favicon.ico 404.

Known limits / left undone

  • Helsinki-vantaan lentoasema (and the other hyphenated town-genitive airport
    names: Tampere-pirkkalan, Kemi-tornion, …) lose the second town's capital — the
    register stores these in caps with no cased twin, and the shared R94 rule's
    first-letter fallback is the register's own cased convention (Kiuruveden
    lentokenttä). Documented on the casing pass; a register-membership test against
    inflected genitives would need a stemmer and is deliberately out of scope.
  • airports is a standalone layer drawn over the cover's transport fill, exactly like
    #106 — folding it into cover itself is #118's overlay-carve staging kind (still
    open).
  • The airport_class tier is the register's LENTOASEMA/LENTOKENTTÄ vocabulary, not a
    scheduled-service ledger: Halli, Utti, Malmi, Mikkeli and Varkaus are class 1 though
    they have no scheduled traffic (the same over-inclusion Sweden's iata and Poland's
    port-lotniczy signals carry). Traficom's airport register would be the exact signal,
    and is not on disk.

Merge notes

  • Branch is off current main; the R13/style.md/unification.md edits overlap #103's and
    #106's open PRs (each added the same sentences for its own country) — the final text
    names all three and is what the merged tree should say; git will need a small
    manual resolution there.
  • The DB now holds the rebuilt fi_paikannimi_cased (with 32401) and the new layer
    tables; nothing else was touched (places/stations/water_bodies read the cased pass
    filtered by their own kohdeluokka and are unchanged — their acceptance tests pass).
Closes #110 --- # #110: Finland airports + airport_points from MTK-muut lentokenttaalue ## What changed Finland's `airports` (+ derived `airport_points`) land from the already-fetched `lentokenttaalue` layer in `MTK-muut` — no fetch change, only staging + two layers, exactly as the issue scoped it. `fi` becomes the third per-country tile source to come back for this pair (`se` #103, `pl` #106). **conf/fimap.yaml** - `fi_lentokentta` staging — the 835 airport-grounds polygons. kohdeluokka is the area-type ladder, per MML's kohdemalli (read from the official 2023-01 kohdemalli workbook): 32411/32412 paved/unpaved runways, 32415–32418 other airport / other air-traffic areas, paved/unpaved; the delivered counts are in the staging comment. `paallyste` is redundant with the ladder (measured, no NULLs). - `fi_paikannimi_cased` gains 32401 — the traffic-area names ("Liikennealueen nimi", airport AND harbour names share the class) — so airport labels go through the shared R94 display-casing pass, the one copy of the rule. The hyphen-rule's known limit gains the airport genitive case, documented: HELSINKI-VANTAAN LENTOASEMA → Helsinki-vantaan lentoasema (the register's own cased convention, cf. its cased twin Kiuruveden lentokenttä). The pass also marks `regnames AS MATERIALIZED` so the hyphen-rule probes a materialised set instead of re-scanning the register per part. - `airports` layer (z10–13, filter_only): all 835 areas — runways plus grounds, the whole-complex shape Poland's KU* areas give. Names via the register join (R92): 32401 label point within 500 units, Finnish preferred, biggest font first, longer name on a font tie (Kokkola-Pietarsaari's two same-font rows — the truncated KRUUNUPYYN LENTOASEMA ELI fragment loses to the full name). **airport_class is the register's own airport tier** — LENTOASEMA → 1, LENTOKENTTÄ/KEVYTLENTOPAIKKA → 2 — the Finnish analogue of Poland's TYPLOTNISKA port-lotniczy ladder, because MTK's kohdeluokka does not carry a scheduled/other tier (a paved runway is a kohdeluokka, not a traffic class; measured: every named airport has exactly one runway area). 26 lentoasema airports = Finland's 21 scheduled-service airports + the military bases (Halli, Utti) + Malmi, Mikkeli, Varkaus; 62 airfields class 2. - `airport_points` (z8–13, filter_only, z9 keeps class 1): one centroid per named airport (R140, reads fi_airports_13), 88 labels. - `sources: [fi]` — the config's first own source (it never had one; every Finland layer was shared until now). - Acceptance tests: R6 populated, R119 per-zoom no-empty, R120 no-NULL-class / no-invalid / class-1-exactly-lentoasema, on both layers, plus an R94 casing test on airport_points. R105 staging list grows to 17. **importer/staging.go — ANALYZE after staging builds.** The cased rebuild exposed a real landmine: a query-kind staging table reading a just-loaded load-kind table plans on NO statistics (bulk-loaded tables have none until autoanalyze fires a minute later), and the planner's no-stats guess for `fi_paikannimi_cased` was a nested loop over the 1.17M-row register — 551k × 308k lower() comparisons, measured: **the build ran over an hour; the same query runs ~5 s once ANALYZEd**. Both staging build paths now end with `ANALYZE`, so every downstream query plans on real stats. This would have hit any full `import-fi` (and #113's original build); documented in the code comment and docs/architecture.md. **conf/mapsrv.yaml** — `sources: [map, is, fi]`. **html/style.json** — `fi` source + `airports_fi` + `airport_labels_fi` (the same two blocks `se`/`pl` got). Docs: country-finland.md (catalogue line, known gaps), style.md, workstream-a.md, country-requirements.md R13, unification.md, architecture.md. ## Verification - `make build`, `make test` — ok. - `importer/importer import --layer airports,airport_points conf/fimap.yaml` — 22 s (cased staging rebuild now ~6 s), all layer acceptance tests pass. - `make acceptance` — every config rc 0. fimap tally: `acceptance: 40 test(s): 40 passed, 0 failed, 0 skipped, 0 not run` - Live numbers: `fi_airports_13` 835 rows (107 class-1 / 728 class-2, 345 named / 490 unnamed); `fi_airport_points_13` 88 rows (26 class-1 / 62 class-2); z8/z9 hold the 26 lentoasema airports. Every register airport name is on the map except GENBÖLEN (13 km from any grounds — the survey names an airfield it does not map as airport area) and KRUUNUPYYN LENTOASEMA ELI (a second row for Kokkola-Pietarsaari, merged by the tie-break). - `importer/importer tiles --check conf/fimap.yaml` — R121: `airports` z10/z13 PASS, `airport_points` z8/z13 PASS, keys [name airport_class country]. - Real-browser CDP render (never virtual time): z10 over Helsinki-Vantaa renders 77 `airports_fi` polygons + the label "Helsinki-vantaan lentoasema"; z8/z9 render only class-1 labels (Jyväskylän lentoasema at the cursor), class-2 labels never appear below z10. Qwen2.5-VL corroborates the z10 screenshot (grey grounds + label). The only console error is the page's pre-existing `/favicon.ico` 404. ## Known limits / left undone - **Helsinki-vantaan lentoasema** (and the other hyphenated town-genitive airport names: Tampere-pirkkalan, Kemi-tornion, …) lose the second town's capital — the register stores these in caps with no cased twin, and the shared R94 rule's first-letter fallback is the register's own cased convention (Kiuruveden lentokenttä). Documented on the casing pass; a register-membership test against inflected genitives would need a stemmer and is deliberately out of scope. - `airports` is a standalone layer drawn over the cover's transport fill, exactly like #106 — folding it into `cover` itself is #118's overlay-carve staging kind (still open). - The `airport_class` tier is the register's LENTOASEMA/LENTOKENTTÄ vocabulary, not a scheduled-service ledger: Halli, Utti, Malmi, Mikkeli and Varkaus are class 1 though they have no scheduled traffic (the same over-inclusion Sweden's iata and Poland's port-lotniczy signals carry). Traficom's airport register would be the exact signal, and is not on disk. ## Merge notes - Branch is off current main; the R13/style.md/unification.md edits overlap #103's and #106's open PRs (each added the same sentences for its own country) — the final text names all three and is what the merged tree should say; git will need a small manual resolution there. - The DB now holds the rebuilt `fi_paikannimi_cased` (with 32401) and the new layer tables; nothing else was touched (places/stations/water_bodies read the cased pass filtered by their own kohdeluokka and are unchanged — their acceptance tests pass).
Finland's airports from the already-fetched `lentokenttaalue` layer in MTK-muut
(835 grounds polygons, no fetch change). The `fi` tile source appears for the
first time — every Finland layer was shared until now — with the same pair that
brought `se` back in #103 and `pl` in #106.

The airports layer draws all 835 areas (runways + grounds, the whole-complex
shape Poland's KU* gives). kohdeluokka is the area-type ladder (32411/32412
paved/unpaved runways, 32415-32418 other airport / other air-traffic areas), but
it does not carry a scheduled/other tier — a paved runway is a kohdeluokka, not
a traffic class, and every named airport has exactly one runway area (measured;
Joensuu has both). The tier comes from the register's 32401 traffic-area names:
the survey prints its airports as LENTOASEMA and its airfields as LENTOKENTTÄ /
KEVYTLENTOPAIKKA — the Finnish analogue of Poland's port-lotniczy ladder — so
airport_class 1 = the register names it lentoasema (26 airports: all 21
scheduled-service airports plus Halli, Utti, Malmi, Mikkeli, Varkaus), else 2.

Names are the register join (R92): 32401 label within 500 units (the terminal
sits on a building footprint or a grounds hole — measured 0-127 m outside the
nearest area, so strict containment would leave Helsinki-Vantaa and the big
airports unnamed), Finnish preferred, biggest font first, longer name on a font
tie (Kokkola-Pietarsaari's KRUUNUPYYN LENTOASEMA ELI fragment loses to the full
name). The 32401 class also carries harbour names; the join filters the name
text to the airport vocabulary. 32401 joins the shared R94 casing pass so the
labels come out cased (one copy of the rule); the register has no cased twin for
the big airports, so the hyphenated genitives render first-letter-cased
(Helsinki-vantaan lentoasema) — the register's own convention, documented.
GENBÖLEN LENTOKENTTÄ is the one register name 13 km from any grounds (source
gap). airport_points derives per R140: one centroid per named airport, z9 keeps
class 1, 88 labels.

Importer fix this exposed: staging builds now end with ANALYZE. A query-kind
staging table reading a just-loaded load-kind table plans on no statistics
(autoanalyze fires a minute after a bulk load), and the planner's no-stats guess
for the fi_paikannimi_cased rebuild was a nested loop over the 1.17M-row
register — 551k x 308k lower() comparisons, an hour-long build vs ~5 s once
analyzed. This would have hit any full import-fi, not just this layer's rebuild.

Verification: make build/test; --layer import 22 s with all layer acceptance
tests green; make acceptance rc 0 across all configs (fimap 40/40); tile-check
R121 PASS on both layers; CDP render over Helsinki-Vantaa shows 77 polygons +
"Helsinki-vantaan lentoasema" at z10 and only class-1 labels at z8/z9, Qwen2.5-VL
corroborates. Acceptance tests added: R6 populated, R119 per-zoom, R120 no-NULL
class / no-invalid / class-1-exactly-lentoasema, R94 casing.
No change to Finland's output (measured: 88 points, 26 class 1, before and
after) — the derivation is made right for the sources that will exercise it,
and the comment that got the reasoning wrong is corrected.

* `min(airport_class)`, not `max`. The ladder is 1 = scheduled, 2 = everything
  else, so max() demotes an airport to the tier of its weakest polygon. The old
  comment claimed the derivation "takes max(airport_class), so the label tier
  comes out right" — that is not why it came out right. Finland's class is a
  pure function of `name` and the grouping is by name, so every row in a group
  already agrees and the aggregate cannot change the answer. Poland's BDOT10k
  does disagree with itself, and there max() drops Wrocław-Strachowice and
  Lublin off the z9 filter.

* Group by (name, spatial cluster), the R91 idiom places uses, not by name
  alone. eps 5 000 web-Mercator units; Finland's grounds areas touch (23 units
  apart at closest approach, the tightest of the four countries), so nothing
  splits here.
Author
Owner

Merged through !154

Merged through !154
art closed this pull request 2026-08-17 22:10:33 +00:00

Pull request closed

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!147
No description provided.