facett-map/src/paint_order.rs
//! **THE declared paint order — one key, both lanes.**
//!
//! # The defect this closes
//!
//! Reported by the owner on the live 2D map, 2026-08-22: the basemap "draws in the order
//! things were read in, and paints over what should be on top". Four independent
//! mechanisms produced that, and none of them was a bug in any single line of code —
//! the order was simply never *declared* anywhere a painter could sort on:
//!
//! 1. **Class order was a LAYER-PUSH convention.** korp pushes one `WaysLayer` per
//! feature class in `map_paint::OSM_WAY_LAYER_IDS` order, and the painter's only
//! class signal was the order those layers happen to sit in the facet. *Within* a
//! class the order was the input order — whatever the tile merge appended last won
//! every overlap. Two adjacent buildings, a lake and the park it sits in, a road and
//! the service road beside it: all decided by decode order, and therefore different
//! on the next fetch.
//! 2. **`z` was per KIND, not per class.** `LayerKind::default_z()` gives every polygon
//! `10` and every line `20`, and `MapFacet::features_for_cpu` sorted on exactly that
//! — a *stable* sort, so class order survived only as the tie-break inside one kind.
//! `DrawFeature::z`'s own doc claimed "a wider way (motorway) draws over a thinner
//! one at the same tier". It never did: every line carried the identical `20`.
//! 3. **The GPU lane broke the stack — CLOSED 2026-08-22, and this text is history.**
//! `MapFacet::ui` painted in three buckets — CPU features below a hardcoded
//! `LayerKind::Line.default_z()`, the GPU callback, then the CPU tail — while the
//! lane owns `GpuOwns { lines, fills }`. A **fill** the GPU took was therefore drawn
//! in the LINE slot, above every CPU-painted fill whatever its class.
//!
//! **Do not go looking for this one: the hardcoded slot is gone.** `MapFacet::ui`
//! now splits on `gpu_lane_rank_floor` — the lowest [`PaintKey::rank`] the lane will
//! actually draw this frame (layer.rs:2565) — and
//! `tests/paint_order_pixels.rs::the_gpu_slot_lands_in_the_declared_stack` holds it
//! there. VERIFIED 2026-08-31 (oden, RTX 4090): the device-lane pixel claims in
//! `tests/gpu_lane_paint_order.rs` — a tunnel under a lake, and the two tile
//! arrangements — pass on this tree; both were RED when they were written.
//! 4. **OSM's own z-order tags were read NOWHERE.** `layer`, `bridge` and `tunnel` did
//! not appear in `facett-map`, `facett-osm` or `korp/src/map_paint.rs` at all
//! (grep, 2026-08-22). A bridge over a river and a tunnel under a street had no
//! order to go on beyond their class, so a motorway viaduct sank under the water it
//! crosses whenever the water happened to decode later.
//! 5. **The floor was not a floor — and closing #4 is what opened it.**
//! [`LANDMASS_BAND`] was a *band* (`-1_000`) in a rank whose dominant term is
//! `effective_layer * LAYER_STEP`. So the moment `layer`/`tunnel` started being read,
//! a single `tunnel=yes` way ranked ~97 800 units under the land silhouette, dragged
//! the GPU lane's one opaque callback slot below it, and the **white land painted
//! over the whole map** — varying with what had been loaded, exactly like #3, which
//! is why it was mistaken for #3 not being fixed. See [`LANDMASS_BAND`] for the
//! measurement and [`WAY_RANK_FLOOR`] for the const assertion that now forbids it.
//! Reported by the owner 2026-08-30 (*"vita marken tar över allt"*), closed 2026-08-31.
//!
//! # The shape of the cure (LAW 5)
//!
//! ONE key, [`PaintKey`], computed by ONE function, [`way_paint_key`], from the way
//! itself — never from where it landed in a layer vector and never from when it was
//! read. Everything that paints sorts on it:
//!
//! * the CPU painter (`MapFacet::features_for_cpu` → `DrawFeature::order`),
//! * the GPU legacy lane (`gpu_lane_ways` / `gpu_lane_fills`),
//! * the GPU tile lane (each group's lines + fills).
//!
//! …and the CPU/GPU **seam** sorts on it too, which the list above used to leave out:
//! the GPU callback is ONE opaque slot, so `MapFacet::ui` places it at the lane's own
//! [`PaintKey::rank`] floor and paints the CPU features below that rank before it. That
//! makes every band constant here load-bearing across the seam and not merely inside one
//! painter's sort — which is the whole content of defect 5.
//!
//! Because the key is a pure function of the `Way`, **swapping the input order cannot
//! change a pixel** — which is the property `tests/paint_order_pixels.rs` asserts on
//! the rendered framebuffer rather than on the sorted vector.
//!
//! # The key, term by term
//!
//! ```text
//! rank = effective_layer * LAYER_STEP + band + class + tier
//! ```
//!
//! * **`effective_layer`** — the OSM `layer` tag, clamped to [`LAYER_CLAMP`]. It
//! dominates every other term, which is the OSM data model: `layer` is the
//! cartographer's explicit statement about what is above what. `bridge`/`tunnel`
//! *imply* ±1 only when `layer` is absent (the osm-carto rule — an explicit
//! `layer=2` on a bridge means 2, not 3).
//! * **`band`** — areas under lines under points under labels ([`AREA_BAND`] …
//! [`LABEL_BAND`]). The old `LayerKind::default_z()` ladder, kept as the coarse tier
//! so a themed basemap looks the way it did.
//! * **`class`** — water < landuse/park < buildings < (unclassified area) inside the
//! area band; coastline < water lines < paths < rail/roads < (unclassified line)
//! inside the line band — the rail INTERLEAVED with the road tiers, see [`RAIL_CLASS`].
//! This is korp's `OSM_WAY_LAYER_IDS` order, moved from a push convention to a
//! declared per-way value.
//! * **`tier`** — the way's [`Lod`]: a `Country` motorway paints OVER a `City`
//! cul-de-sac. This is the hierarchy `DrawFeature::z` documented and never had.
//!
//! Ties on `rank` are broken by **area, largest first** — so a building sitting inside
//! a landuse polygon of the same rank draws on top of it — and finally by the stable
//! `osm_id`, so two identical frames stack identically and no overlap is ever decided
//! by decode order.
use crate::{Lod, Way, WayKind};
/// One OSM `layer` step, in rank units. Large enough that no combination of band,
/// class and tier can climb out of its own layer.
pub const LAYER_STEP: i32 = 100_000;
/// `layer` is clamped to `-CLAMP ..= CLAMP`. Real OSM data lives in `-5..=5`; the
/// clamp keeps a vandalised `layer=99999` from overflowing the rank.
pub const LAYER_CLAMP: i32 = 8;
/// **The lowest rank [`way_rank`] can ever produce** — the deepest OSM `layer` with the
/// lowest class and the deepest tier. Nothing below this is a way, so this is where a
/// base band has to go to be a base band.
///
/// `tier_rank`'s minimum is `0` and `class_rank`'s is `AREA_BAND + 100` (a water fill), so
/// the whole floor is the `layer` term at full negative clamp. `AREA_BAND` is used rather
/// than that `+ 100`, deliberately: a floor may be conservative (a hundred units of slack)
/// but must never be optimistic, and the class offsets are the thing most likely to be
/// re-tuned. MEASURED slack, `tests::no_way_can_sink_below_the_landmass`: the deepest way
/// real classification produces is **-799 870** against this **-800 000** — 130 units, and
/// the test fails if that gap ever grows past one [`LAYER_STEP`], so "conservative" cannot
/// quietly become "a number that happens to be small".
///
/// Written as an expression off the constants that produce it rather than as a literal,
/// because a literal is exactly what let the landmass sit 799 000 rank units ABOVE it (see
/// [`LANDMASS_BAND`]).
pub const WAY_RANK_FLOOR: i32 = -LAYER_CLAMP * LAYER_STEP + AREA_BAND;
/// The **landmass base fill** — the floor, under every other band, so the land
/// silhouette paints first and land-vs-sea reads instantly. A dedicated band (not
/// `AREA_BAND - something`) so a future aerial-raster base can still slot under it.
///
/// # THE DEFECT THIS NUMBER WAS (owner, 2026-08-30: *"vita marken tar över allt"*)
///
/// It was **`-1_000`**, and the doc line above it — *"the floor, under every other
/// band"* — was false the moment [`OsmZ`] landed. A band is not a floor: [`way_rank`] is
/// `effective_layer * LAYER_STEP + band + class + tier`, and [`LAYER_STEP`] is 100 000,
/// so **one `tunnel=yes` / `layer=-1` way ranks at about −98 800** — 97 800 units BELOW a
/// landmass at −1 000.
///
/// That is not merely an ordering curiosity inside the CPU list, where the landmass
/// would still have been under every *positive*-layer feature. It decides the CPU/GPU
/// seam: `MapFacet::ui` inserts the GPU callback as ONE opaque slot at the lane's own
/// rank FLOOR (`gpu_lane_rank_floor` — the minimum `way_rank` over every way the lane
/// draws, scanned with **no viewport cull**). A single tunnel anywhere in the loaded set
/// dropped that slot below the landmass, the landmass fell into the CPU **tail**, and
/// the white country silhouette painted over the entire GPU-rendered map.
///
/// Both halves of the report, from one constant: it covers everything (the silhouette is
/// the country), and it varies with what has been loaded (whether a negative-`layer` way
/// is in the set at all).
///
/// MEASURED, that this is a live data shape and not a corner case: on a real Sweden
/// pyramid tile (`z9/288/125.parquet`, 2026-08-31) **11 of 1 493 ways (0.74 %)** carry
/// `tunnel=*` and 11 carry `layer < 0`.
///
/// MEASURED, the pixels (`tests/gpu_lane_paint_order.rs`,
/// `a_tunnel_in_the_loaded_set_cannot_put_the_white_land_over_the_map`, oden / RTX 4090,
/// 2026-08-31): two renders differing by exactly that one tag disagreed on **91 212 of
/// 208 000 pixels (43.9 %)** and the pane centre went from water `[18, 58, 90]` to land
/// `[242, 239, 233]`.
///
/// So the value is now derived from [`WAY_RANK_FLOOR`] — one clear `LAYER_STEP` under
/// the deepest way there can be — and the const assertion below makes "the landmass is
/// the floor" a build failure to break rather than a sentence to trust.
pub const LANDMASS_BAND: i32 = WAY_RANK_FLOOR - LAYER_STEP;
/// **The landmass really is under every way.** The claim [`LANDMASS_BAND`]'s doc makes,
/// asserted at build time against the arithmetic that produces a way's rank instead of
/// being restated in prose — which is how it stayed false for eight days.
const _: () = assert!(
LANDMASS_BAND < WAY_RANK_FLOOR,
"LANDMASS_BAND is no longer under the lowest rank a way can have: a `tunnel=yes` or a \
`layer=-1` way now sorts BELOW the land silhouette, which drags the GPU lane's callback \
slot under it and paints the white land over the whole map."
);
/// Area fills — the bottom band.
pub const AREA_BAND: i32 = 0;
/// Stroked lines (coastline, roads, paths) — above every fill of the same layer.
pub const LINE_BAND: i32 = 1_000;
/// Point markers.
pub const POINT_BAND: i32 = 2_000;
/// Labels — always the top band.
pub const LABEL_BAND: i32 = 3_000;
/// The rank a single [`Lod`] step is worth. See [`CLASS_STEP`] for the budget it
/// spends.
pub const TIER_STEP: i32 = 10;
/// **The tightest gap between two adjacent class offsets** — the budget the whole tier
/// ladder has to fit inside.
///
/// The AREA band steps by 100 (water 100, park 200, building 300, other 400) and that
/// is the number `tier_rank` used to quote. The LINE band does not: coastline `0`,
/// **water 50**, path 100, road 200, other 400. The headroom is therefore HALF what was
/// written, and the tier at which a sub-rank climbs into the class above is a 6th, not
/// an 11th. `tests::the_tier_ladder_fits_under_the_measured_class_gap` measures this
/// off `class_rank` rather than trusting it to stay true.
pub const CLASS_STEP: i32 = 50;
/// **A sixth tier does not fit, and this is where it fails.**
///
/// The coarsest tier's sub-rank is `(LOD_COUNT-1) * TIER_STEP`; it must be strictly
/// under [`CLASS_STEP`], because *equal* puts the coarsest tier of one class exactly on
/// the finest tier of the next and the collision resolves through the area tie-break —
/// a look defect with nothing pointing back here. Five tiers fit with 10 to spare.
const _: () = assert!(
(crate::style::LOD_COUNT as i32 - 1) * TIER_STEP < CLASS_STEP,
"LOD_COUNT tiers x TIER_STEP no longer fits under CLASS_STEP: the coarsest tier of one class now collides with the finest tier of the next. Either widen the class offsets in `class_rank` (and CLASS_STEP with them) or shrink TIER_STEP."
);
/// **Where a RAILWAY sits: inside the road ladder, not beside it** — GG audit row 28.
///
/// # The defect
///
/// `class_rank` had no arm for [`WayKind::Rail`] because there was no such kind:
/// `classify_with`'s `railway` arm stamped `WayKind::Other`, and `Other`'s line offset is
/// the OVERLAY slot, `LINE_BAND + 400`. So every `railway=rail` in Sweden ranked 1440
/// against the motorway's 1240 and painted over the E4 at every crossing, and
/// [`line_kind_outranks_roads`] told the 3D extruder the same lie.
///
/// # Why this is not `LINE_BAND + 150` or `+ 250`
///
/// The class offsets step by [`CLASS_STEP`] = 50 and the tier ladder spends 40 of that,
/// which is the right shape for classes that do not interleave. A railway DOES: the
/// order every reference agrees on — osm2pgsql's default `z_order` (rail 35, level with
/// `secondary`, under `primary`/`trunk`/`motorway`, over `tertiary`/`residential`) and the
/// Garmin look korp is after — puts trunk rail between the primary and the secondary
/// road network, i.e. BETWEEN two tiers of one class. No offset 50 apart from `Road`
/// can express that: `+150` is under every road including `service`, `+250` is over
/// the motorway.
///
/// So the rail offset is the road offset **minus half a tier step**: five units under
/// the road ladder, which puts each rail tier exactly midway between two road tiers and
/// never ON one (the const assertion below forbids the tie, which would fall through to
/// the bbox-area tie-break — decode-order luck at every level crossing):
///
/// ```text
/// Road·Country 1240 motorway, trunk, primary
/// Rail·Country 1235 railway=rail, narrow_gauge ← the national network
/// Road·Region 1230 secondary, primary_link, motorway_link
/// Road·District 1220 tertiary
/// Rail·District 1215 light_rail, subway, tram, funicular ← urban rail
/// Road·City 1210 residential, unclassified
/// Road·Detail 1200 service, track
/// Path·Country 1140 (the path ladder tops out here)
/// ```
///
/// This is the ONE class that interleaves, and
/// `tests::the_tier_ladder_fits_under_the_measured_class_gap` deliberately leaves it out
/// of the gap measurement for that reason; its own order is pinned by
/// `tests::a_railway_ranks_under_the_motorway_and_over_the_residential_street`.
pub const RAIL_CLASS: i32 = ROAD_CLASS - TIER_STEP / 2;
/// The road class offset inside the LINE band — named so [`RAIL_CLASS`] can be written
/// as an expression off it rather than as a literal that drifts.
const ROAD_CLASS: i32 = 200;
/// **No rail tier may land ON a road tier.** Half a tier step is not a multiple of one,
/// which is the whole property; spelled out so a retuned `TIER_STEP` cannot silently make
/// it one.
const _: () = assert!(
(ROAD_CLASS - RAIL_CLASS) % TIER_STEP != 0,
"RAIL_CLASS is a whole number of tier steps off ROAD_CLASS: some rail tier now TIES a \
road tier and the tie resolves through the bbox-area fallback at every level crossing"
);
/// Per-[`Lod`] sub-rank: `Country` (the trunk network, the big water) sits **highest**
/// inside its class, the deepest tier (footway furniture) lowest.
///
/// Derived from [`LOD_COUNT`](crate::style::LOD_COUNT) rather than the literal `3` it
/// used to hardcode, so adding a tier moves the ladder instead of folding two tiers
/// onto one rank — the same defect `LOD_COUNT`'s own doc was written about.
fn tier_rank(lod: Lod) -> i32 {
let last = crate::style::LOD_COUNT as i32 - 1;
(last - (lod as i32).clamp(0, last)) * TIER_STEP
}
/// The class offset inside a band — korp's `OSM_WAY_LAYER_IDS` order, declared here
/// per way instead of being implied by which layer vector the way landed in.
fn class_rank(kind: WayKind, is_area: bool) -> i32 {
if is_area {
AREA_BAND
+ match kind {
WayKind::Water => 100,
WayKind::Park => 200,
WayKind::Building => 300,
// An unclassified closed ring: above the classified fills, still under
// every line. This is the half of korp's `osm-other` layer that used to
// paint over the ROADS purely because that layer is pushed last.
_ => 400,
}
} else {
LINE_BAND
+ match kind {
WayKind::Coastline => 0,
// A `waterway=river`/`stream`/`ditch` is a LINE, not a fill, and it
// belongs with the shore — under the road network, not above it. It fell
// into the catch-all below while it was the only line class with no arm,
// which put every river over every motorway.
WayKind::Water => 50,
WayKind::Path => 100,
// INSIDE the road ladder, half a tier step under it — see `RAIL_CLASS`.
// It was the catch-all below, i.e. overlay ink above every road (GG #28).
WayKind::Rail => RAIL_CLASS,
WayKind::Road => ROAD_CLASS,
_ => 400,
}
}
}
/// **Does a LINE of this class paint above the road network?** — lane GG #4.
///
/// The one question a second painter has to ask the declared order, exported so it cannot
/// be answered twice. `true` for [`WayKind::Glow`], [`WayKind::Overprint`] and
/// [`WayKind::Other`] — overlay ink, at `LINE_BAND + 400`; `false` for
/// [`Coastline`](WayKind::Coastline) `+ 0`, [`Water`](WayKind::Water) `+ 50`,
/// [`Path`](WayKind::Path) `+ 100`, [`Rail`](WayKind::Rail) `+ 195` and
/// [`Road`](WayKind::Road) `+ 200`, which are the basemap. A railway answered `true`
/// until GG #28, because it had no kind and rode `Other`.
///
/// It exists because the 3D extruder had its own, coarser stack (`GroundLayer`) and made
/// the opposite call: `facett-map3d`'s `line_feature` gave a road three stacked sub-passes
/// and laid **everything else** at the lowest of them, so a case glow or a graph edge sat
/// *under* the road it crossed. That is the same defect `class_rank`'s `Glow` doc already
/// names — *"a trail belongs above the roads it crosses, not among them"* — arriving in
/// the lane that did not ask.
///
/// Area kinds are not a question this answers: it reads the LINE band, where the ordering
/// argument is about ribbons on the ground.
#[must_use]
pub fn line_kind_outranks_roads(kind: WayKind) -> bool {
class_rank(kind, false) > class_rank(WayKind::Road, false)
}
/// The OSM z-order tags a way carries: `layer`, `bridge`, `tunnel`. Default (all zero /
/// false) is "ground level", which is what every untagged way and every synthetic way
/// gets — byte-identical ordering to before this type existed.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct OsmZ {
/// The `layer` tag, already clamped to ±[`LAYER_CLAMP`]. `0` = absent or ground.
pub layer: i8,
/// `bridge=*` (anything but `no`).
pub bridge: bool,
/// `tunnel=*` (anything but `no`).
pub tunnel: bool,
}
impl OsmZ {
/// The layer this way actually paints on. `layer` is authoritative when present;
/// `bridge`/`tunnel` only *imply* one when it is absent.
#[must_use]
pub fn effective_layer(self) -> i32 {
if self.layer != 0 {
i32::from(self.layer)
} else if self.bridge {
1
} else if self.tunnel {
-1
} else {
0
}
}
/// **[`effective_layer`](Self::effective_layer) for an AREA** — lane GG #6.
///
/// # The defect a bare `layer` on a polygon causes
///
/// [`effective_layer`](Self::effective_layer) is the dominant term of [`way_rank`],
/// multiplied by [`LAYER_STEP`] = `100_000` — a step chosen so no combination of band,
/// class and tier can climb out of its own layer. That is exactly right for what
/// `layer` exists to say: a deck over a river, a street under a square.
///
/// It is a disaster for a **mis-tag**. One careless `layer=1` on an opaque polygon
/// lifts it a hundred thousand rank units, past every road, path and label — and OSM
/// is full of them. `landuse=grass layer=1` then paints over the motorway crossing it
/// and no class order can answer, because the declared order genuinely says the grass
/// is on top. That is a "terrain over roads" symptom that survives every fix to the
/// GPU depth stack, because it is not a depth bug.
///
/// # The rule
///
/// An area honours `layer` **only when it also says why**. `bridge=*` or `tunnel=*`
/// alongside the `layer` is a polygon declaring what it is — a deck, a passage, an
/// underground car park — and those are the cases `layer` is for. A **bare** `layer`
/// on an area says only *"put me somewhere else"* without saying what it is, and that
/// is the shape of the mis-tag.
///
/// LINES are deliberately untouched and keep [`effective_layer`](Self::effective_layer):
/// a `layer=1` road really is a flyover and a `layer=-1` one really is a tunnel, which
/// is what `paint_order_pixels::a_bridge_paints_over_the_water_and_a_tunnel_under_it`
/// holds. The asymmetry is the point — a line with a `layer` is describing its own
/// geometry, an area with a bare one is usually describing nothing.
#[must_use]
pub fn effective_layer_for_area(self) -> i32 {
if self.bridge || self.tunnel {
self.effective_layer()
} else {
0
}
}
}
/// A truthy OSM flag: present and not `no`/`false`/`0`.
fn flag_set(v: &str) -> bool {
let v = v.trim();
!v.is_empty() && !v.eq_ignore_ascii_case("no") && !v.eq_ignore_ascii_case("false") && v != "0"
}
/// Read `layer` / `bridge` / `tunnel` off a way's raw OSM tags.
///
/// Tolerant of the spellings that occur: `layer=+1`, `layer=1;2` (first wins),
/// `bridge=viaduct`, `tunnel=building_passage`. Anything unparsable is ground level —
/// a bad `layer` must never move a way, only fail to move it.
pub fn parse_osm_z<K: AsRef<str>, V: AsRef<str>>(tags: &[(K, V)]) -> OsmZ {
let get = |k: &str| {
tags.iter().find(|(tk, _)| tk.as_ref() == k).map(|(_, tv)| tv.as_ref())
};
let layer = get("layer")
.and_then(|v| {
let head = v.trim().split(';').next().unwrap_or("").trim();
head.strip_prefix('+').unwrap_or(head).parse::<i32>().ok()
})
.map_or(0, |l| l.clamp(-LAYER_CLAMP, LAYER_CLAMP) as i8);
OsmZ {
layer,
bridge: get("bridge").is_some_and(flag_set),
tunnel: get("tunnel").is_some_and(flag_set),
}
}
/// **The paint order of one drawable.** Sorted ascending: a larger key draws LATER,
/// i.e. on top.
///
/// The field order IS the comparison order (derived `Ord`), and every field is a pure
/// function of the feature — never of its position in an input vector. That is what
/// makes "swap the input order, get identical pixels" true by construction rather than
/// by review.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PaintKey {
/// Layer · band · class · tier — see the module docs.
pub rank: i32,
/// **Area, largest first.** `!f32::to_bits(area)` of the way's Mercator bbox: for
/// a non-negative `f32` the bit pattern is monotone in the value, so the
/// complement sorts descending. A small footprint therefore paints over the big
/// polygon it sits inside — the standard cartographic fallback for two fills that
/// have nothing else to separate them, and the one that used to be "whoever was
/// decoded last".
pub area_desc: u32,
/// Final, fully deterministic tie-break. Two ways with the same rank and the same
/// bbox area still stack the same way on every frame and in every process.
pub id: i64,
}
impl PaintKey {
/// A key for a non-way AREA drawable (a landmass base polygon): band + the same
/// largest-first area fallback a way gets, so two overlapping land polygons stack
/// by size rather than by the order they were decoded in.
#[must_use]
pub fn area(band: i32, merc_area: f32, id: i64) -> Self {
Self { rank: band, area_desc: !merc_area.max(0.0).to_bits(), id }
}
/// A key for a non-way drawable (a point marker, a label): band + tier only, with
/// no area to compare.
#[must_use]
pub fn simple(band: i32, lod: Lod, id: i64) -> Self {
Self { rank: band + tier_rank(lod), area_desc: u32::MAX, id }
}
}
/// **The rank of one way** — the `rank` half of [`way_paint_key`], exposed because the
/// GPU/CPU seam has to ask "what is the lowest rank the GPU lane owns this frame?"
/// without building keys.
#[must_use]
pub fn way_rank(w: &Way) -> i32 {
// **A rank the producer already computed wins** — lane GG #17. This is not a second
// opinion about the paint order: the only way to fill `Way::rank` is to have called
// THIS function, so the answer is the same answer, just not derived twice. korp's
// server fills it (proto `MapPolyline.rank`) because every client was re-deriving it
// per way per landing on the UI thread — `parse_osm_z` walking the tag list, then the
// two arms below. Measured on oden: 9.77 ms of serial tag parse per 20 000-way
// landing.
if let Some(r) = w.rank {
return r;
}
let is_area = w.fill.is_some();
layer_term(w, is_area) + class_rank(w.style.kind, is_area) + tier_rank(w.style.lod)
}
/// **The `layer` term of a rank — the ONE place either rank function reads it** (lane
/// GG #6). `way_rank` and `way_draw_band` both computed
/// `effective_layer() * LAYER_STEP` inline, so the area rule had two places to land in
/// and one of them would eventually be missed. See
/// [`OsmZ::effective_layer_for_area`] for why an area is asked a different question.
fn layer_term(w: &Way, is_area: bool) -> i32 {
let l = if is_area { w.z_order.effective_layer_for_area() } else { w.z_order.effective_layer() };
l * LAYER_STEP
}
/// **The DRAW BAND of one way** — [`way_rank`] with the per-[`Lod`] tier term removed.
///
/// # Why a hybrid renderer needs this and not the rank
///
/// A GPU lane's draw is not a painter with a z; it is a *submission order*. `PaintKey`
/// decides the order **inside one submitted stream** (the compaction is ordered — see
/// `tests/cull_order.rs`) and has no way to reach across two of them. The lane therefore
/// has to submit one draw per *band*, and the band is the coarsest unit that loses
/// nothing: the tier, the area fallback and the id tie-break all still decide the order
/// within the band, because the stream inside a band is still sorted on the whole
/// [`PaintKey`].
///
/// MEASURED, oden / RTX 4090, 2026-08-23 (`tests/gpu_lane_paint_order.rs`): with the
/// lane submitting three fixed buckets instead — all fills, then all outlines, then all
/// lines — moving the SAME lake and building between two tiles changed **9 660 of 208 000
/// pixels (4.64 %)** and in one of the two arrangements the building was **not on screen
/// at all**. A `tunnel=yes` road under a lake painted on top of the lake in both.
///
/// Note what is deliberately NOT in the band: the tier. Splitting on the full rank would
/// give one draw per (layer, class, tier) triple — 4× the draws for an ordering the
/// stream already carries.
#[must_use]
pub fn way_draw_band(w: &Way) -> i32 {
let is_area = w.fill.is_some();
layer_term(w, is_area) + class_rank(w.style.kind, is_area)
}
/// **THE declared paint key of one way.** The single writer every lane sorts on.
#[must_use]
pub fn way_paint_key(w: &Way) -> PaintKey {
// Mercator-unit bbox area. `[x0, y0, x1, y1]`, always non-negative.
let area = (((w.bbox[2] - w.bbox[0]).max(0.0)) * ((w.bbox[3] - w.bbox[1]).max(0.0))) as f32;
PaintKey { rank: way_rank(w), area_desc: !area.to_bits(), id: w.osm_id }
}
// ═════════════════════════════════════════════════════════════════════════════
// RANK AS DEPTH — lane MP-a, 2026-09-05.
//
// `PaintKey` orders the stream INSIDE one submitted stream and has no way to
// reach across two of them (see `way_draw_band`). The lane submits one
// `draw_indirect` per chunk and the tiled path iterates per tile, so a later
// tile's dark casing lands on an earlier tile's bright fill. MEASURED on an
// RTX 4090 2026-09-05, `gpu_lane_paint_order::
// which_tile_a_road_landed_in_cannot_decide_which_road_is_on_top`: the crossing
// pixel of a motorway and a residential came back [56, 78, 92] — neither the
// motorway [70, 198, 224] nor the residential [85, 98, 117], but the
// residential's CASING over the motorway's fill.
//
// No submission order fixes that in general: which tiles a camera holds is not
// known when the rank is assigned. Depth is the only thing that reaches across
// submissions — the rank rides in Z, the depth test decides, and which pass drew
// first stops mattering.
// ═════════════════════════════════════════════════════════════════════════════
/// The highest rank the ladder can produce: the top OSM layer, the label band.
const RANK_CEILING: i32 = LAYER_CLAMP * LAYER_STEP + LABEL_BAND;
/// **How far behind its own fill a casing sits, in depth units.**
///
/// Small on purpose, and asserted against the tightest CLASS gap by
/// `tests/paint_depth.rs`: a bias as wide as a class gap would sort a road's casing past
/// the class below it, which is the same defect this lane is fixing with the roles
/// swapped.
pub const CASING_DEPTH_BIAS: f32 = 1.0e-6;
/// **A [`way_rank`] as a depth in `0.0 ..= 1.0`**, for a `Less` depth test against a
/// buffer cleared to `1.0`: a HIGHER rank yields a SMALLER depth and therefore paints
/// closer.
///
/// `casing` pushes the value one [`CASING_DEPTH_BIAS`] further back, so a line's dark
/// outline sits behind the bright fill it outlines and still in front of the next rank
/// down.
///
/// The mapping is linear over the whole reachable rank range —
/// [`LANDMASS_BAND`] up to the top layer's [`LABEL_BAND`] — and **clamped**. A depth
/// outside the clip range is dropped by the rasteriser, and a way that silently vanishes
/// is worse than one in the wrong order: nothing on screen points back here.
#[must_use]
pub fn depth_of(rank: i32, casing: bool) -> f32 {
let lo = f64::from(LANDMASS_BAND);
let hi = f64::from(RANK_CEILING);
let t = (f64::from(rank) - lo) / (hi - lo);
// Inverted: rank up ⇒ depth down ⇒ closer.
let d = 1.0 - t;
let d = d as f32 + if casing { CASING_DEPTH_BIAS } else { 0.0 };
d.clamp(0.0, 1.0)
}
/// **The depth a FILL's own ring outline is drawn at** — lane GG #1.
///
/// # Why this is not `depth_of(way_rank(w), true)`
///
/// A fill's outline is not a casing and does not belong behind the thing it outlines: it
/// **is** that fill's edge, and on this lane it is also the fill's only anti-aliasing
/// (`tests/fill_outline_aa.rs`). Put it one [`CASING_DEPTH_BIAS`] *behind* its own fill
/// and `Less` rejects it wherever the fill already wrote depth, which is everywhere the
/// ring is filled — the shoreline loses its edge and the polygon loses its AA.
///
/// So it sits one bias **in front**, and the band is [`way_draw_band`], not [`way_rank`].
/// That is the load-bearing half: the tile lane pins a whole band run of fills to
/// `depth_of(band, false)` through `set_viewport`, deliberately dropping the tier, and an
/// outline computed from the rank would carry a tier term its own fill does not have. The
/// two would disagree by up to a tier's worth of depth — enough for a coarse-tier ring's
/// outline to fall *behind* its fill and vanish, and for a fine-tier one to climb toward
/// the band above. The outline follows the plane its fill was actually pinned to.
///
/// The bias is the same magnitude a casing uses, for the same reason: the smallest step
/// that separates two things of one rank without reaching the rank next door.
///
/// # What it replaced
///
/// `0.0` — the CLOSEST plane the clip volume has, passed as the "no rank to give"
/// default of [`crate::gpu::line::LineExpander::push_pts`]. Harmless while the lane had
/// no depth buffer; the moment MP-a's depth pass landed it made every lake, park and
/// building outline the highest-priority ink on the map, beating every road drawn after
/// it. OSM landuse rings share their geometry with the road that bounds them, so that
/// edge lands ON the road's axis: lane GG #1, and the mechanism behind "roads overwritten
/// by landcover".
#[must_use]
pub fn fill_outline_depth(band: i32) -> f32 {
(depth_of(band, false) - CASING_DEPTH_BIAS).clamp(0.0, 1.0)
}
/// **Pack a `side` (±1) and a depth (`0..=1`) into one `f32`** — lane MP-a.
///
/// # Why the vertex cannot simply grow
///
/// Depth has to be **per way**. The tile lane's draw is already band-major, so bands
/// order correctly across tiles — and the two-tile oracle still failed, because
/// [`way_draw_band`] deliberately drops the tier: a motorway and a residential share one
/// band. Inside a band the draw walks TILES, so a later tile's casing lands on an earlier
/// tile's fill. A per-draw uniform therefore cannot carry it.
///
/// And a new vertex field is not available: `LineVertex` is exactly 48 B, asserted at
/// compile time, and that size is load-bearing — the vertex layout, the chunk
/// partition and the bake format all rest on it.
///
/// `side` is `+1.0` or `-1.0`: **one bit of information in a whole `f32`**. So the depth
/// rides in its magnitude, `sign(side) · (1 + depth)`, and the shader pays one
/// `sign`/`abs` pair.
///
/// **Precision, checked rather than assumed:** in `[1, 2)` an `f32` step is
/// `2^-23 ≈ 1.19e-7`, so a depth survives to about that — comfortably finer than
/// [`CASING_DEPTH_BIAS`] (`1e-6`), which is what keeps a casing distinguishable from the
/// fill it outlines. `tests/side_carries_depth.rs` asserts it.
///
/// The magnitude starts at `1.0`, so the packed value never reaches `0`, where a sign
/// cannot be recovered.
#[must_use]
pub fn pack_side_depth(side: f32, depth: f32) -> f32 {
let s = if side < 0.0 { -1.0f32 } else { 1.0 };
s * (1.0 + depth.clamp(0.0, 1.0))
}
/// The `side` half of a [`pack_side_depth`] value: `+1.0` or `-1.0`.
#[must_use]
pub fn unpack_side(packed: f32) -> f32 {
if packed < 0.0 { -1.0 } else { 1.0 }
}
/// The depth half of a [`pack_side_depth`] value, back in `0..=1`.
#[must_use]
pub fn unpack_depth(packed: f32) -> f32 {
(packed.abs() - 1.0).clamp(0.0, 1.0)
}
#[cfg(test)]
mod tests {
use super::*;
fn tags(pairs: &[(&str, &str)]) -> Vec<(String, String)> {
pairs.iter().map(|(k, v)| ((*k).to_string(), (*v).to_string())).collect()
}
/// **THE TIER SUB-RANK MUST FIT UNDER THE TIGHTEST CLASS GAP, and the number in
/// the comment was not the tightest one.**
///
/// `tier_rank`'s doc claimed "4 tiers × 10 fits inside the 100-unit class spacing".
/// The AREA band does step by 100 (water 100, park 200, building 300, other 400).
/// The LINE band does not: coastline `0`, water `50`. The real spacing is **50**, so
/// the headroom is half what the comment says, and the point at which a tier
/// sub-rank climbs into the class above is a 6th tier, not an 11th.
///
/// A collision here is SILENT — a coastline of the coarsest tier and a river of the
/// finest would simply share a rank and fall through to the area tie-break, which
/// is a look defect nobody would trace back to this function. So the gap is measured
/// off `class_rank` itself rather than restated, and the const assertion beside
/// `TIER_STEP` turns a 6th tier into a build failure.
#[test]
fn the_tier_ladder_fits_under_the_measured_class_gap() {
// Measured, not recited: the smallest step between adjacent class offsets that
// `class_rank` actually produces, in either band.
//
// `WayKind::Rail` is deliberately NOT in this list. It is the one class that sits
// INSIDE another's tier ladder — half a tier step under `Road`, so its gap to the
// road offset is 5 by design and not a ladder that has to fit anything. Its order
// is pinned by `a_railway_ranks_under_the_motorway_and_over_the_residential_street`.
let mut ranks: Vec<i32> = Vec::new();
for kind in [
WayKind::Water,
WayKind::Park,
WayKind::Building,
WayKind::Coastline,
WayKind::Path,
WayKind::Road,
WayKind::Other,
] {
for is_area in [true, false] {
ranks.push(class_rank(kind, is_area));
}
}
ranks.sort_unstable();
ranks.dedup();
let measured = ranks
.windows(2)
.map(|w| w[1] - w[0])
// Band boundaries are not class gaps: AREA_BAND..LINE_BAND is 1000 apart and
// the step across it says nothing about how much room a tier has.
.filter(|d| *d < LINE_BAND - AREA_BAND)
.min()
.expect("class_rank produces more than one distinct offset");
assert_eq!(
measured, CLASS_STEP,
"the declared class spacing ({CLASS_STEP}) is not the one `class_rank` \
produces ({measured}) — the tier sub-rank's headroom is whatever the \
TIGHTEST gap is, and the comment used to say 100 when the coastline→water \
step is {measured}"
);
// …and the ladder that has to fit under it. The deepest tier is 0 and the
// coarsest is `(LOD_COUNT-1) * TIER_STEP`, so THAT is the span, and it must be
// strictly under the gap: equal would put the coarsest tier of one class exactly
// on the finest tier of the next.
let span = (crate::style::LOD_COUNT as i32 - 1) * TIER_STEP;
assert_eq!(span, tier_rank(Lod::Country), "the span IS the coarsest tier's rank");
assert_eq!(tier_rank(Lod::ALL[crate::style::LOD_COUNT - 1]), 0, "the deepest tier is the floor");
assert!(
span < CLASS_STEP,
"{} tiers x {TIER_STEP} = {span} does not fit under a class gap of \
{CLASS_STEP} — the coarsest tier of one class collides with the finest of \
the next, silently",
crate::style::LOD_COUNT
);
}
#[test]
fn layer_tag_dominates_class() {
// A tunnel-layer road (layer = -2) must sink under the water it crosses, and a
// bridge road must climb over it — whatever their classes say.
// The `layer` term of a rank (`effective_layer * LAYER_STEP`, as `layer_term`
// computes it) plus the class term.
let rank = |layer: i32, kind: WayKind, is_area: bool| layer * LAYER_STEP + class_rank(kind, is_area);
let ground_water = rank(0, WayKind::Water, true);
let tunnel_road = rank(-2, WayKind::Road, false);
let bridge_road = rank(1, WayKind::Road, false);
assert!(tunnel_road < ground_water, "layer=-2 road belongs UNDER ground water");
assert!(bridge_road > ground_water, "layer=1 road belongs OVER ground water");
}
#[test]
fn bridge_implies_a_layer_only_when_layer_is_absent() {
assert_eq!(parse_osm_z(&tags(&[("bridge", "yes")])).effective_layer(), 1);
assert_eq!(parse_osm_z(&tags(&[("tunnel", "yes")])).effective_layer(), -1);
// Explicit layer WINS — a bridge tagged layer=2 is on 2, not 3.
assert_eq!(parse_osm_z(&tags(&[("bridge", "yes"), ("layer", "2")])).effective_layer(), 2);
// `bridge=no` is not a bridge.
assert_eq!(parse_osm_z(&tags(&[("bridge", "no")])).effective_layer(), 0);
// Spellings that occur in real extracts.
assert_eq!(parse_osm_z(&tags(&[("layer", "+1")])).layer, 1);
assert_eq!(parse_osm_z(&tags(&[("layer", "-1;0")])).layer, -1);
assert_eq!(parse_osm_z(&tags(&[("layer", "banana")])).layer, 0);
assert_eq!(parse_osm_z(&tags(&[("layer", "99999")])).layer, LAYER_CLAMP as i8);
}
#[test]
fn class_ladder_is_the_declared_one() {
// Areas below lines; korp's OSM_WAY_LAYER_IDS order inside each band.
let w = class_rank(WayKind::Water, true);
let p = class_rank(WayKind::Park, true);
let b = class_rank(WayKind::Building, true);
let o_area = class_rank(WayKind::Other, true);
let coast = class_rank(WayKind::Coastline, false);
let road = class_rank(WayKind::Road, false);
assert!(w < p && p < b && b < o_area, "water < landuse < buildings < other-area");
assert!(o_area < coast, "every fill under every line of the same layer");
assert!(coast < road, "coastline under roads");
}
#[test]
fn a_country_road_outranks_a_city_road() {
// The hierarchy `DrawFeature::z` documented and never produced: at the same
// class, the more important tier paints ON TOP.
assert!(tier_rank(Lod::Country) > tier_rank(Lod::City));
assert!(tier_rank(Lod::City) > tier_rank(Lod::Detail));
// ... and it stays inside its own class, never climbing into the next one.
assert!(
class_rank(WayKind::Road, false) + tier_rank(Lod::Country)
< class_rank(WayKind::Other, false),
"a tier bump must not promote a road out of its class"
);
}
/// **A RAILWAY RUNS UNDER THE MOTORWAY AND OVER THE RESIDENTIAL STREET** — GG #28.
///
/// # The defect
///
/// `class_rank` had no arm for a railway. `classify_with` grew its `railway` arm in
/// `railtier-1` (the ink, the tier) but stamped the style `WayKind::Other`, so every
/// `railway=rail` in the country ranked at `LINE_BAND + 400` — the OVERLAY offset,
/// above every road there is. On korp's "sweden · roads+rails" the Västra stambanan
/// painted over the E4 at every crossing, and `line_kind_outranks_roads` answered
/// `true` for it, so the 3D extruder laid it over the roads too.
///
/// # The order, with its neighbours named
///
/// osm2pgsql's default `z_order` puts `railway=rail` at 35 — level with `secondary`,
/// under `primary`/`trunk`/`motorway`, over `tertiary` and `residential`. That is the
/// Garmin look too, and it is INSIDE the road class's tier ladder, so a rail cannot
/// be a class offset 50 apart from `Road`: it is [`RAIL_CLASS`], five units off the
/// road ladder, and lands
///
/// * trunk rail (`Lod::Country`) at `LINE_BAND + 235`, between Road·Region (1230:
/// `secondary`, `primary_link`, `motorway_link`) and Road·Country (1240:
/// `motorway`, `trunk`, `primary`);
/// * urban rail (`Lod::District`) at `LINE_BAND + 215`, between Road·City (1210:
/// `residential`) and Road·District (1220: `tertiary`).
///
/// RED before the arm: `railway=rail` measured 1440 against the motorway's 1240.
#[test]
fn a_railway_ranks_under_the_motorway_and_over_the_residential_street() {
let pal = crate::MapPalette::cartographic(false);
let mk = |id: i64, t: &[(&str, &str)]| {
crate::Way::from_lonlat_with(id, &[(18.0, 59.0), (18.01, 59.0)], &tags(t), &pal)
.expect("a 2-vertex line is a way")
};
let rail = mk(1, &[("railway", "rail")]);
let motorway = mk(2, &[("highway", "motorway")]);
let primary = mk(3, &[("highway", "primary")]);
let secondary = mk(4, &[("highway", "secondary")]);
let tertiary = mk(5, &[("highway", "tertiary")]);
let residential = mk(6, &[("highway", "residential")]);
let tram = mk(7, &[("railway", "tram")]);
let footway = mk(8, &[("highway", "footway")]);
let river = mk(9, &[("waterway", "river")]);
// The claim of the row, then the exact neighbours.
assert!(
way_rank(&rail) < way_rank(&motorway),
"a railway ({}) must run UNDER the motorway ({}) — it ranked in the overlay \
offset above every road",
way_rank(&rail),
way_rank(&motorway)
);
assert!(
way_rank(&rail) > way_rank(&residential),
"a railway ({}) must run OVER a residential street ({})",
way_rank(&rail),
way_rank(&residential)
);
assert_eq!(way_rank(&motorway), way_rank(&primary), "fixture: motorway and primary share Road·Country");
assert!(
way_rank(&secondary) < way_rank(&rail) && way_rank(&rail) < way_rank(&motorway),
"trunk rail ({}) sits between Road·Region ({}) and Road·Country ({})",
way_rank(&rail),
way_rank(&secondary),
way_rank(&motorway)
);
assert!(
way_rank(&residential) < way_rank(&tram) && way_rank(&tram) < way_rank(&tertiary),
"urban rail ({}) sits between Road·City ({}) and Road·District ({})",
way_rank(&tram),
way_rank(&residential),
way_rank(&tertiary)
);
// No rail tier ever TIES a road tier: a tie falls through to the bbox-area
// tie-break, which is decode-order-shaped luck at every level crossing.
for r in [&rail, &tram] {
for road in [&motorway, &primary, &secondary, &tertiary, &residential] {
assert_ne!(way_rank(r), way_rank(road), "a rail rank collided with a road rank");
}
}
// Still a line of the basemap: over every path and every river, and NOT overlay
// ink — the extruder's question, answered the same way.
assert!(way_rank(&tram) > way_rank(&footway), "every rail runs over the footpath network");
assert!(way_rank(&tram) > way_rank(&river), "every rail runs over the rivers");
assert!(
!line_kind_outranks_roads(rail.style.kind),
"a railway is basemap, not overlay ink — `line_kind_outranks_roads` must say so"
);
}
/// **The draw band is the rank WITHOUT the tier** — and both halves of that matter
/// to the GPU lane, which submits one draw per band.
///
/// RED, both ways:
/// * add the tier back into `way_draw_band` and the first assertion fires — the lane
/// would then mint one group per (layer, class, TIER) triple, four times the draws
/// and four times the VRAM entries for an order the stream already carries;
/// * drop the `effective_layer() * LAYER_STEP` term and the second fires — a tunnel
/// road would share a band with a ground road and land back above the water.
#[test]
fn the_draw_band_is_the_rank_with_the_tier_stripped() {
let ll = vec![(18.0, 59.0), (18.01, 59.0)];
let mk = |id: i64, tags: &[(&str, &str)]| {
let t: Vec<(String, String)> =
tags.iter().map(|(k, v)| ((*k).to_string(), (*v).to_string())).collect();
crate::Way::from_lonlat_with(id, &ll, &t, &crate::MapPalette::cartographic(false))
.expect("a 2-vertex line is a way")
};
// Same class, DIFFERENT tier: a motorway is `Country`, a residential street is
// `City` (style.rs). They must share one band.
let trunk = mk(1, &[("highway", "motorway")]);
let street = mk(2, &[("highway", "residential")]);
assert_ne!(trunk.style.lod, street.style.lod, "fixture: the two tiers must really differ");
assert_eq!(
way_draw_band(&trunk),
way_draw_band(&street),
"two tiers of the same class must share ONE band, or the lane pays 4x the draws"
);
// ... while the RANK still separates them, so nothing is lost inside the band.
assert!(way_rank(&trunk) > way_rank(&street), "the tier still orders them by rank");
// A tunnel road's band sinks below a ground WATER FILL's band. This is the pair
// the GPU lane could not express while it submitted three fixed buckets.
let tunnel = mk(3, &[("highway", "motorway"), ("tunnel", "yes")]);
let lake = crate::Way::from_lonlat_with(
4,
&[(18.0, 59.0), (18.01, 59.0), (18.01, 59.01), (18.0, 59.01), (18.0, 59.0)],
&[("natural".to_string(), "water".to_string())],
&crate::MapPalette::cartographic(false),
)
.expect("a closed ring is a way");
assert!(lake.fill.is_some(), "fixture: the lake must be a FILL");
assert!(
way_draw_band(&tunnel) < way_draw_band(&lake),
"a tunnel road's band ({}) must sit UNDER a ground water fill's ({})",
way_draw_band(&tunnel),
way_draw_band(&lake)
);
// And a bridge's band sits above it, so the two are not merely both negative.
let bridge = mk(5, &[("highway", "motorway"), ("bridge", "yes")]);
assert!(way_draw_band(&bridge) > way_draw_band(&lake), "a bridge's band sits over the water");
}
/// **THE LANDMASS IS UNDER EVERY WAY THERE CAN BE — measured off `way_rank`, not
/// off the constant it is supposed to bound.**
///
/// The const assertion beside [`LANDMASS_BAND`] compares two constants. That is a
/// real gate, but it is also exactly the shape LAW 2 warns about: if
/// [`WAY_RANK_FLOOR`]'s expression ever stops matching what `way_rank` actually
/// computes, both sides move together and the assertion stays green while the map
/// goes white again.
///
/// So this walks REAL ways through the real `way_rank`: every class, in both bands,
/// at every tier, at the full negative `layer` clamp and as a `tunnel`. The number it
/// compares against is the minimum those ways produce — never the constant.
///
/// RED: put `LANDMASS_BAND` back to `-1_000` (the value that shipped 2026-08-22 to
/// 2026-08-31) and the deepest way measures −799 890 against a landmass at −1 000.
#[test]
fn no_way_can_sink_below_the_landmass() {
let pal = crate::MapPalette::cartographic(false);
let ring = |id: i64, t: &[(&str, &str)]| {
let t: Vec<(String, String)> =
t.iter().map(|(k, v)| ((*k).to_string(), (*v).to_string())).collect();
crate::Way::from_lonlat_with(
id,
&[(18.0, 59.0), (18.01, 59.0), (18.01, 59.01), (18.0, 59.01), (18.0, 59.0)],
&t,
&pal,
)
.expect("a closed ring is a way")
};
let ln = |id: i64, t: &[(&str, &str)]| {
let t: Vec<(String, String)> =
t.iter().map(|(k, v)| ((*k).to_string(), (*v).to_string())).collect();
crate::Way::from_lonlat_with(id, &[(18.0, 59.0), (18.01, 59.0)], &t, &pal)
.expect("a 2-vertex line is a way")
};
// Every class this crate can classify, as a fill AND as a line, sunk as deep as
// OSM lets a way go: the full negative clamp, and the `tunnel` that implies −1.
let classes: &[&[(&str, &str)]] = &[
&[("natural", "water")],
&[("landuse", "forest")],
&[("building", "yes")],
&[("natural", "coastline")],
&[("highway", "motorway")],
&[("highway", "footway")],
&[("waterway", "river")],
// `boundary=administrative` used to stand here as the CATCH-ALL case. It no
// longer builds a way at all: `boundary` is not a key `classify_with` reads,
// so `has_nothing_to_draw` refuses it at the constructor (lane SI — an
// untagged kommungräns was being stroked in road ink across Riddarfjärden).
// `railway=rail` takes its place: a real class, and the one whose ink korp
// re-stamps from the network column.
&[("railway", "rail")],
];
let sinkers: &[&[(&str, &str)]] = &[
&[("tunnel", "yes")],
&[("layer", "-1")],
&[("layer", "-8")],
// Past the clamp: a vandalised value must not be able to reach further down
// than `LAYER_CLAMP` says it can.
&[("layer", "-99999")],
];
let mut deepest = i32::MAX;
let mut id = 1;
for c in classes {
for s in sinkers {
let tags: Vec<(&str, &str)> = c.iter().chain(s.iter()).copied().collect();
for w in [ring(id, &tags), ln(id + 1, &tags)] {
deepest = deepest.min(way_rank(&w));
}
id += 2;
}
}
assert_ne!(deepest, i32::MAX, "the fixture must have ranked at least one way");
// NON-VACUOUS: the sinkers must really sink. Without this the claim would pass
// on a `parse_osm_z` that had stopped reading the tags at all — the state the
// whole `layer`/`bridge`/`tunnel` term was introduced to leave.
let ground = way_rank(&ring(999, &[("natural", "water")]));
assert!(
deepest < ground - LAYER_STEP / 2,
"fixture: the negative-layer ways must really sink (deepest {deepest}, a \
ground water fill {ground}) — otherwise this test bounds nothing"
);
assert!(
LANDMASS_BAND < deepest,
"the landmass ({LANDMASS_BAND}) must sit UNDER the deepest way `way_rank` can \
produce ({deepest}). It did not between 2026-08-22 and 2026-08-31, and the \
cost was the GPU lane's callback slot dropping below the land silhouette: one \
`tunnel=yes` in the loaded set and the white land painted over the whole map."
);
// …and the declared floor is the measured one, not merely below it. A
// `WAY_RANK_FLOOR` that drifted far under the truth would keep the assertion
// above green while quietly meaning nothing.
assert!(
WAY_RANK_FLOOR <= deepest && deepest - WAY_RANK_FLOOR < LAYER_STEP,
"WAY_RANK_FLOOR ({WAY_RANK_FLOOR}) must be the tight floor under the deepest \
measured way ({deepest}), within one LAYER_STEP — otherwise it is a number \
that happens to be small, not the floor it claims to be"
);
}
#[test]
fn larger_area_sorts_under_smaller() {
let big = !1.0_f32.to_bits();
let small = !0.001_f32.to_bits();
assert!(big < small, "the bigger polygon must sort FIRST, i.e. be painted under");
// And zero-area degenerates still sort last rather than panicking.
assert!(small < !0.0_f32.to_bits());
}
}