#38: Poland's transit will not merge: the tram network is 73% junction nodes #57
No reviewers
Labels
No labels
claude-wip
map-report
map:is
map:se
needs-review
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
art/ismap!57
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "night/issue-38-transit-merge"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #38
Issue #38 — Poland's
transitwill not merge: the tram network is 73% junction nodesStatus: done — branch
night/issue-38-transit-merge(3 commits), regeneralizedtransitz9–z13 for SE, PL and FI against the live DB, verified per the repo chain.Review round: Finland's partition had the same bug and now carries a measured
parallel_merge_pixels(0.5 px) of its own; the Sweden comment now describes thewidening corridor, not just z13.
The mechanism
ST_LineMergechains only through degree-2 nodes. BDOT10k SKTR (PL) models each trackseparately and splits it at every crossover and switch, so 73% of Poland's tram nodes
are junctions (degree 3+) against 30% in Sweden's Topo 10 — the merge had almost
nothing to chain. The fix is
parallel_merge_pixels, the same knob that collapseddouble track for
railways: most degree-3 nodes are crossovers between the two tracksof one line, so dropping the second track of a parallel pair turns them into
degree-2 nodes and the next cascade level chains straight through.
The issue's expected obstacle does not exist. It asked whether
parallel_prioritycould be made optional defaulting to longest-wins — the code already does exactly
that: the rank falls back to
(-ST_Length, ST_XMin, ST_YMin)when the column isunset, and
validateMergenever required it (it has been this way since the railwayscommit
e6abf49, 2026-08-01). The docs implied it was required; the change makes thecontract explicit in
importer/generalize.go,importer/layer.go,docs/generalization.md, and locks it withTestParallelPriorityOptional. Nogeneration-code change was needed —
transithas onlymode, and longest-wins isexactly what it wants.
What changed
conf/plmap.yaml: transit getsparallel_merge_pixels: 1(full railway-strength drop).conf/semap.yaml: transit getsparallel_merge_pixels: 0.25— deliberately small,see Sweden below.
conf/fimap.yaml: transit getsparallel_merge_pixels: 0.5— measured mid-size, seeFinland below.
importer/generalize.go+layer.go: comment the optional-parallel_prioritycontract (empty = longest-wins).
importer/layer_test.go:TestParallelPriorityOptionallocks validation + thelongest-wins rank tuple, and that a set priority still leads the rank.
docs/generalization.md:parallel_prioritydocumented optional, default longest-wins.docs/performance.md: the transit measurement replaces the stale "transit merged muchless well in Poland" paragraph in place, and now carries the Finland row.
Measurements (regeneralized tables, live DB)
(~0%) → 976 of 3 690 (26%); junction nodes 4 383 → 1 214. The cascade now chains
below z13 (z12 2 096, z11 1 162, z10 729, z9 531) where it previously froze at
7 280. The z14 source histogram is unchanged (still 17% degree-2, exactly the
issue's table) — the source import is untouched; the drop happens inside the cascade.
like BDOT10k, so its z13 chain network was 0 of 855 nodes degree-2 (712 junction
nodes, 83% — junction-heavier than Poland's 73%) and the cascade froze at
z13 = z9 = 1 223. Regeneralized with the 0.5 px drop it chains to z9 = 75, with z13
degree-2 at 289 of 766 nodes (37.7%). The value is the measured middle: the full
1 px would eat Helsinki's metro (parallel track pairs 10–19 m apart, the SE twin-tunnel
geometry — a 1 px dry run dropped 18.6 km of its 202 km), while a 0.25 px regeneralize
leaves z13 at 1 136 chains (10.4% degree-2). At 0.5 px the z13 metro keeps 200.0 of
202.0 km (only the above-ground double track collapses).
zxy_map_transit(9,285,168): 29 203 B → 2 016 B (11% of thetile → under 1%). Stockholm z9: 8 081 → 3 707 B. Helsinki z9: 1 945 B.
from source (313 km → 313 km) because Stockholm's twin tunnels sit 10–25 m apart,
outside the 0.25 px corridor (4.8 m at z13) — Sweden only loses sub-pixel street tram
double track (tram 328 → 327 km). The corridor widens as the cascade descends
(~9.5 m at z12, ~19 m at z11, ~38 m at z10, ~76 m at z9), and from z11 down the
tunnels are inside it and correctly collapsed — that is where most of Sweden's z9
improvement comes from (z13 only moves 862 → 846; z9 moves 862 → 321). The first cut
at 1 px changed SE's ratio to 2.6x and dropped 181 z13 chains; that is why SE uses
0.25 px. Poland's drop removes the second track of each parallel pair (tram
2 324 → 1 865 km by z13) — the merge, not lost lines.
Verification
make buildclean;go test ./...fresh in importer/ and mapsrv/.metroandtrammodes present with correct
mode/countryattributes.scripts/cdp-map-check.mjs, no virtual time) at z13 overWarszawa (344 transit features, 320 tram + 24 metro) and Stockholm (136, 87 metro +
49 tram): lines continuous, no console errors; station layers render in Stockholm.
0.25 px DELETE drops 0).
Left undone
the cascade, and regeneralize 9–13 covers all merged levels.
transitwill not merge: the tram network is 73% junction nodes #38Review — #57 (transit merge, issue #38)
Builds clean,
go vetandgo testpass in both modules (the only failure isTestDumpGlyphPNG, which cannot write/tmp/glyph_g.pngin this sandbox and failsidentically on every branch,
mainincluded).This is the right response to the issue. The central claim checks out against
main:rank()atgeneralize.go:519–529already omits the column whenParallelPriorityisempty and falls back to
(-ST_Length, ST_XMin, ST_YMin), andvalidateMergeat:401only validates it when non-empty. So "the issue's expected obstacle does not exist" and
"no generation-code change was needed" are both accurate — the correct outcome was to
document the contract, lock it with a test, and set two config values, which is exactly
what the diff does. A 30-line code change for a problem the issue expected to need a
schema extension is the good kind of result.
The mechanism measurement is the decisive one and it is the right one: 73% junction nodes
against Sweden's 30%, and the degree-2 share of Poland's z13 chain network moving from
3 of 5 244 to 976 of 3 690. Sweden's 0.25 px against Poland's 1 px is properly justified
by the tunnel-separation measurement rather than picked, and recording that the first cut
at 1 px moved SE to 2.6× and dropped 181 z13 chains is exactly the rejected-alternative
number that makes the choice reviewable.
Also worth noting, since the other two open generalization PRs are entangled: this branch
is based on
main, carries no cherry-picks, and merges cleanly with both #55 and #56(verified by trial merge —
docs/performance.md,importer/generalize.goandimporter/layer_test.goall auto-merge).Finland does have a transit layer, and it has exactly this bug
"Left undone" says:
That premise is wrong.
conf/fimap.yaml:638defines one:simplify_line+merge_lines: true, noparallel_merge_pixels, sourced fromfi_muut.rautatie— i.e. precisely the pre-fix configuration Poland had. And the livetables show Helsinki in the same frozen state the issue describes:
1 879 → 1 223 is a 1.54× merge, and then z13 and z9 are the same number — the cascade
does not chain at all below z13, which is the exact signature of PL freezing at 7 280.
It is not an empty partition either: 1 105 tram + 118 metro chains, so this is Helsinki's
real network.
So the third country with a tram network is being left in the pre-fix state on a false
premise. Either measure Finland's junction share and give it a
parallel_merge_pixelsalongside the other two (the layer is already
merge_lines: true, so it is a one-lineconfig change plus a regeneralize), or correct the claim and file the follow-up
explicitly. Given that the PR closes #38 and #38 is about transit not merging, quietly
leaving a third of the affected countries unmerged seems worth resolving before merge
rather than after.
The Sweden comment describes z13 only
conf/semap.yaml:True at z13, but the corridor is
px * 0.25andpxdoubles every level down: ~9.5 m atz12, ~19 m at z11, ~38 m at z10, ~76 m at z9. Tunnels 10–25 m apart are inside the
corridor from z11 down and do get collapsed.
That is correct behaviour — at z11 the two tunnels are half a pixel apart and drawing them
separately would be wrong — and it is where most of Sweden's improvement actually comes
from: z13 only moves 862 → 846, while z9 moves 862 → 321. But the comment reads as a
property of the setting rather than of one zoom level, and someone tuning this later will
believe the tunnels are preserved throughout. One clause ("at z13; the corridor widens with
the cascade, and by z11 the tunnels are correctly collapsed") covers it, and it makes the
0.25 choice easier to understand rather than harder.
Smaller notes
DELETE's rank tuple is a strict total order in both forms, so no mutualdeletion is possible, and exact ties simply both survive. The fixed-point check
(re-running drops 0) confirms it empirically — good check to have run.
docs/performance.mdgives the same series without the z10 729. Harmless, but theyshould agree.
actually replaces the stale transit paragraph in place, which is the better thing to have
done — the description just undersells it.
TestParallelPriorityOptionalpins both the longest-wins tuple and the priority-led one,which is the right pair to lock given the whole point is that the two paths coexist.
873c9b818fto7322d2958bBoth asks from the review are addressed on the branch (head now
7322d29, 3 commits).Finland — measured, and given a value of its own (0.5 px, not borrowed).
The premise error is acknowledged: FI has had a transit layer all along (conf/fimap.yaml), and the live tables did show Helsinki frozen at z13 = z9 = 1 223. Measured on the first merged level (z13), Finland's chain network is 0 of 855 nodes degree-2 — 712 junction nodes, 83%, junction-heavier than Poland's pre-fix 73%. And the tunnel-separation measurement did apply to Helsinki after all: the metro's parallel track pairs sit 10–19 m apart, and a 1 px dry run dropped 18.6 km of its 202 km — so Poland's full 1 px was not an option. Sweden's 0.25 px regeneralizes the cascade but weakly (z13 stays at 1 136 chains, only 10.4% degree-2). The measured middle is 0.5 px: z13 metro keeps 200.0 of 202.0 km (only the above-ground double track collapses), degree-2 reaches 289 of 766 nodes (37.7% — past Poland's post-fix 26%), and the FI cascade now chains z13 676 → z12 280 → z11 163 → z10 107 → z9 75 (was frozen at 1 223). The z13 table is a fixed point (re-running the 0.5 px DELETE drops 0), and tiles decode at Helsinki z9/z13 with both modes and correct country/mode attributes.
Sweden comment. Added the clause: the corridor is px * 0.25 and px doubles every level down (~9.5 m at z12, ~19 m at z11, ~38 m at z10, ~76 m at z9), so from z11 down the 10–25 m tunnels are inside it and correctly collapsed — a property of z13, not of the setting, and the source of most of the z9 improvement.
Smaller notes.
Verification per the repo chain:
make buildclean,go test ./...fresh in importer/ and mapsrv/, confs parse, decoded tiles at Helsinki z9/z13 (64 features at z9, both modes with correct attrs at z13).7322d2958bto0bb3ee5b92