facett-map/tests/place_label_tier_pixels.rs
//! **PLACE NAMES ON THE CANVAS, AS PIXELS, ON A LADDER** — the label half of `railtier-1`.
//!
//! `korp/src/map_paint.rs` recorded the census that named this: on the owner's live thin
//! client (`korp BUILD 7a8733e91f` against oden `:50051`),
//! `$KORP_STATE.map.scene.applied.layers` at camera **9.33** read
//!
//! ```text
//! labels | fed 22 269 | DRAWN 0
//! ```
//!
//! and the note beside it was honest about only half of that being explained: a street name
//! is `Lod::City` and correctly culled at 9.33, but `classify_label` tiers
//! `place=city|town` at `Lod::Region` — camera **6** — so a town label should have drawn and
//! the total should not have been 0.
//!
//! The cause was on korp's side of the seam and is fixed there (`map_layers::Feed`: the
//! `place-names` bucket had no path into any renderer). What this file pins is the half that
//! lives HERE: that the tier ladder the fix depends on is a LADDER and not a threshold.
//!
//! # Why pixels and why four renders
//!
//! `lod_visible` returning `true` is a statement about a comparison; `label_placement()`
//! returning `placed == 1` is a state round-trip. The owner's complaint was about a canvas, so
//! this paints the canvas and counts the ink — the same argument, harness and hue-census
//! `rail_tier_pixels.rs` makes, deliberately reusing its shape rather than growing a second
//! one (LAW 5).
//!
//! And ONE count is not a measurement. Four renders, each of which the other three cannot
//! stand in for:
//!
//! | render | tier | must |
//! |---|---|---|
//! | `place=city` at camera 6 | Region (floor 6) | **paint** — the claim |
//! | `place=city` at camera 5 | Region (floor 6) | **paint nothing** — the gate bites |
//! | `place=village` at camera 6 | City (floor 12) | **paint nothing** — the declutter |
//! | `place=village` at camera 12 | City (floor 12) | **paint** — the rung opens |
//!
//! A build that lifted every `place=*` to `Lod::Country` passes rows 1 and 4 and fails 2
//! and 3; a build that dropped the whole label pass passes 2 and 3 and fails 1 and 4. Only a
//! working ladder passes all four.
//!
//! # THE MEASURED NUMBERS, AND THE RED — both, on 2026-08-27
//!
//! Green, this build: row 1 **159 px**, row 2 **0 px**, row 3 **0 px**, row 4 **159 px**,
//! `place=country` at camera 3 **159 px**, and every reference render (same camera, no label
//! layer) **0 px**. 159 and not four figures because the label font is 12 px and the census
//! counts glyph pixels of ONE 26-glyph name — the count that matters is that it is three
//! digits away from the 0 beside it, not that it is large.
//!
//! Watched RED twice, and both reds fired on the row they should:
//!
//! 1. `"city" | "town" => (Lod::City, 0.95)` in `classify_label` — row 1 goes **159 → 0**
//! and the failure names *"a place=city name put 0 px … at camera 6"*. Nothing else moves:
//! a demoted town is still absent at 5 and still absent from the Country rung.
//! 2. the `lod_visible` filter deleted from `LabelLayer::features` (replaced with
//! `.filter(|_| true)`) — row 2 goes **0 → 159 px** (*"a place=city name put 159 px on
//! the canvas at camera 5, one rung BELOW its own tier"*) and the Country test's own
//! non-vacuity fires with it (*"a place=city name drew 159 px at camera 3 as well"*).
//!
//! That is the pair the file is for: red 1 without red 2 would leave a build that paints
//! every name at every camera green, and red 2 without red 1 would leave a build that paints
//! nothing green.
//!
//! Run: `cargo test -p facett-map --test place_label_tier_pixels -- --test-threads=1`
//!
//! ⚠ `--test-threads=1` is not tidiness: two `egui_kittest` `render()` calls in parallel
//! SIGSEGV on this host (measured — signal 11 on the first run of this file), because the
//! harness brings up its own wgpu adapter per test.
use facett_map::layer::{LabelLayer, LatLon, MapFacet, MapTheme, MapView};
/// One tagged label feature: `(id, position, OSM tags)`.
type TaggedLabel = (i64, LatLon, Vec<(String, String)>);
// One render at a time per binary — see the module's own doc for the measured SIGSEGV.
#[path = "common/one_gpu.rs"]
mod one_gpu;
/// The pane korp's 2D canvas actually gets on a 1900×1150 window — the same constant
/// `label_collision.rs` measures in, so a count here and a box count there are about one
/// picture.
const CANVAS: egui::Vec2 = egui::vec2(1258.0, 728.0);
/// Central Stockholm, the centre every map measurement in this constellation uses.
const CLON: f64 = 18.0686;
const CLAT: f64 = 59.3293;
/// **One place node with a LONG name, at the centre of the pane.**
///
/// Long on purpose: the label font is 12 px and its glyph strokes are ~1 px wide with
/// antialiasing on both sides, so a short name puts very few pixels anywhere near the pure
/// ink and a census of it measures the feather. `"Södertäljeviken-Norrköping"` is 26
/// glyphs — the length of a real Swedish compound place name — which is what makes the
/// counts below three digits instead of one and therefore able to move.
fn place(value: &str) -> Vec<TaggedLabel> {
vec![(
1,
LatLon::new(CLAT, CLON),
vec![
("place".to_string(), value.to_string()),
("name".to_string(), "Södertäljeviken-Norrköping".to_string()),
],
)]
}
/// Render the pane at `zoom` with (or without) the place-name layer, and hand back the
/// theme it painted with so the census reads the SAME palette the paint used.
///
/// One theme for both halves — the trap `rail_tier_pixels.rs` documents: a census against a
/// theme the pane did not paint with is a census for the wrong hue.
fn render(zoom: f32, labels: bool, value: &str) -> (MapTheme, image::RgbaImage) {
let map_theme = MapTheme::default();
let view = MapView::new(LatLon::new(CLAT, CLON), zoom, (CANVAS.x, CANVAS.y));
let mut f = MapFacet::new("place-label-tier-pixels", view, map_theme.clone());
if labels {
f.push_layer(Box::new(LabelLayer::from_tagged("place-names", place(value))));
}
let mut h = egui_kittest::Harness::builder().with_size(CANVAS).build_ui(move |ui| {
f.ui(ui);
});
h.run_steps(3);
(map_theme, h.render().expect("the CPU painter rendered into a framebuffer"))
}
/// Chebyshev distance between an sRGB triple and a colour — `rail_tier_pixels.rs`'s.
fn dist(a: [u8; 3], b: egui::Color32) -> i32 {
(i32::from(a[0]) - i32::from(b.r()))
.abs()
.max((i32::from(a[1]) - i32::from(b.g())).abs())
.max((i32::from(a[2]) - i32::from(b.b())).abs())
}
/// The rows of the image that are MAP CANVAS — mostly the land colour.
///
/// Without finding the canvas first a census counts the harness chrome; `cpu_casing.rs` was
/// once measured wrong exactly this way (a 4.5 px road reported as a 640 px band).
fn canvas_rows(img: &image::RgbaImage, land: egui::Color32) -> (u32, u32) {
let mut rows: Vec<u32> = Vec::new();
for y in 0..img.height() {
let n = (0..img.width())
.filter(|&x| {
let p = img.get_pixel(x, y).0;
dist([p[0], p[1], p[2]], land) <= 6
})
.count();
if n as f32 > img.width() as f32 * 0.5 {
rows.push(y);
}
}
assert!(!rows.is_empty(), "no row of this render is mostly land — the pane never painted");
(rows[0], rows[rows.len() - 1])
}
/// Pixels within the canvas rows whose hue is `ink`, within `tol`.
fn ink_count(img: &image::RgbaImage, ink: egui::Color32, rows: (u32, u32), tol: i32) -> u64 {
let mut n = 0u64;
for y in rows.0..=rows.1 {
for x in 0..img.width() {
let p = img.get_pixel(x, y).0;
if dist([p[0], p[1], p[2]], ink) <= tol {
n += 1;
}
}
}
n
}
/// The label ink — `LabelLayer::label_style`'s colour. Read as a constant here rather than
/// asked of the layer because `LayerStyle` is what the paint reads and it is not public per
/// layer; the pair is asserted apart from the land below, which is what makes the census
/// mean something.
const LABEL_INK: egui::Color32 = egui::Color32::GRAY;
/// Glyph strokes are ~1 px under antialiasing, so a pure-ink census would count almost
/// nothing. 40 is wide enough to take a glyph's core and its first feather ring, and the
/// test asserts the land is more than 80 away — so the land can never be what passes.
const TOL: i32 = 40;
/// One rung of the ladder, measured: the ink a `place=<value>` node puts on the canvas at
/// `zoom`, minus whatever the same pane carries with no label layer at all.
fn rung(zoom: f32, value: &str) -> (u64, u64, egui::Color32) {
let (theme, with) = render(zoom, true, value);
let (_, without) = render(zoom, false, value);
let land = theme.palette.land;
let a = ink_count(&with, LABEL_INK, canvas_rows(&with, land), TOL);
let b = ink_count(&without, LABEL_INK, canvas_rows(&without, land), TOL);
(a, b, land)
}
/// **THE RUNG NOTHING STOOD ON: a village draws at camera 9, and a hamlet does not.**
///
/// `LOD_TIER_ZOOMS` is `[0, 6, 9, 12, 15]`, and until `label-district-1` **no settlement
/// class resolved to `Lod::District` at all** — country/state went to Country, city/town
/// to Region, and every remaining class straight to City. So the ladder had a six-level
/// dead band: a town appeared at camera 6 and nothing new was named until camera 12,
/// which is 300 km down to 40 km of pane — the band a person navigates in.
///
/// MEASURED on the live client 2026-08-31 at camera 11.3: `labels` **5 319 fed / 1
/// drawn** against a pane budget of ~65. Not overloaded — starved.
///
/// SEEN RED with `"village" => (Lod::City, 0.65)` put back: the village paints **0 px**
/// at camera 9 and at camera 10.
///
/// The second half is what stops this from being the clutter defect from the other side.
/// `hamlet` must STAY at City: measured over the whole 2026-08-31 Sweden cut there are
/// **14 348 named hamlets and 25 221 isolated dwellings** against **996 villages**, so
/// promoting the small classes would put tens of thousands of names on a regional pane
/// and the areal budget would spend itself on farmsteads.
#[test]
fn a_village_stands_on_the_district_rung_and_a_hamlet_does_not() {
let _gpu = one_gpu::hold();
// The rung opens at 9 — the village is named there, where nothing named it before.
let (village_at_9, ref_at_9, _) = rung(9.0, "village");
assert_eq!(ref_at_9, 0, "the reference pane at camera 9 is not empty");
assert!(
village_at_9 > 100,
"a place=village name drew only {village_at_9} px at camera 9. District is \
LOD_TIER_ZOOMS[2] == 9, so the rung is open and the name must be on the canvas — \
this is the whole of the dead band between Region (6) and City (12)."
);
// And one rung BELOW it still holds nothing: District opens at 9, not at 6.
let (village_at_6, _, _) = rung(6.0, "village");
assert_eq!(
village_at_6, 0,
"a place=village name put {village_at_6} px on the canvas at camera 6, one rung \
below District. Without this the assertion above would pass on a build that had \
lifted every place=* to Region, which is a threshold and not a ladder."
);
// THE OTHER SIDE: the dense classes stay where they were, or the pane fills with
// farmsteads. 25 221 isolated dwellings and 14 348 hamlets in one country.
let (hamlet_at_9, _, _) = rung(9.0, "hamlet");
assert_eq!(
hamlet_at_9, 0,
"a place=hamlet name drew {hamlet_at_9} px at camera 9. Hamlets outnumber \
villages fourteen to one in the measured cut; promoting them is the clutter \
defect this rung exists to avoid."
);
}
/// **THE LADDER: a town draws at camera 6 and not at 5; a village draws at 12 and not at 6.**
#[test]
fn a_town_name_paints_at_a_regional_camera_and_a_village_name_waits_for_a_city_one() {
let _gpu = one_gpu::hold();
// ── THE REGION INSTRUMENT IS `town`, NOT `city` (`label-city-country-1`) ────────
// `place=city` used to share Region with `town` and was this test's instrument for
// the rung. It is `Lod::Country` now — there are SIX named `place=city` in the whole
// 2026-08-31 Sweden cut against one `place=country`, and a whole-country pane that
// opened no rung but Country drew literally no names (measured live at camera 5.6:
// 15 797 fed / 0 drawn). `town` (67 nationally) is the class Region is for, so it is
// the honest instrument here. The PROPERTY this test guards is unchanged; only what
// it is measured with moved, and the `city` tier is pinned explicitly below so the
// move cannot happen again silently.
let (city_at_6, ref_at_6, land) = rung(6.0, "town");
// The census is only a census if the two hues are far apart. Asserted before anything
// else, because every count below is meaningless if this fails.
assert!(
dist([land.r(), land.g(), land.b()], LABEL_INK) > 80,
"the label ink {LABEL_INK:?} and the land {land:?} are within 80 of each other — a \
tolerance-{TOL} census cannot tell them apart and every count below is noise"
);
assert_eq!(
ref_at_6, 0,
"an empty pane at camera 6 already carries {ref_at_6} px of the label hue — the \
reference render is not a reference"
);
// ── RUNG 1: the claim. `place=town` is Lod::Region, LOD_TIER_ZOOMS[Region] == 6.
assert!(
city_at_6 > 100,
"a place=town name put {city_at_6} px of {LABEL_INK:?} on the canvas at camera 6. \
`classify_label` tiers place=town at Lod::Region and LOD_TIER_ZOOMS enters \
Region at exactly 6, so this is the rung a town name is FOR. On the owner's live \
client the whole `labels` row read 22 269 fed / 0 drawn — half of that was the \
street names being correctly culled, and this is the half that was not."
);
// ── RUNG 2: the gate bites. One rung below Region there is only Country.
let (city_at_5, _, _) = rung(5.0, "town");
assert_eq!(
city_at_5, 0,
"a place=town name put {city_at_5} px on the canvas at camera 5, one rung BELOW its \
own tier. Without this the assertion above would pass on a build that had lifted \
every place=* to Lod::Country — which is the clutter defect from the other side, \
and is what a threshold rather than a ladder looks like."
);
// ── THE NEW TOP RUNG, PINNED. A city IS Country now, and that is deliberate.
let (city_at_5_country, _, _) = rung(5.0, "city");
assert!(
city_at_5_country > 100,
"a place=city name drew only {city_at_5_country} px at camera 5 — the rung it was \
moved to (Country, floor 0) is what makes a whole-country pane carry any name at \
all. Six named cities exist in the measured cut; the areal budget is ~65."
);
// ── RUNG 3: the declutter. A village is Lod::District (9), so nothing at 6.
let (village_at_6, _, _) = rung(6.0, "village");
assert_eq!(
village_at_6, 0,
"a place=village name put {village_at_6} px on the canvas at camera 6. \
`classify_label`'s `_ =>` arm holds village/suburb/hamlet/locality at Lod::City \
(camera 12) on purpose: a regional view of Sweden carrying every hamlet is the \
grey static `label_collision.rs` exists to prevent."
);
// ── RUNG 4: …and the rung OPENS. A tier that never opens is a wall.
let (village_at_12, ref_at_12, _) = rung(12.0, "village");
assert_eq!(ref_at_12, 0, "the camera-12 reference render is not a reference");
assert!(
village_at_12 > 100,
"at camera 12 the village name drew only {village_at_12} px. A tier that never \
opens is a wall, not a rung — and rung 3 above would pass on a build where the \
label pass had been deleted outright."
);
}
/// **THE COUNTRY RUNG — the top of the ladder, and the one korp's `Rails` floors made
/// necessary to state.**
///
/// `place=country|state|province|region` is `Lod::Country`, floor 0: visible at EVERY
/// camera. Its own test because `Lod::Country` is the one tier whose correctness cannot be
/// shown by a pair — there is no camera below it to be absent from — so what it needs
/// instead is a camera far wider than any the other rungs use.
#[test]
fn a_country_name_paints_at_a_camera_no_other_rung_survives() {
let _gpu = one_gpu::hold();
// Camera 3 — two rungs below Region's floor and wider than anything the ladder test
// above touches, so nothing but a Country-tier label can be what paints here.
let (country_at_3, ref_at_3, land) = rung(3.0, "country");
assert!(dist([land.r(), land.g(), land.b()], LABEL_INK) > 80, "ink and land too close");
assert_eq!(ref_at_3, 0, "the camera-3 reference render is not a reference");
assert!(
country_at_3 > 100,
"a place=country name drew {country_at_3} px at camera 3. Lod::Country is floor 0 — \
every camera there is — and this is the rung the whole country→region→city \
declutter backbone hangs from."
);
// NON-VACUITY, and it is the load-bearing half: a `city` at the same camera must draw
// NOTHING, or "the Country rung works" is a statement about a pane that paints text
// whatever the tier says.
// The non-vacuity instrument is `town` (Region, floor 6), not `city`: `city` moved
// to Country in `label-city-country-1` — six of them exist nationally and a
// whole-country pane drew no names without them. `town` is the class Region is for
// (67 nationally) and is therefore what proves the Country rung is distinguishable.
let (city_at_3, _, _) = rung(3.0, "town");
assert_eq!(
city_at_3, 0,
"a place=town name drew {city_at_3} px at camera 3 as well — then this test is not \
measuring the Country rung, it is measuring a pane with no LOD gate at all."
);
}