facett-map3d/tests/common/mod.rs
//! Shared fixtures for the scale-honesty tests.
//!
//! Both `ground_never_outrises_the_roofs` and `region_span_buildings_rise` ask the same
//! question — *does a quantity declared in absolute design units still mean the right
//! physical thing when the scene spans a region?* — so they build their scenes from ONE
//! set of helpers. Two copies of "a 12 m building at 18.07 E" is exactly how the two
//! halves of a scale bug come to be measured against two different scales.
// Every test binary that mounts `common` compiles its own copy and calls a subset of it
// (the `facett-map/tests/common` precedent) — an unused helper is not dead in another binary.
#![allow(dead_code)]
use facett_core::Theme;
use facett_map::Way;
/// The flat-Earth ruler these fixtures are built on — RE-EXPORTED from the extruder,
/// never re-declared. A fixture placed with a private copy of 111_320 measures a
/// different earth from the code under test the moment either side is edited, and the
/// copies agree right up until then (`granska-20`).
pub use facett_map3d::extrude::{M_PER_DEG_LAT, m_per_deg_lon};
/// Central Stockholm — korp's own served viewport centre, the camera every measurement
/// in these tests was taken at.
pub const STOCKHOLM: (f64, f64) = (18.0686, 59.3293);
/// A closed square ring in `(lon, lat)`, `half_lon`/`half_lat` degrees on a side.
pub fn ring(lon: f64, lat: f64, half_lon: f64, half_lat: f64) -> Vec<(f64, f64)> {
vec![
(lon - half_lon, lat - half_lat),
(lon + half_lon, lat - half_lat),
(lon + half_lon, lat + half_lat),
(lon - half_lon, lat + half_lat),
(lon - half_lon, lat - half_lat),
]
}
/// OSM tags from `(key, value)` pairs.
pub fn tags(kv: &[(&str, &str)]) -> Vec<(String, String)> {
kv.iter().map(|(k, v)| (k.to_string(), v.to_string())).collect()
}
/// A synthetic town whose data bbox spans `span_m` metres: a big park (ground cover),
/// a road across it, and 12 untagged 12 m-square building footprints spread along the
/// diagonal.
pub fn town(span_m: f64) -> Vec<Way> {
let (clon, clat) = STOCKHOLM;
let mpdl = m_per_deg_lon(clat);
let half_lat = 0.5 * span_m / M_PER_DEG_LAT;
let half_lon = 0.5 * span_m / mpdl;
let theme = Theme::default();
let mut ways = Vec::new();
// Ground cover spanning most of the scene (a park) — the layer that engulfed them.
ways.extend(Way::from_lonlat(
1,
&ring(clon, clat, half_lon * 0.9, half_lat * 0.9),
&tags(&[("leisure", "park")]),
&theme,
));
// A road straight across, so its ribbons are in the mesh too.
ways.push(Way::line(
2,
"road",
vec![(clon - half_lon, clat), (clon + half_lon, clat)],
egui::Color32::GRAY,
2.0,
));
// 12 real 12 m × 12 m untagged buildings — the honest OSM majority (< 1 % carry a
// `height`), so they take the default that the footprint cap bounds.
let b_lon = 6.0 / mpdl;
let b_lat = 6.0 / M_PER_DEG_LAT;
for i in 0..12 {
let t = (i as f64 + 0.5) / 12.0 - 0.5;
ways.extend(Way::from_lonlat(
100 + i,
&ring(clon + t * half_lon * 1.4, clat + t * half_lat * 1.2, b_lon, b_lat),
&tags(&[("building", "yes")]),
&theme,
));
}
ways
}
/// **The korp region viewport**: a `span_m`-wide scene (the covering tile set at a
/// zoom rung) with the buildings where they really are — packed into ONE dense town of
/// `n × n` untagged 12 m houses on a 40 m pitch, offset off the scene centre, with the
/// rest of the region filled by ground cover and roads.
///
/// This is the shape that broke every risen-geometry observable: two million triangles,
/// almost all of them buildings, spread over a bbox tens of kilometres wide, so each
/// house's metric rise lands one to two orders below an absolute design-unit threshold.
/// The cluster is deliberately OFF-CENTRE so `Map3D::frame_densest_cluster` has to
/// actually find it rather than land on it by symmetry.
pub fn region_with_town(span_m: f64, n: usize) -> Vec<Way> {
let (clon, clat) = STOCKHOLM;
let mpdl = m_per_deg_lon(clat);
let half_lat = 0.5 * span_m / M_PER_DEG_LAT;
let half_lon = 0.5 * span_m / mpdl;
let theme = Theme::default();
let mut ways = Vec::new();
// The region's ground: a lake and a forest out in the empty corners, plus two roads
// crossing the whole extent. These are what a korp viewport is mostly made of, and
// they are what a broken threshold counts INSTEAD of the houses.
ways.extend(Way::from_lonlat(
1,
&ring(clon - half_lon * 0.5, clat + half_lat * 0.5, half_lon * 0.35, half_lat * 0.35),
&tags(&[("natural", "water")]),
&theme,
));
ways.extend(Way::from_lonlat(
2,
&ring(clon + half_lon * 0.55, clat - half_lat * 0.5, half_lon * 0.3, half_lat * 0.3),
&tags(&[("landuse", "forest")]),
&theme,
));
ways.push(Way::line(
3,
"road",
vec![(clon - half_lon, clat), (clon + half_lon, clat)],
egui::Color32::GRAY,
2.0,
));
ways.push(Way::line(
4,
"road",
vec![(clon, clat - half_lat), (clon, clat + half_lat)],
egui::Color32::GRAY,
2.0,
));
// A trunk road running the FULL width of the region straight THROUGH the town. This
// is the way that defeated the town subset: it has vertices inside the 3 km window,
// so it was kept — whole — and its far endpoints dragged the retained bbox, and with
// it the scene's design unit, back out to region scale.
let (tlon0, tlat0) = (clon + half_lon * 0.33, clat + half_lat * 0.28);
ways.push(Way::line(
5,
"road",
vec![(clon - half_lon, tlat0), (tlon0, tlat0), (clon + half_lon, tlat0)],
egui::Color32::GRAY,
3.0,
));
// The town: `n × n` untagged 12 m houses on a 40 m pitch, centred a third of the
// way out toward the north-east corner of the region.
let (tlon, tlat) = (clon + half_lon * 0.33, clat + half_lat * 0.28);
let pitch_lon = 40.0 / mpdl;
let pitch_lat = 40.0 / M_PER_DEG_LAT;
let b_lon = 6.0 / mpdl;
let b_lat = 6.0 / M_PER_DEG_LAT;
let mut id = 1000i64;
for r in 0..n {
for c in 0..n {
let lon = tlon + (c as f64 - n as f64 * 0.5) * pitch_lon;
let lat = tlat + (r as f64 - n as f64 * 0.5) * pitch_lat;
ways.extend(Way::from_lonlat(
id,
&ring(lon, lat, b_lon, b_lat),
&tags(&[("building", "yes")]),
&theme,
));
id += 1;
}
}
ways
}