Skip to main content

osmnxr_core/
graph.rs

1//! Compressed-sparse-row (CSR) directed graph built from edge lists.
2//!
3//! Node identifiers are 0-based contiguous indices; the R layer is responsible
4//! for mapping OSM node ids to and from these indices.
5
6/// A directed, weighted graph in CSR form.
7pub struct Graph {
8    /// `offsets[i] .. offsets[i + 1]` is the slice of out-edges of node `i`.
9    pub offsets: Vec<usize>,
10    /// Destination node of each out-edge, grouped by source node.
11    pub targets: Vec<usize>,
12    /// Weight (e.g. length or travel time) of each out-edge.
13    pub weights: Vec<f64>,
14    /// Number of nodes.
15    pub n_nodes: usize,
16}
17
18impl Graph {
19    /// Build a CSR graph from parallel `from`/`to`/`weight` edge vectors.
20    ///
21    /// `n_nodes` is the number of distinct node indices; every value in
22    /// `from`/`to` must be in `0 .. n_nodes`.
23    pub fn from_edges(from: &[usize], to: &[usize], weight: &[f64], n_nodes: usize) -> Self {
24        let m = from.len();
25        let mut degree = vec![0usize; n_nodes + 1];
26        for &u in from {
27            degree[u + 1] += 1;
28        }
29        for i in 0..n_nodes {
30            degree[i + 1] += degree[i];
31        }
32        let offsets = degree; // now a prefix-sum offset array
33        let mut targets = vec![0usize; m];
34        let mut weights = vec![0.0f64; m];
35        let mut cursor = offsets.clone();
36        for e in 0..m {
37            let u = from[e];
38            let pos = cursor[u];
39            targets[pos] = to[e];
40            weights[pos] = weight[e];
41            cursor[u] += 1;
42        }
43        Graph {
44            offsets,
45            targets,
46            weights,
47            n_nodes,
48        }
49    }
50
51    /// Out-edges of `node` as `(target, weight)` pairs.
52    pub fn neighbors(&self, node: usize) -> impl Iterator<Item = (usize, f64)> + '_ {
53        let start = self.offsets[node];
54        let end = self.offsets[node + 1];
55        (start..end).map(move |e| (self.targets[e], self.weights[e]))
56    }
57
58    /// Number of directed edges.
59    pub fn n_edges(&self) -> usize {
60        self.targets.len()
61    }
62}