|
1 | | -use super::ClusteredGeometry; |
2 | | -use crate::algorithm::bfs_undirected; |
3 | | -use crate::model::osm::graph::OsmGraph; |
4 | 1 | use crate::model::osm::graph::OsmNodeId; |
5 | | -use crate::model::osm::OsmError; |
6 | | -use geo::{BooleanOps, BoundingRect, Geometry, Intersects, Polygon, RemoveRepeatedPoints}; |
7 | | -use geo::{Coord, MultiPolygon}; |
| 2 | + |
| 3 | +use super::ClusteredGeometry; |
| 4 | +use geo::{BoundingRect, Coord, Polygon}; |
8 | 5 | use itertools::Itertools; |
9 | | -use kdam::{tqdm, Bar, BarExt}; |
10 | | -use rayon::prelude::*; |
11 | | -use routee_compass_core::model::unit::AsF64; |
12 | | -use routee_compass_core::model::unit::DistanceUnit; |
| 6 | +use kdam::tqdm; |
13 | 7 | use rstar::primitives::{GeomWithData, Rectangle}; |
14 | 8 | use rstar::{RTree, RTreeObject}; |
15 | | -use std::collections::HashSet; |
16 | | -use std::collections::{BinaryHeap, HashMap}; |
17 | | -use std::sync::Arc; |
18 | | -use std::sync::Mutex; |
19 | 9 | use wkt::ToWkt; |
20 | 10 |
|
21 | | -pub type ClusterLabel = usize; |
22 | 11 | pub type ClusteredIntersections = GeomWithData<Rectangle<(f32, f32)>, ClusteredGeometry>; |
23 | 12 |
|
24 | 13 | /// build an undirected graph of node geometries that intersect spatially. |
@@ -75,197 +64,6 @@ pub fn build( |
75 | 64 | Ok(rtree) |
76 | 65 | } |
77 | 66 |
|
78 | | -// /// merges two geometries by Union that should be either Polygon or MultiPolygon geometries. |
79 | | -// fn merge_areal_geometries(a: &Geometry, b: &Geometry) -> Result<Geometry, String> { |
80 | | -// let unioned: MultiPolygon = match (a, b) { |
81 | | -// (Geometry::Polygon(a), Geometry::Polygon(b)) => Ok(a.union(b)), |
82 | | -// (Geometry::Polygon(p), Geometry::MultiPolygon(mp)) => Ok(p.union(mp)), |
83 | | -// (Geometry::MultiPolygon(mp), Geometry::Polygon(p)) => Ok(mp.union(p)), |
84 | | -// (Geometry::MultiPolygon(a), Geometry::MultiPolygon(b)) => Ok(a.union(b)), |
85 | | -// _ => Err(format!( |
86 | | -// "invalid geometry types \n{} \n{}", |
87 | | -// a.to_wkt(), |
88 | | -// b.to_wkt() |
89 | | -// )), |
90 | | -// }?; |
91 | | -// // Ok(geo::Geometry::MultiPolygon(unioned)) |
92 | | -// // let cleaned = unioned.union(&geo::MultiPolygon::new(vec![])); |
93 | | - |
94 | | -// let exteriors = unioned |
95 | | -// .remove_repeated_points() |
96 | | -// .iter() |
97 | | -// .map(|p| Polygon::new(p.exterior().clone(), vec![])) |
98 | | -// .collect_vec(); |
99 | | -// let no_holes = MultiPolygon::new(exteriors); |
100 | | -// Ok(geo::Geometry::MultiPolygon(no_holes)) |
101 | | -// } |
102 | | - |
103 | | -// /// in order to simplify the graph, we have to identify nodes that are within |
104 | | -// /// some distance threshold of each other and then also confirm that they are |
105 | | -// /// connected in the graph space (for example, a bridge may not be connected to |
106 | | -// /// the roads beneath it). |
107 | | -// /// |
108 | | -// /// # Arguments |
109 | | -// /// * `indices` - a collection of entity indices that should correspond to |
110 | | -// /// the indices of the undirected graph. can be the complete |
111 | | -// /// collection of indices to find all components, or a subset |
112 | | -// /// in order to identify the disjoint union of sub-subsets that |
113 | | -// /// are connected through the undirected graph. |
114 | | -// /// * `undirected_graph` - all valid indices and their connections. in the |
115 | | -// /// context of OSM import, this may be the graph of |
116 | | -// /// spatial intersections or the graph of network |
117 | | -// /// connections. |
118 | | -// /// |
119 | | -// /// # Returns |
120 | | -// /// |
121 | | -// /// A vector of components, each represented as a set of indices. |
122 | | -// pub fn connected_components_clustering( |
123 | | -// indices: &[OsmNodeId], |
124 | | -// undirected_graph: &[Vec<OsmNodeId>], |
125 | | -// ) -> Result<Vec<HashSet<OsmNodeId>>, OsmError> { |
126 | | -// // handle base cases |
127 | | -// match *indices { |
128 | | -// [] => return Ok(vec![]), |
129 | | -// [singleton] => return Ok(vec![HashSet::from([singleton])]), |
130 | | -// _ => {} |
131 | | -// }; |
132 | | -// // connected components (undirected graph) |
133 | | -// // run breadth-first searches from indices that have not yet been assigned a label. |
134 | | -// // labels found in the search become part of the next cluster. |
135 | | -// let mut clusters: Vec<HashSet<OsmNodeId>> = vec![]; |
136 | | -// let mut unassigned = indices |
137 | | -// .iter() |
138 | | -// .map(|idx| (*idx, true)) |
139 | | -// .collect::<HashMap<_, _>>(); |
140 | | - |
141 | | -// let valid_set = indices.iter().collect::<HashSet<_>>(); |
142 | | - |
143 | | -// let cc_iter = tqdm!( |
144 | | -// indices.iter(), |
145 | | -// total = unassigned.len(), |
146 | | -// desc = "connected components" |
147 | | -// ); |
148 | | -// for label in cc_iter { |
149 | | -// if unassigned[label] { |
150 | | -// // found a label that is unassigned. begin the next cluster. |
151 | | -// let next_cluster: HashSet<usize> = |
152 | | -// bfs_undirected(*label, undirected_graph, &Some(valid_set)).map_err(|e| { |
153 | | -// OsmError::GraphConsolidationError(format!( |
154 | | -// "failure executing graph search for connected components: {}", |
155 | | -// e |
156 | | -// )) |
157 | | -// })?; |
158 | | - |
159 | | -// // for each clustered geometry index, label it assigned and add it to this cluster |
160 | | -// for index in next_cluster.iter() { |
161 | | -// unassigned.entry(*index).and_modify(|val| { |
162 | | -// *val = false; |
163 | | -// }); |
164 | | -// } |
165 | | - |
166 | | -// clusters.push(next_cluster); |
167 | | -// } |
168 | | -// } |
169 | | -// eprintln!(); |
170 | | -// Ok(clusters) |
171 | | -// } |
172 | | - |
173 | | -// /// a succession of intersection tests, in increasing order of computational complexity, |
174 | | -// /// for finding geometric intersections. |
175 | | -// fn geometries_intersect( |
176 | | -// a: (usize, &Geometry), |
177 | | -// b: (usize, &Geometry), |
178 | | -// // ns: &Vec<HashSet<usize>>, // todo!: reintroduce this argument, respecting Arc<Mutex<T>> |
179 | | -// ) -> bool { |
180 | | -// let (a_label, a_geom) = a; |
181 | | -// let (b_label, b_geom) = b; |
182 | | - |
183 | | -// // simple identifier test. dismiss when row == column on the matrix. |
184 | | -// if a_label == b_label { |
185 | | -// log::debug!( |
186 | | -// "geometries {},{} intersect? TRUE - matching label", |
187 | | -// a_label, |
188 | | -// b_label |
189 | | -// ); |
190 | | -// return false; |
191 | | -// } |
192 | | - |
193 | | -// // // did we already find a from b? |
194 | | -// // let a_has_already_found_b = ns |
195 | | -// // .get(a_label) |
196 | | -// // .ok_or_else(|| out_of_index_err(b_label, ns))? |
197 | | -// // .contains(&b_label); |
198 | | -// // if a_has_already_found_b { |
199 | | -// // return false; |
200 | | -// // } |
201 | | - |
202 | | -// // first geometric test, only using bounding boxes. |
203 | | -// if bbox_intersects(a_geom, b_geom) { |
204 | | -// log::debug!( |
205 | | -// "geometries {},{} intersect? TRUE - bboxes intersect", |
206 | | -// a_label, |
207 | | -// b_label |
208 | | -// ); |
209 | | -// return true; |
210 | | -// } |
211 | | - |
212 | | -// // final test is a true geometric intersection test (expensive). |
213 | | -// let geometries_intersect = a_geom.intersects(b_geom); |
214 | | -// if geometries_intersect { |
215 | | -// log::debug!( |
216 | | -// "geometries {},{} intersect? TRUE - matching label", |
217 | | -// a_label, |
218 | | -// b_label |
219 | | -// ); |
220 | | -// return true; |
221 | | -// } else { |
222 | | -// log::debug!("geometries {},{} intersect? FALSE", a_label, b_label); |
223 | | -// return false; |
224 | | -// } |
225 | | -// } |
226 | | - |
227 | | -// fn bbox_intersects(a: &Geometry, b: &Geometry) -> bool { |
228 | | -// let a_box = match a.bounding_rect() { |
229 | | -// Some(bbox) => bbox, |
230 | | -// None => return false, |
231 | | -// }; |
232 | | -// let b_box = match b.bounding_rect() { |
233 | | -// Some(bbox) => bbox, |
234 | | -// None => return false, |
235 | | -// }; |
236 | | -// a_box.intersects(&b_box) |
237 | | -// } |
238 | | - |
239 | | -// fn out_of_index_err(label: usize, ns: &[Vec<usize>]) -> String { |
240 | | -// format!( |
241 | | -// "neighbors expected to have index {} but only has {} elements", |
242 | | -// label, |
243 | | -// ns.len() |
244 | | -// ) |
245 | | -// } |
246 | | - |
247 | | -// /// finds the set of indices that are part of the same geometry cluster |
248 | | -// /// using a breadth-first search over an undirected graph of geometry |
249 | | -// /// intersection relations. |
250 | | -// fn bfs(src: usize, ns: &[Vec<usize>]) -> Result<HashSet<usize>, String> { |
251 | | -// let mut visited: HashSet<usize> = HashSet::new(); |
252 | | -// visited.insert(src); |
253 | | -// let mut frontier: BinaryHeap<(usize, i32)> = BinaryHeap::new(); |
254 | | -// frontier.push((src, 0)); |
255 | | -// while let Some((next_id, next_depth)) = frontier.pop() { |
256 | | -// visited.insert(next_id); |
257 | | -// let next_neighbors = ns |
258 | | -// .get(next_id) |
259 | | -// .ok_or_else(|| out_of_index_err(next_id, ns))?; |
260 | | -// for neighbor in next_neighbors.iter() { |
261 | | -// if !visited.contains(neighbor) { |
262 | | -// frontier.push((*neighbor, next_depth - 1)); // max heap |
263 | | -// } |
264 | | -// } |
265 | | -// } |
266 | | -// Ok(visited) |
267 | | -// } |
268 | | - |
269 | 67 | /// helper function to create a rectangular rtree envelope for a given geometry |
270 | 68 | fn rect_from_geometries(ps: &[&Polygon<f32>]) -> Result<Rectangle<(f32, f32)>, String> { |
271 | 69 | if ps.is_empty() { |
|
0 commit comments