Closes #101 #202

Merged
art-bot merged 2 commits from issue-101-se-contours-peaks into main 2026-08-20 22:51:53 +00:00
Owner

Closes #101


Sweden contours + peaks from Topo 10 Höjd (issue #101)

What changed

Sweden's contours and peaks from Lantmäteriet Topo 10, tema Höjd, landing into
the shared contours/peaks contracts (issues #151/#152) as the se partitions of
map_contours_<z> / map_peaks_<z>, side by side with Iceland's. The layers extends:
the contracts, so the zoom range, attribute set and strategy are inherited; the
zoom_filters cascade — the per-source cadence — stays in the se config, exactly as the
contracts intend.

  • conf/se/downloads.yaml — the Höjd theme via a new topo10-hojd geotorget order
    (a separate entry for the same reason as topo10-text/topo10-byggnadsverk: a
    rotating delivery id must not force a re-download of the whole topo10 order). Plus the
    se_hojd_t10 databases: entry. R5, recorded on first fetch: hojd_sverige.zip is
    2 506 916 311 bytes zipped / 9 134 403 584 bytes on disk.
  • conf/se/staging.yaml — two staged feature layers, streamed like the buildings
    theme: se_hojdlinje_t10 (5 576 429 lines, Höjdkurva5 2401 + Gropkurva5 2402 depression
    contours, 5 m ekvidistans) and se_hojdpunkt_t10 (33 783 spot heights).
  • conf/se/16-contours.yaml — the se contours partition.
  • conf/se/17-peaks.yaml — the se peaks partition.
  • importer/configdir_test.go, importer/geotorget_test.go — layer-set / geotorget-order
    expectations updated for the two new layers and the fifth order.
  • docs/ — country-sweden.md records the R5 sizes and that contours/peaks are no
    longer Iceland-only; country-requirements.md inventory flips the se column to ✅ for
    both.

The interval ladder and the dense-end cascade

The contract pins interval_class = 1 as the index/major line. Lantmäteriet marks
exactly that with stodkurva='Ja' (every fifth line, 25 m) — so stodkurva → 1, the
remaining 5 m lines → rung 2. The source carries no finer sub-ladder, so Sweden's ladder
is {1, 2} (the contract allows a country with no finer rung to never emit one).

Sweden is the dense end: 5.58M lines, ~40× Iceland's 139k, so the cascade thins
harder and earlier. The "serve z12+ for the fine lines" guess from the issue is answered
by measurement — the fine 5 m rung is z14-only (5.58M lines cannot be served at z13
without swamping a tile), the 25 m index at z13 (1.1M), the 100 m index at z12 (239k),
and a 500 m floor at z11/z10 (27k) — the same coarse floor Iceland uses.

Peaks are elevation markers (no name, per contract), thinning z13 ≥200 m, z11 ≥500 m
(1 987 high summits at the coarse floor) — the same 500 m coarse floor as Iceland.

Verification (live DB)

Row counts per zoom — match the design:

contours: z10=26948  z11=26948  z12=239546  z13=1097249  z14=5576429
peaks:    z11=1987   z12=13403   z13=13403   z14=33783

(The small z13/z12 drops from 1 097 264/239 551 are simplify_line's length floor — 15 and
5 sub-pixel contour segments.)

NULLs that matter = 0 (elevation/interval_class); every contour is a 1/2 rung;
both _is and _se partitions coexist at every zoom in the shared parents (10 partitions
contours, 8 peaks). Decoded tiles over the Swedish mountains: z13 contours (14 features),
z14 contours (366 features, 46.8 kB — the price of 5 m detail on a mountain tile), z11
peaks (11 features); the tiles carry elevation + interval_class + country properties,
the keys the style's index stroke and labels read.

Acceptance tally (make acceptance over the se config — all contours/peaks tests):

layer contours (se) / R120: the Sweden contours' index rung is exactly the stodkurva='Ja' set — PASS  (index_lines=1097264, source_index=1097264)
layer contours (se) / R31: the z11 floor holds only 500 m index lines — PASS  (lines=26948, not_500=0)
layer peaks (se) / R120: every Sweden peak made it through, with a valid elevation — PASS  (peaks=33783, source_points=33783, null_elevation=0)
layer peaks (se) / R31: the z11 floor holds only the ≥500 m summits — PASS  (peaks=1987, below_500=0)
shared layer contours / R119: no contours partition empty z10–z14 — PASS  (partitions=10)
shared layer contours / R120: valid elevation + positive interval_class — PASS  (countries=2, offenders=<nil>)
shared layer peaks / R119: no peaks partition empty z11–z14 — PASS  (partitions=8)
shared layer peaks / R120: every country's peaks carry a valid elevation — PASS  (countries=2)

Notes for the reviewer

  • Branching note. The shared contours/peaks contracts live on unmerged branches
    (PRs #198/#199). This branch is based on main with those two commits cherry-picked on
    top (their docs/schema/style conflicts resolved to the post-both state), then the se
    work on top. Merge #198/#199 first; the cherry-picked commits then drop out on rebase.
  • Pre-existing make acceptance failure (not mine): a Poland buildings R120 test
    (sum(ST_NumGeometries) over PL's ~9M footprints) times out at 10s. Reproduces in
    isolation on conf/pl, untouched by this change. Flagging rather than silently hiding.
  • conf/is/downloads.yaml still lists layers: [contours] on the is source — a
    leftover from #151/#152 landing in parallel (contours is now shared, so zxy_is serving
    it is redundant with zxy_map). Left out of this se-focused PR; worth a one-line cleanup
    once #198/#199 merge.
  • Sweden–Finland border match (#84): the Tornio–Haparanda contour-agreement check
    becomes possible once Finland's contours land (#107).
  • z14 mountain tiles are ~46 kB (366 contour lines) — the 5 m product's cost. Noted,
    not changed; tune the fine rung if tile weight matters.
Closes #101 --- # Sweden contours + peaks from Topo 10 Höjd (issue #101) ## What changed Sweden's `contours` and `peaks` from Lantmäteriet Topo 10, tema **Höjd**, landing into the **shared** `contours`/`peaks` contracts (issues #151/#152) as the `se` partitions of `map_contours_<z>` / `map_peaks_<z>`, side by side with Iceland's. The layers `extends:` the contracts, so the zoom range, attribute set and strategy are inherited; the `zoom_filters` cascade — the per-source cadence — stays in the se config, exactly as the contracts intend. - **`conf/se/downloads.yaml`** — the Höjd theme via a new `topo10-hojd` geotorget order (a separate entry for the same reason as `topo10-text`/`topo10-byggnadsverk`: a rotating delivery id must not force a re-download of the whole topo10 order). Plus the `se_hojd_t10` `databases:` entry. **R5, recorded on first fetch:** `hojd_sverige.zip` is 2 506 916 311 bytes zipped / 9 134 403 584 bytes on disk. - **`conf/se/staging.yaml`** — two staged feature layers, streamed like the buildings theme: `se_hojdlinje_t10` (5 576 429 lines, Höjdkurva5 2401 + Gropkurva5 2402 depression contours, 5 m ekvidistans) and `se_hojdpunkt_t10` (33 783 spot heights). - **`conf/se/16-contours.yaml`** — the se contours partition. - **`conf/se/17-peaks.yaml`** — the se peaks partition. - **`importer/configdir_test.go`, `importer/geotorget_test.go`** — layer-set / geotorget-order expectations updated for the two new layers and the fifth order. - **`docs/`** — `country-sweden.md` records the R5 sizes and that contours/peaks are no longer Iceland-only; `country-requirements.md` inventory flips the `se` column to ✅ for both. ### The interval ladder and the dense-end cascade The contract pins `interval_class = 1` as the index/major line. Lantmäteriet marks exactly that with `stodkurva='Ja'` (every fifth line, 25 m) — so **stodkurva → 1**, the remaining 5 m lines → rung 2. The source carries no finer sub-ladder, so Sweden's ladder is `{1, 2}` (the contract allows a country with no finer rung to never emit one). Sweden is the **dense end**: 5.58M lines, ~40× Iceland's 139k, so the cascade thins harder and earlier. The "serve z12+ for the fine lines" guess from the issue is answered by measurement — the fine 5 m rung is **z14-only** (5.58M lines cannot be served at z13 without swamping a tile), the 25 m index at z13 (1.1M), the 100 m index at z12 (239k), and a **500 m floor** at z11/z10 (27k) — the same coarse floor Iceland uses. Peaks are elevation markers (no `name`, per contract), thinning z13 ≥200 m, z11 ≥500 m (1 987 high summits at the coarse floor) — the same 500 m coarse floor as Iceland. ## Verification (live DB) **Row counts per zoom — match the design:** ``` contours: z10=26948 z11=26948 z12=239546 z13=1097249 z14=5576429 peaks: z11=1987 z12=13403 z13=13403 z14=33783 ``` (The small z13/z12 drops from 1 097 264/239 551 are simplify_line's length floor — 15 and 5 sub-pixel contour segments.) **NULLs that matter = 0** (`elevation`/`interval_class`); every contour is a 1/2 rung; both `_is` and `_se` partitions coexist at every zoom in the shared parents (10 partitions contours, 8 peaks). Decoded tiles over the Swedish mountains: z13 contours (14 features), z14 contours (366 features, 46.8 kB — the price of 5 m detail on a mountain tile), z11 peaks (11 features); the tiles carry `elevation` + `interval_class` + `country` properties, the keys the style's index stroke and labels read. **Acceptance tally** (`make acceptance` over the se config — all contours/peaks tests): ``` layer contours (se) / R120: the Sweden contours' index rung is exactly the stodkurva='Ja' set — PASS (index_lines=1097264, source_index=1097264) layer contours (se) / R31: the z11 floor holds only 500 m index lines — PASS (lines=26948, not_500=0) layer peaks (se) / R120: every Sweden peak made it through, with a valid elevation — PASS (peaks=33783, source_points=33783, null_elevation=0) layer peaks (se) / R31: the z11 floor holds only the ≥500 m summits — PASS (peaks=1987, below_500=0) shared layer contours / R119: no contours partition empty z10–z14 — PASS (partitions=10) shared layer contours / R120: valid elevation + positive interval_class — PASS (countries=2, offenders=<nil>) shared layer peaks / R119: no peaks partition empty z11–z14 — PASS (partitions=8) shared layer peaks / R120: every country's peaks carry a valid elevation — PASS (countries=2) ``` ## Notes for the reviewer - **Branching note.** The shared `contours`/`peaks` contracts live on unmerged branches (PRs #198/#199). This branch is based on main with those two commits cherry-picked on top (their docs/schema/style conflicts resolved to the post-both state), then the se work on top. Merge #198/#199 first; the cherry-picked commits then drop out on rebase. - **Pre-existing `make acceptance` failure (not mine):** a Poland `buildings` R120 test (`sum(ST_NumGeometries)` over PL's ~9M footprints) times out at 10s. Reproduces in isolation on conf/pl, untouched by this change. Flagging rather than silently hiding. - **`conf/is/downloads.yaml` still lists `layers: [contours]`** on the `is` source — a leftover from #151/#152 landing in parallel (contours is now shared, so `zxy_is` serving it is redundant with `zxy_map`). Left out of this se-focused PR; worth a one-line cleanup once #198/#199 merge. - **Sweden–Finland border match (#84):** the Tornio–Haparanda contour-agreement check becomes possible once Finland's contours land (#107). - **z14 mountain tiles are ~46 kB** (366 contour lines) — the 5 m product's cost. Noted, not changed; tune the fine rung if tile weight matters.
Iceland's contours were the last terrain layer still per-country: conf/is/07-contours.yaml
built ismap_contours_<z> and the style read them via the `is` source. This lands the
shared-contours contract and folds Iceland into it, the same mechanism airports (#149) and
buildings (#150) used.

- conf/shared/contracts/18-contours.yaml (new): the `contours` contract — stop_z 10,
  maxz 14, simplify_line, attrs [geom, elevation, interval_class] — with the
  cross-country acceptance tests (R119 no-empty-partition over the catalogue, R120
  valid elevation / positive interval_class). The attribute set stays source-agnostic
  by convention rather than rename: `interval_class = 1` is the index/major line for
  every source (what the style's `== 1` filters and labels key on), the values above
  are each source's intermediates. A binary index flag would collapse Iceland's
  three-rung cascade (the z12 filter drops only the fine 10 m rung below z13), so the
  contract carries a multi-valued ladder. #101/#104/#107 map their index markers into
  1 when they land.
- conf/is/07-contours.yaml: extends: contours; scaffold inherited, zoom_filters stay
  (the per-source thinning). R119/R120 moved to the contract; a new R31 test holds
  Iceland's 500 m elevation ladder at z10.
- conf/world.yaml: contours added to the `map` source layer list so zxy_map serves it.
- html/style.yaml: the three contour layers switch source is -> map; `is` survives for
  peaks (#152 removes it entirely).
- docs updated (R13, layer inventory, schema, style, country-iceland): peaks is now the
  only Iceland-only layer.

Verified live: reimported the layer and regenerated zxy_map. Row counts per zoom match
the cascade exactly (139690/139690/138111/24873/4064), zero NULL elevation or
interval_class, all five partitions present, tiles --check decodes zxy_map_contours
with keys [elevation interval_class country]. conf/is acceptance: 40/40 passed
including the new R31/R119/R120. (The pl buildings R120 10s-timeout failure is
pre-existing on main and unrelated.)
peaks is no longer Iceland-only: a new conf/shared/contracts/19-peaks.yaml
contract (stop_z 11 / maxz 14 / filter_only, attrs geom+elevation) carries the
R119 no-empty-partition and R120 no-NULL-elevation checks, and
conf/is/08-peaks.yaml now `extends: peaks` so it builds map_peaks_<z>_is
partitions instead of ismap_peaks_<z>. The style's peaks symbol layer reads the
shared `map` source; the `is` source's layer list drops to [contours]; world.yaml
adds peaks to the map source. The elevation ladder (zoom_filters) stays
Iceland-specific, with a new R31 test asserting the z11 coarse floor holds only
the >=500 m summits.

The attribute set is deliberately elevation-only (no `name`): that is the spot-height
shape Iceland, Sweden (#101) and Poland (#104) fill, and Finland's source (#108) is
still undecided between DEM spot heights and named fells — the contract does not
gamble on a name only one option needs. The contract is numbered 19 rather than 18 so
it does not collide with #151's in-flight 18-contours.yaml.

main; the `is` source block is therefore kept — whoever of #198/#152 lands second
should retire it.

Verified against the live DB before commit: build, test, config-check (18 contracts);
reimported peaks (no re-download); shared partitions z11–z14 byte-identical in row
count to the old ismap_peaks (5678/8936/8936/10156), 0 NULL elevations, gist indexes
present; decoded tiles carry peaks with elevation; gl-style-validate passes. Full
acceptance: the peaks R31/R119/R120 and config-level checks all pass (11/11 on the
peaks import). One unrelated pre-existing flake: conf/pl buildings R120 times out at
10s under load (6.2s in isolation; reads only Poland buildings, untouched here).
Sweden's `contours` and `peaks` from Lantmäteriet Topo 10, tema Höjd, land into the
shared `contours`/`peaks` contracts (conf/shared/contracts/18- and 19-.yaml, issues
#151/#152) as the se partitions of map_contours_<z> / map_peaks_<z>, alongside
Iceland's.

Source and download (R5): hojd_sverige.zip, 2 506 916 311 bytes zipped / 9 134 403
584 bytes on disk (recorded on first fetch in docs/country-sweden.md). Its own
`topo10-hojd` geotorget entry — a separate order, the same pattern as topo10-text
and topo10-byggnadsverk, so a rotating delivery id cannot force a re-download of the
whole order. Two staged feature layers: `hojdlinje` (5 576 429 lines, Höjdkurva5 2401
+ Gropkurva5 2402 depression contours, 5 m ekvidistans) and `hojdpunkt` (33 783 spot
heights).

contours: the contract's interval_class = 1 index rung is stodkurva='Ja' (every fifth
line, 25 m); the remaining 5 m lines are rung 2. The 5 m set is the dense end (~40x
Iceland), so the cascade thins harder and earlier than Iceland's: z14 the full 5.58M,
z13 the 25 m index (1.1M), z12 the 100 m index (239k), z11/z10 a 500 m floor (27k).
peaks: elevation markers (no name), thinning z13 ≥200 m, z11 ≥500 m — the same coarse
floor as Iceland, so both countries' peaks read as their mountain chains.

Acceptance: four se-specific tests (the index-rung == stodkurva='Ja' mapping pin, the
z11 500 m floors for both layers) plus the shared contracts' R119/R120 now run over
both countries. Verified against the live DB: per-zoom row counts match the design,
zero NULLs in the columns the style reads, se + is partitions coexist at every zoom,
decoded z13/z14 contours and z11 peaks tiles carry elevation + interval_class.

Also: configdir_test.go and geotorget_test.go updated for the new layer/order; docs
record contours+peaks as no longer Iceland-only (R13 gap closed for se).

Note: `make acceptance` shows one pre-existing, unrelated failure — a Poland buildings
R120 test timing out at 10s (sum(ST_NumGeometries) over PL's ~9M footprints), present
on main and untouched by this change.

The Sweden–Finland contour agreement check at Tornio–Haparanda becomes possible once
Finland's contours land (#107).
art force-pushed issue-101-se-contours-peaks from fa01f58b37 to 5af667becc 2026-08-20 21:57:31 +00:00 Compare
The import itself verifies: 5 576 429 staged contour lines land as 5 576 429 rows
in map_contours_14_se (ST_Dump adds no parts), 1 097 264 of them interval_class 1
against 1 097 264 stodkurva='Ja', thinning 1 097 249 / 239 546 / 26 948 / 26 948
down the cascade; peaks 33 783 / 13 403 / 13 403 / 1 987 with elevations 1–1797.
Every number in the PR body reproduced exactly. Four changes on top:

- The Sweden contours R120 was `timeout: 10` and took 7.7 s in a `make acceptance`
  run — and 9.1 s in `make acceptance-all`, which would have failed the cap on a
  slightly busier box. Both halves are seq scans over 5.58M rows (0.8 s + 2.4 s
  warm); no index answers either predicate and adding one for a test is not worth
  it. Moved to `timeout: 100`, which per importer/acceptance.go runsIn also moves
  it out of the default run into `make acceptance-all` — a deliberate trade, and
  the right home: the claim it pins is the stodkurva→interval_class source
  mapping, which changes when the Höjd theme is re-fetched, not when the layer is
  rebuilt. The contract's own R120 (positive interval_class, 2.2 s) still runs
  every time. Same tier as the other million-row checks in conf/pl/15-buildings,
  conf/se/03-cover and conf/world.
- se_hojdpunkt_t10.hojdvarde loads as bigint, and the peaks query passed it
  through uncast to the contract's int column, relying on the assignment cast.
  Harmless at 1–1797 m, but the contours query next door casts explicitly; made
  peaks match and recorded the gpkg-vs-Postgres type in staging.yaml.
- 16-contours.yaml and 17-peaks.yaml had no trailing newline; 17-peaks said
  33 787 spot heights in one line of a comment that says 33 783 everywhere else.

Docs: this is the first country other than Iceland to draw either layer, so the
claims that terrain is Iceland-only needed retiring, not just the Sweden
catalogue's own entry — country-iceland.md ("no other country has terrain data
yet", written when #152 merged an hour ago), geo-primer's draw-order preamble and
its per-country zoom table (which had no `peaks` row at all), country-sweden's
section heading, and R13's non-conformance row 11, which gets a **Resolved** note
in the style rows 6 and 10 already use: nothing on that line is Iceland-only now,
and what remains is Poland (#104) and Finland (#107/#108) owing partitions.

Verified: acceptance conf/se 53/53 in the default tier and 54/54 under
-acceptance all; both shared contracts now report two countries (contours
partitions=10, peaks=8); tiles --check decodes both from zxy_map_* with the
declared keys; make test and gl-style-validate pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2QYwuohkM9vWJs6uzsK58
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!202
No description provided.