Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/facett

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
}