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/examples/relmeasure.rs

//! **How many relations reach the pyramid, before and after the exclusion fix.**
//!
//! Measured against a REAL `relations.resolved.parquet`, with the REAL predicate
//! (`osm_katana::network::carried_by_a_network`) — not a fixture and not a replica.
//!
//! `cargo run --release --example relmeasure -- <relations.resolved.parquet>`

use arrow::array::{Array, BinaryArray, StringArray};
use osm_katana::network::{carried_by_a_network, Source};
use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
use std::collections::BTreeMap;

fn wkb_type(b: &[u8]) -> Option<u32> {
    if b.len() < 5 || b[0] != 1 {
        return None;
    }
    Some(u32::from_le_bytes([b[1], b[2], b[3], b[4]]))
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let path = std::env::args().nth(1).expect("usage: relmeasure <relations.resolved.parquet>");
    let f = std::fs::File::open(&path)?;
    let rdr = ParquetRecordBatchReaderBuilder::try_new(f)?.with_batch_size(4096).build()?;

    let (mut rows, mut with_geom, mut matched, mut matched_with_geom, mut mp) = (0u64, 0u64, 0u64, 0u64, 0u64);
    let mut classes: BTreeMap<String, u64> = BTreeMap::new();

    for batch in rdr {
        let batch = batch?;
        let geom = batch.column_by_name("geometry").and_then(|c| c.as_any().downcast_ref::<BinaryArray>());
        let tags = batch.column_by_name("tags").and_then(|c| c.as_any().downcast_ref::<StringArray>());
        for i in 0..batch.num_rows() {
            rows += 1;
            let has_geom = geom.map(|g| !g.is_null(i) && !g.value(i).is_empty()).unwrap_or(false);
            if has_geom {
                with_geom += 1;
            }
            let t = match tags {
                Some(a) if !a.is_null(i) && !a.value(i).is_empty() => a.value(i),
                _ => continue,
            };
            // **The OLD rule.** `is_way` was true for a relation, so the tiler asked
            // the ways predicate — the one ROADS and RAILS are cut with.
            if !carried_by_a_network(Source::Ways, t) {
                continue;
            }
            matched += 1;
            if !has_geom {
                continue;
            }
            matched_with_geom += 1;
            if wkb_type(geom.unwrap().value(i)) == Some(6) {
                mp += 1;
            }
            for kv in t.split('\u{1}') {
                if let Some((k, v)) = kv.split_once('\u{2}').or_else(|| kv.split_once('=')) {
                    if k == "highway" || k == "railway" {
                        *classes.entry(format!("{k}={v}")).or_default() += 1;
                    }
                }
            }
        }
    }

    // The NEW rule: `SrcKind::Relation::network_source()` is `None`, so the predicate
    // is not consulted at all and nothing is excluded on a relation.
    println!("file                      {path}");
    println!("relation rows             {rows}");
    println!("  with assembled geometry {with_geom}");
    println!("match the ways predicate  {matched}");
    println!("  of those, with geometry {matched_with_geom}   <- what the OLD rule dropped");
    println!("  of those, MultiPolygon  {mp}");
    println!();
    println!("REACHING THE PYRAMID");
    println!("  before (Relation => Some(Ways)) {}", with_geom - matched_with_geom);
    println!("  after  (Relation => None)       {with_geom}");
    let mut v: Vec<_> = classes.into_iter().collect();
    v.sort_by_key(|(_, n)| std::cmp::Reverse(*n));
    for (k, n) in v.into_iter().take(8) {
        println!("    {k:32} {n}");
    }
    Ok(())
}