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

nordisk/znippy-zoomies

osm-katana/tests/admin_poly_bz2.rs

//! **The admin boundary comes out of the planet, not off the internet.**
//!
//! `stream_admin_region_polygon` could only read a `.osm.pbf`, while the planet on
//! the machine that needs a clip polygon is a 173 GB `.osm.bz2`. Because of that
//! one gap a Sweden outline was once *downloaded* rather than cut from our own
//! data. These tests pin the wiring that closes it:
//!
//!   * a real `.osm.bz2` (many bz2 blocks) yields the country's boundary, and it
//!     is the RIGHT country — asserted at named places, the only check that can
//!     tell one outline from another;
//!   * the bz2 reader and the PBF reader agree **exactly**, proven by converting
//!     the same fixture to PBF with this crate's own converter and deriving again;
//!   * `admin_level` actually filters, and an unknown name is a fact (`None`),
//!     not a silent empty polygon.
//!
//! Fixture: `lbzip2/test_data/liechtenstein.osm.bz2` (5.2 MB → ~60 MB of XML,
//! spanning many bz2 blocks). A country small enough to test in seconds and real
//! enough to have a genuine `admin_level=2` relation with 40 member ways.

use osm_katana::{AdminPolyOptions, Polygon};

/// The bz2 OSM fixture, in the sibling `lbzip2` crate's test data.
fn fixture_bz2() -> String {
    concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/../lbzip2/test_data/liechtenstein.osm.bz2"
    )
    .to_string()
}

/// Derive Liechtenstein's `admin_level=2` boundary from `path`.
fn derive(path: &str) -> osm_katana::AdminRegionPolygon {
    let opts = AdminPolyOptions {
        admin_level: Some("2"),
        ..Default::default()
    };
    osm_katana::stream_admin_region_polygon_detailed(path, "liechtenstein", &opts)
        .expect("the admin scan runs")
        .expect("Liechtenstein has a boundary=administrative admin_level=2 relation")
}

/// **The country that comes out is the country that went in.**
///
/// A polygon-shaped result proves nothing: the old bundled Sweden asset was a
/// valid `.poly` that put Visby outside Sweden. So this asserts containment at
/// named places on both sides of three international borders.
#[test]
fn bz2_admin_scan_derives_the_real_country_boundary() {
    let d = derive(&fixture_bz2());

    assert_eq!(
        d.relations.len(),
        1,
        "exactly one admin_level=2 relation is named Liechtenstein"
    );
    assert_eq!(
        d.relations[0].admin_level, "2",
        "the level filter was applied"
    );
    assert_eq!(d.open_rings, 0, "every member-way chain closed into a ring");
    assert_eq!(d.polygon.ring_count(), 1, "Liechtenstein is one ring");
    // PINNED, not bounded: the fixture is a committed file, so the derivation is
    // deterministic — 2553 vertices, the same figure `clip_mask_equivalence.rs:30`
    // documents. The old `> 2_000` could not tell "the real border" from "the real
    // border quietly lost a fifth of its vertices".
    assert_eq!(
        d.polygon.vertex_count(),
        2_553,
        "Liechtenstein's admin_level=2 border derives from the committed fixture \
         with exactly 2553 vertices; a different count means the ring assembly or \
         the reader changed behaviour",
    );

    for (name, lon, lat) in [
        ("Vaduz", 9.5215, 47.1410),
        ("Schaan", 9.5090, 47.1660),
        ("Balzers", 9.5040, 47.0660),
    ] {
        assert!(
            d.polygon.contains(lon, lat),
            "{name} must be INSIDE Liechtenstein"
        );
    }
    // Neighbours a few km away across the Rhine and the Austrian border — the
    // distances that a merely plausible outline gets wrong.
    for (name, lon, lat) in [
        ("Feldkirch, Austria", 9.5959, 47.2380),
        ("Buchs, Switzerland", 9.4770, 47.1670),
        ("Sargans, Switzerland", 9.4450, 47.0480),
        ("Bludenz, Austria", 9.8220, 47.1560),
        ("Zürich, Switzerland", 8.5417, 47.3769),
    ] {
        assert!(
            !d.polygon.contains(lon, lat),
            "{name} must be OUTSIDE Liechtenstein"
        );
    }
}

