osm-katana/tests/clip_mask_equivalence.rs
//! **A faster clip must be the SAME clip.**
//!
//! `Polygon::contains` now answers most points from a raster mask instead of a
//! ray-cast over every vertex, because cutting Sweden out of the planet is 10.58
//! billion nodes against a 14 602-vertex border. An acceleration that quietly
//! admits or drops geometry is the exact class of defect this whole night has
//! been about — a wrong clip does not look wrong, it looks like a smaller extract.
//!
//! So the mask is not judged on speed here. It is judged on **producing the same
//! verdict as [`osm_katana::Polygon::contains_exact`] for every real node**, over
//! a real country boundary derived from real OSM data, with the nodes taken from
//! the same file.
//!
//! Note what this does and does not prove. It proves fidelity **to the polygon** —
//! which is the mask's whole job. It says nothing about whether the polygon is the
//! right shape; that is what korp's named-place guard is for. "Zero disagreements"
//! here plus "zero foreign places" there are two different claims, and neither
//! substitutes for the other.
use osm_katana::{AdminPolyOptions, Polygon};
fn fixture_bz2() -> String {
concat!(
env!("CARGO_MANIFEST_DIR"),
"/../lbzip2/test_data/liechtenstein.osm.bz2"
)
.to_string()
}
/// Liechtenstein's real `admin_level=2` boundary — 2553 vertices, comfortably over
/// the threshold at which `contains` engages the mask.
fn real_boundary() -> Polygon {
let opts = AdminPolyOptions {
admin_level: Some("2"),
..Default::default()
};
osm_katana::stream_admin_region_polygon_detailed(&fixture_bz2(), "liechtenstein", &opts)
.expect("the admin scan runs")
.expect("Liechtenstein has an admin_level=2 relation")
.polygon
}
/// **Every real node, same verdict.**
///
/// The corpus is every node in the fixture — ~350 000 real coordinates, all of
/// them inside the polygon's bbox and therefore all of them points the bbox
/// pre-filter cannot answer. That is the population the mask has to get right; a
/// generated cloud would mostly miss the boundary, which is where the only
/// interesting disagreements can be.
///
/// RED-PROVEN 2026-08-03 on oden: with `polygon_mask::mark_segment` stubbed to a
/// no-op — so cells an edge crosses are classified from their centre instead of
/// being sent to the ray-cast — this fails in BOTH directions at once, which is
/// the whole defect class: `(9.5594762, 47.2504125): mask false vs exact true`
/// (a node silently dropped) alongside `(9.5158287, 47.0884581): mask true vs
/// exact false` (a foreign node silently admitted). The companion arm below fails
/// at the same time with 10 895 of 50 459 nodes wrong at 4².
///
/// Coarsening the grid alone does NOT break it — a coarse mask is slower, never
/// wrong — which is exactly the design, and why the guard has to bite on the edge
/// walk rather than on the resolution.
#[test]
fn the_mask_gives_the_same_verdict_as_the_exact_raycast_on_real_nodes() {
let poly = real_boundary();
assert!(
poly.vertex_count() > 512,
"the fixture boundary must be big enough that `contains` engages the mask, got {}",
poly.vertex_count(),
);
let nodes =
osm_katana::xml_scan::sample_node_coords(std::path::Path::new(&fixture_bz2()), 8, 1)
.expect("sample node coordinates");
assert!(
nodes.len() > 100_000,
"a real corpus, got {} nodes",
nodes.len()
);
let bb = poly.bbox();
let in_bbox = nodes
.iter()
.filter(|&&(lon, lat)| bb.contains(lon, lat))
.count();
assert!(
in_bbox > 10_000,
"the corpus must actually exercise the polygon, not just the bbox reject: \
{in_bbox} of {} nodes are inside the bbox",
nodes.len(),
);
let mut disagreements = Vec::new();
let mut kept = 0usize;
for &(lon, lat) in &nodes {
let fast = poly.contains(lon, lat);
let exact = poly.contains_exact(lon, lat);
if fast {
kept += 1;
}
if fast != exact && disagreements.len() < 8 {
disagreements.push(format!(
"({lon:.7}, {lat:.7}): mask {fast} vs exact {exact}"
));
}
}
assert!(
disagreements.is_empty(),
"the accelerated clip disagreed with the exact ray-cast: {}",
disagreements.join("; "),
);
// Not a round-trip: the clip must actually KEEP a country's worth of nodes, so
// a mask that answered "outside" everywhere cannot pass by agreeing with a
// broken exact test.
assert!(
kept > 10_000,
"the clip kept only {kept} of {} nodes — that is not Liechtenstein",
nodes.len(),
);
}
/// **Coarsening the mask changes the cost, never the answer.**
///
/// Every grid resolution — including one so coarse that most of the country is a
/// single cell — must produce identical verdicts. This is the property that lets
/// `MASK_STEPS` be tuned for speed without anyone having to re-check correctness.
#[test]
fn every_grid_resolution_agrees_with_the_exact_raycast() {
let poly = real_boundary();
let nodes = osm_katana::xml_scan::sample_node_coords(
std::path::Path::new(&fixture_bz2()),
8,
7, // thinned: this arm runs six masks, and the population is the same shape
)
.expect("sample node coordinates");
let truth: Vec<bool> = nodes
.iter()
.map(|&(lon, lat)| poly.contains_exact(lon, lat))
.collect();
for steps in [4usize, 16, 64, 256, 1024, 4096] {
let mask = osm_katana::polygon_mask::PolygonMask::build(poly.rings(), poly.bbox(), steps);
let wrong = nodes
.iter()
.zip(&truth)
.filter(|(node, want)| {
let (lon, lat) = **node;
mask.lookup(lon, lat)
.unwrap_or_else(|| poly.contains_exact(lon, lat))
!= **want
})
.count();
assert_eq!(
wrong,
0,
"mask at {steps}² disagreed on {wrong} of {} nodes",
nodes.len()
);
let (boundary, inside, outside) = mask.cell_counts();
assert_eq!(
boundary + inside + outside,
steps * steps,
"every cell must be classified as exactly one of the three",
);
}
}
/// A finer grid must decide MORE queries — otherwise the mask is paying memory
/// for nothing. Measured on the real boundary rather than asserted in the
/// abstract, because a shape with a long enough coastline can defeat a coarse
/// grid entirely.
#[test]
fn a_finer_grid_sends_fewer_points_to_the_raycast() {
let poly = real_boundary();
let share = |steps: usize| {
let m = osm_katana::polygon_mask::PolygonMask::build(poly.rings(), poly.bbox(), steps);
let (b, _, _) = m.cell_counts();
b as f64 / (steps * steps) as f64
};
let coarse = share(64);
let fine = share(1024);
assert!(
fine < coarse,
"a finer grid must shrink the boundary share, got {coarse:.4} → {fine:.4}",
);
assert!(
fine < 0.25,
"at 1024² most of a country's bbox must be decidable by lookup, got {fine:.4}",
);
}