solverforge-maps 2.1.4

Generic map and routing utilities for VRP and similar problems
Documentation
use solverforge_maps::{BoundingBox, NetworkConfig, RoadNetwork};
use textplots::{Chart, Plot, Shape};

struct Location {
    name: &'static str,
    bbox: BoundingBox,
}

fn locations() -> Vec<Location> {
    vec![
        Location {
            name: "Philadelphia (City Hall area)",
            bbox: BoundingBox::new(39.946, -75.174, 39.962, -75.150),
        },
        Location {
            name: "Dragoncello (Poggio Rusco, IT)",
            bbox: BoundingBox::new(44.978, 11.095, 44.986, 11.108),
        },
        Location {
            name: "Clusone (Lombardy, IT)",
            bbox: BoundingBox::new(45.882, 9.940, 45.895, 9.960),
        },
    ]
}

fn plot_network(name: &str, network: &RoadNetwork, bbox: &BoundingBox) {
    let nodes: Vec<(f64, f64)> = network.nodes_iter().collect();
    let edges: Vec<(usize, usize, f64, f64)> = network.edges_iter().collect();

    if nodes.is_empty() {
        println!("\n{}: No data", name);
        return;
    }

    let mut segments: Vec<(f32, f32)> = Vec::new();
    let mut seen = std::collections::HashSet::new();
    for &(from, to, _, _) in &edges {
        let key = (from.min(to), from.max(to));
        if seen.insert(key) && from < nodes.len() && to < nodes.len() {
            let (lat1, lng1) = nodes[from];
            let (lat2, lng2) = nodes[to];
            segments.push((lng1 as f32, lat1 as f32));
            segments.push((lng2 as f32, lat2 as f32));
            segments.push((f32::NAN, f32::NAN));
        }
    }

    let intersections: Vec<(f32, f32)> = nodes
        .iter()
        .map(|(lat, lng)| (*lng as f32, *lat as f32))
        .collect();

    println!("\n{}", name);
    println!(
        "{} nodes, {} edges",
        network.node_count(),
        network.edge_count()
    );

    let x_min = bbox.min_lng as f32;
    let x_max = bbox.max_lng as f32;

    Chart::new(180, 60, x_min, x_max)
        .lineplot(&Shape::Lines(&segments))
        .lineplot(&Shape::Points(&intersections))
        .nice();
}

#[tokio::main]
async fn main() {
    let config = NetworkConfig::default();

    for loc in locations() {
        print!("\nFetching {}...", loc.name);
        match RoadNetwork::load_or_fetch(&loc.bbox, &config, None).await {
            Ok(network_ref) => {
                println!(" done");
                plot_network(loc.name, &network_ref, &loc.bbox);
            }
            Err(e) => {
                println!(" failed: {}", e);
            }
        }
    }
}