/// **The bz2 reader and the PBF reader must not drift.**
///
/// Same OSM data, two container formats, two very different readers: the PBF path
/// mmaps and addresses blobs at random; the bz2 path streams bit-aligned blocks
/// through gatling and stitches the elements that straddle a block boundary. The
/// fixture is converted to PBF by this crate's own `xml2pbf`, so the comparison
/// needs nothing from outside the repo.
///
/// Byte-equality of the emitted `.poly` is the strongest available statement: same
/// rings, same vertex order, same coordinates to 7 decimals.
#[test]
fn bz2_and_pbf_readers_derive_the_same_boundary() {
    let dir = tempfile::tempdir().expect("tempdir");
    let pbf = dir.path().join("liechtenstein.osm.pbf");
    let (elems, bytes) = osm_katana::xml_to_pbf::xml_to_pbf_bz2_path(
        std::path::Path::new(&fixture_bz2()),
        &pbf,
        8,
        /*skip_to_nodes=*/ false,
    )
    .expect("bz2 → pbf conversion");
    assert!(
        elems > 100_000 && bytes > 0,
        "the converted PBF holds the fixture ({elems} elements)"
    );

    let from_bz2 = derive(&fixture_bz2());
    let from_pbf = derive(pbf.to_str().expect("utf8 temp path"));

    assert_eq!(
        from_bz2.relations[0].id, from_pbf.relations[0].id,
        "both readers matched the same relation",
    );
    assert_eq!(
        from_bz2.polygon.to_poly_str("liechtenstein"),
        from_pbf.polygon.to_poly_str("liechtenstein"),
        "the bz2 sweep and the PBF blob scan derived DIFFERENT geometry from the same data",
    );
}

/// A derived boundary survives the `.poly` round trip byte-for-byte in geometry:
/// what is written to `korp/assets/sweden.poly` is what was cut from the planet.
#[test]
fn derived_boundary_round_trips_through_the_poly_format() {
    let d = derive(&fixture_bz2());
    let text = d.polygon.to_poly_str("liechtenstein");
    let reparsed = Polygon::from_poly_str(&text).expect("what we write, we can read");

    assert_eq!(reparsed.ring_count(), d.polygon.ring_count());
    assert_eq!(reparsed.vertex_count(), d.polygon.vertex_count());
    assert!(
        reparsed.contains(9.5215, 47.1410),
        "Vaduz survives the round trip"
    );
    assert!(
        !reparsed.contains(9.5959, 47.2380),
        "Feldkirch stays out after the round trip"
    );
    assert_eq!(
        text,
        reparsed.to_poly_str("liechtenstein"),
        "writing what we parsed reproduces the file",
    );
}

/// **The region CATALOG reads the bz2 planet too — and a format it cannot read
/// is an ERROR, never an empty list.**
///
/// `stream_admin_region_names` was PBF-only. Handed a `.osm.bz2` it did not fail:
/// there are no PBF blobs in a bzip2 stream, so the blob scan found none, the
/// fan-out ran over zero units, and it returned `Ok(vec![])` — the same value as
/// "this planet has no administrative regions". The collectors daemon treats an
/// empty catalog as a documented fact, so it went ONLINE with an empty region
/// pick-list and re-swept 173 GB on every tick, forever, with nothing red anywhere.
///
/// So this asserts BOTH halves of the distinction:
///   * a `.osm.bz2` now yields the real catalog;
///   * the PBF reader handed that same bz2 yields a NAMED error
///     ([`osm_katana::UnreadablePlanet`]) — and specifically not `Ok(empty)`.
#[test]
fn catalog_reads_bz2_and_names_an_unreadable_planet_instead_of_going_empty() {
    let path = fixture_bz2();

    let names = osm_katana::stream_admin_region_names(&path).expect("catalog scan on a .osm.bz2");
    assert!(
        names.iter().any(|n| n == "Liechtenstein"),
        "the bz2 catalog must hold the country itself; got {} names: {:?}",
        names.len(),
        &names[..names.len().min(8)],
    );
    assert!(
        names.len() > 5,
        "a real extract has more than the country in it: {}",
        names.len()
    );

    // The old path, on the same file. It must FAIL, by name — the empty list it
    // used to return is the bug this whole wiring exists to kill.
    let err = osm_katana::stream_admin_region_names_pbf(&path)
        .expect_err("the PBF reader cannot read a bz2 and must say so, not return Ok(empty)");
    let named = err
        .downcast_ref::<osm_katana::UnreadablePlanet>()
        .expect("the failure is downcastable to UnreadablePlanet so a caller can act on it");
    assert!(
        named.path.ends_with(".osm.bz2"),
        "the error names the file: {named}"
    );
}

