bambam_osm/algorithm/search.rs
1use crate::model::osm::{
2 graph::{OsmGraph, OsmNodeId},
3 OsmError,
4};
5use itertools::Itertools;
6use std::collections::{HashSet, LinkedList};
7
8/// finds the set of indices that are part of the same geometry cluster
9/// using a breadth-first search over an undirected graph of geometry
10/// intersection relations.
11///
12/// # Arguments
13///
14/// * `src` - origin of tree
15/// * `graph` - graph to search
16/// * `valid_set` - set of valid nodes to visit, or None if all are acceptable.
17///
18/// # Returns
19///
20/// The set of node ids connected by the minimum spanning tree within the valid_set.
21pub fn bfs_undirected(
22 src: OsmNodeId,
23 graph: &OsmGraph,
24 valid_set: Option<&HashSet<OsmNodeId>>,
25) -> Result<HashSet<OsmNodeId>, OsmError> {
26 // initial search state. if a NodeOsmid has been visited, it is appended
27 // to the visited set.
28 // breadth-first search is modeled here with a linked list FIFO queue.
29 let mut visited: HashSet<OsmNodeId> = HashSet::new();
30 let mut frontier: LinkedList<OsmNodeId> = LinkedList::new();
31 frontier.push_back(src);
32
33 while let Some(next_id) = frontier.pop_front() {
34 // add to the search tree
35 visited.insert(next_id);
36
37 // expand the frontier
38 let next_out = graph.get_out_neighbors(&next_id).unwrap_or_default();
39 let next_in = graph.get_in_neighbors(&next_id).unwrap_or_default();
40
41 // neighbors are only reviewed here that are "valid" (all fall within the spatial cluster).
42 // they are sorted for algorithmic determinism (frontier insertion order).
43 let valid_neighbors = next_in
44 .union(&next_out)
45 .filter(|n| match &valid_set {
46 Some(valid) => valid.contains(*n),
47 None => true,
48 })
49 .sorted();
50 for neighbor in valid_neighbors {
51 if !visited.contains(neighbor) {
52 // frontier.push(Reverse((next_depth + 1, *neighbor))); // min heap
53 frontier.push_back(*neighbor); // min heap
54 }
55 }
56 }
57
58 Ok(visited)
59}