Documentation
Build your first road-network-backed travel-time matrix with solverforge-maps.
Getting Started with solverforge-maps
This guide covers the standard solverforge-maps workflow:
- validate input coordinates
- derive a bounding box that covers the relevant area
- load a road network from cache or Overpass
- compute a travel-time matrix
- read same-path route distances from the matrix
- optionally compute a route for visualization or debugging
Prerequisites
- Rust stable toolchain
- An async runtime such as Tokio
- Internet access the first time a new road network is fetched
Add the Dependency
[dependencies]
solverforge-maps = "2"
tokio = { version = "1", features = ["full"] }
Step 1: Start with Validated Coordinates
Coord::try_new is the right choice for user input, CSV imports, and API payloads because it rejects invalid latitude and longitude values.
use solverforge_maps::Coord;
let depot = Coord::try_new(39.9526, -75.1652)?;
let customer_a = Coord::try_new(39.9610, -75.1700)?;
let customer_b = Coord::try_new(39.9440, -75.1500)?;
let locations = vec![depot, customer_a, customer_b];
Step 2: Build a Routing Bounding Box
Use BoundingBox::from_coords and then expand it for realistic road detours.
use solverforge_maps::BoundingBox;
let bbox = BoundingBox::from_coords(&locations).expand_for_routing(&locations);
That expansion matters because road routes rarely travel in a straight line. A tight bounding box can clip the roads needed for a valid path.
Step 3: Load or Fetch the Road Network
use solverforge_maps::{NetworkConfig, RoadNetwork};
let config = NetworkConfig::default();
let network = RoadNetwork::load_or_fetch(&bbox, &config, None).await?;
load_or_fetch gives you the normal production behavior:
- reuse the in-memory cache when possible
- reuse the file cache when the region was fetched before
- fall back to the Overpass API only when necessary
Step 4: Compute the Travel-Time Matrix
let matrix = network.compute_matrix(&locations, None).await;
println!("Matrix size: {}", matrix.size());
println!("Travel time 0 -> 1: {:?} seconds", matrix.get(0, 1));
println!("Distance 0 -> 1: {:?} meters", matrix.distance_meters(0, 1));
This matrix is the bridge between geospatial data and optimization. A VRP solver can use it as the cost model for sequencing stops, estimating arrival times, comparing alternative route plans, and reporting the route distance associated with the fastest-time path.
Since the 2.1.4 release, TravelTimeMatrix stores route distances next
to travel times. matrix.distance_meters(from, to) returns meters for the same
fastest-time path used by matrix.get(from, to). Unreachable pairs return
Some(UNREACHABLE), and out-of-bounds indices return None.
Step 5: Route Individual Pairs
let route = network.route(locations[0], locations[1])?;
println!("Duration: {} seconds", route.duration_seconds);
println!("Geometry points: {}", route.geometry.len());
route() snaps both endpoints to the nearest graph nodes before routing. That is a good default for travel-time analysis, but it can produce geometries that start and end at nearby intersections instead of the exact stop positions.
For frontend rendering where the route should stay on the containing road segments, use edge snapping:
let from = network.snap_to_edge(locations[0])?;
let to = network.snap_to_edge(locations[1])?;
let route = network.route_edge_snapped(&from, &to)?;
Full Example
use solverforge_maps::{BoundingBox, Coord, NetworkConfig, RoadNetwork, RoutingResult};
#[tokio::main]
async fn main() -> RoutingResult<()> {
let locations = vec![
Coord::try_new(39.9526, -75.1652)?,
Coord::try_new(39.9610, -75.1700)?,
Coord::try_new(39.9440, -75.1500)?,
];
let bbox = BoundingBox::from_coords(&locations).expand_for_routing(&locations);
let config = NetworkConfig::default();
let network = RoadNetwork::load_or_fetch(&bbox, &config, None).await?;
let matrix = network.compute_matrix(&locations, None).await;
let route = network.route(locations[0], locations[1])?;
println!("Matrix size: {}", matrix.size());
println!("Travel time 0 -> 1: {:?} seconds", matrix.get(0, 1));
println!("Distance 0 -> 1: {:?} meters", matrix.distance_meters(0, 1));
println!("Route duration: {} seconds", route.duration_seconds);
Ok(())
}
Common Next Steps
After the first matrix is working, most applications move on to one or more of these tasks:
- inspect route geometry in a frontend map
- check cache hit rates for repeated workloads
- compute full matrices for solver input
- tune
NetworkConfigfor production environments
Read Routing & Matrices and Caching & Operations for those topics.