/// **`Ok(vec![])` still has to be reachable, and mean what it says.**
///
/// If "unreadable" and "empty" were merged the other way — everything an error —
/// the distinction would be just as useless. A readable PBF that genuinely holds
/// no administrative relation returns an honest empty catalog, while a file that
/// is not a planet at all returns [`osm_katana::UnreadablePlanet`].
#[test]
fn honest_empty_and_unreadable_are_different_answers() {
    let dir = tempfile::tempdir().expect("tempdir");

    // A REAL, readable PBF with two nodes and not one relation.
    let xml = br#"<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="test">
 <node id="1" lat="47.1" lon="9.5" version="1"/>
 <node id="2" lat="47.2" lon="9.6" version="1"/>
</osm>
"#;
    let empty_pbf = dir.path().join("no-regions.osm.pbf");
    osm_katana::xml_to_pbf::xml_to_pbf_raw(&xml[..], &empty_pbf, 2).expect("xml → pbf");
    assert_eq!(
        osm_katana::planet_format(empty_pbf.to_str().unwrap()).expect("sniff"),
        osm_katana::PlanetFormat::Pbf,
        "a converted PBF sniffs as a PBF",
    );
    let names = osm_katana::stream_admin_region_names(empty_pbf.to_str().unwrap())
        .expect("a readable PBF with no regions is Ok, not an error");
    assert!(names.is_empty(), "an honest empty catalog: {names:?}");

    // Files that are NOT planets: gzip magic, and something far too short.
    let gz = dir.path().join("planet.osm.gz");
    std::fs::write(&gz, [0x1f, 0x8b, 0x08, 0x00, 0, 0, 0, 0]).expect("write gz stub");
    let err = osm_katana::stream_admin_region_names(gz.to_str().unwrap())
        .expect_err("a .osm.gz is not readable by the admin scan and must say so");
    let named = err
        .downcast_ref::<osm_katana::UnreadablePlanet>()
        .expect("named failure");
    assert!(
        named.detail.contains("gzip"),
        "the error says what it saw: {named}"
    );

    let stub = dir.path().join("truncated.osm.bz2");
    std::fs::write(&stub, b"BZ").expect("write stub");
    let err = osm_katana::stream_admin_region_names(stub.to_str().unwrap())
        .expect_err("a two-byte file is not a planet");
    assert!(
        err.downcast_ref::<osm_katana::UnreadablePlanet>().is_some(),
        "named failure: {err}"
    );
}

/// **Two consumers, one pass.** The catalog and the boundary come out of a single
/// sweep of the relation tail, and each is identical to what its own dedicated
/// scan produces — the whole point of queueing extractions onto one planet pass.
#[test]
fn catalog_and_polygon_ride_one_pass_with_no_loss() {
    let path = fixture_bz2();
    let opts = AdminPolyOptions {
        admin_level: Some("2"),
        ..Default::default()
    };

    let (names, poly) =
        osm_katana::stream_admin_catalog_and_polygon_bz2(&path, "liechtenstein", &opts)
            .expect("shared pass");
    let poly = poly.expect("the boundary is derived in the shared pass too");

    let names_alone = osm_katana::stream_admin_region_names(&path).expect("catalog alone");
    let poly_alone = derive(&path);

    assert_eq!(names, names_alone, "the shared pass lost catalog names");
    assert_eq!(
        poly.polygon.to_poly_str("x"),
        poly_alone.polygon.to_poly_str("x"),
        "the shared pass derived different geometry than the dedicated one",
    );
}

/// `admin_level` is a FILTER, not decoration — pin the wrong level and the
/// country's own relation must not answer. Without the filter the level-2
/// relation is found, so this cannot pass by accident.
#[test]
fn admin_level_filter_and_unknown_name_are_facts() {
    let path = fixture_bz2();

    let any_level = AdminPolyOptions::default();
    assert!(
        osm_katana::stream_admin_region_polygon_detailed(&path, "liechtenstein", &any_level)
            .expect("scan runs")
            .is_some(),
        "with no level pinned the country IS found (so the negatives below mean something)",
    );

    let level_9 = AdminPolyOptions {
        admin_level: Some("9"),
        ..Default::default()
    };
    assert!(
        osm_katana::stream_admin_region_polygon_detailed(&path, "liechtenstein", &level_9)
            .expect("scan runs")
            .is_none(),
        "no relation named Liechtenstein at admin_level=9 — the level filter must bite",
    );

    let opts = AdminPolyOptions {
        admin_level: Some("2"),
        ..Default::default()
    };
    assert!(
        osm_katana::stream_admin_region_polygon_detailed(&path, "atlantis", &opts)
            .expect("scan runs")
            .is_none(),
        "an unknown region is None (a fact the caller can act on), never an empty polygon",
    );
}