1use crate::error::SpatialResult;
2use crate::rtree::node::{Entry, EntryWithDistance, Node, RTree, Rectangle};
3use scirs2_core::ndarray::ArrayView1;
4use std::cmp::Ordering;
5use std::collections::BinaryHeap;
6
7impl<T: Clone> RTree<T> {
8 pub fn search_range(
20 &self,
21 min: &ArrayView1<f64>,
22 max: &ArrayView1<f64>,
23 ) -> SpatialResult<Vec<(usize, T)>> {
24 if min.len() != self.ndim() || max.len() != self.ndim() {
25 return Err(crate::error::SpatialError::DimensionError(format!(
26 "Search range dimensions ({}, {}) do not match RTree dimension {}",
27 min.len(),
28 max.len(),
29 self.ndim()
30 )));
31 }
32
33 let rect = Rectangle::new(min.to_owned(), max.to_owned())?;
35
36 let mut results = Vec::new();
38 self.search_range_internal(&rect, &self.root, &mut results)?;
39
40 Ok(results)
41 }
42
43 #[allow(clippy::only_used_in_recursion)]
45 fn search_range_internal(
46 &self,
47 rect: &Rectangle,
48 node: &Node<T>,
49 results: &mut Vec<(usize, T)>,
50 ) -> SpatialResult<()> {
51 for entry in &node.entries {
53 if entry.mbr().intersects(rect)? {
55 match entry {
56 Entry::Leaf { data, index, .. } => {
58 results.push((*index, data.clone()));
59 }
60 Entry::NonLeaf { child, .. } => {
62 self.search_range_internal(rect, child, results)?;
63 }
64 }
65 }
66 }
67
68 Ok(())
69 }
70
71 pub fn nearest(
83 &self,
84 point: &ArrayView1<f64>,
85 k: usize,
86 ) -> SpatialResult<Vec<(usize, T, f64)>> {
87 if point.len() != self.ndim() {
88 return Err(crate::error::SpatialError::DimensionError(format!(
89 "Point dimension {} does not match RTree dimension {}",
90 point.len(),
91 self.ndim()
92 )));
93 }
94
95 if k == 0 || self.is_empty() {
96 return Ok(Vec::new());
97 }
98
99 let mut pq = BinaryHeap::new();
101 let mut results = Vec::new();
102
103 if let Ok(Some(root_mbr)) = self.root.mbr() {
105 let _distance = root_mbr.min_distance_to_point(point)?;
106
107 for entry in &self.root.entries {
109 let entry_distance = entry.mbr().min_distance_to_point(point)?;
110 pq.push(EntryWithDistance {
111 entry: entry.clone(),
112 distance: entry_distance,
113 });
114 }
115 }
116
117 let mut max_distance = f64::MAX;
119
120 while let Some(item) = pq.pop() {
122 if item.distance > max_distance && results.len() >= k {
124 break;
125 }
126
127 match item.entry {
128 Entry::Leaf { data, index, .. } => {
130 results.push((index, data, item.distance));
131
132 if results.len() >= k {
134 results.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Ordering::Equal));
136
137 results.truncate(k);
139
140 if let Some((_, _, dist)) = results.last() {
142 max_distance = *dist;
143 }
144 }
145 }
146 Entry::NonLeaf { child, .. } => {
148 for entry in &child.entries {
149 let entry_distance = entry.mbr().min_distance_to_point(point)?;
150
151 if entry_distance <= max_distance || results.len() < k {
153 pq.push(EntryWithDistance {
154 entry: entry.clone(),
155 distance: entry_distance,
156 });
157 }
158 }
159 }
160 }
161 }
162
163 results.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(Ordering::Equal));
165
166 results.truncate(k);
168
169 Ok(results)
170 }
171
172 pub fn spatial_join<U, P>(&self, other: &RTree<U>, predicate: P) -> SpatialResult<Vec<(T, U)>>
185 where
186 U: Clone,
187 P: Fn(&Rectangle, &Rectangle) -> SpatialResult<bool>,
188 {
189 if self.ndim() != other.ndim() {
190 return Err(crate::error::SpatialError::DimensionError(format!(
191 "RTrees have different dimensions: {} and {}",
192 self.ndim(),
193 other.ndim()
194 )));
195 }
196
197 let mut results = Vec::new();
198
199 if self.is_empty() || other.is_empty() {
201 return Ok(results);
202 }
203
204 self.spatial_join_internal(&self.root, &other.root, &predicate, &mut results)?;
206
207 Ok(results)
208 }
209
210 #[allow(clippy::only_used_in_recursion)]
212 fn spatial_join_internal<U, P>(
213 &self,
214 node1: &Node<T>,
215 node2: &Node<U>,
216 predicate: &P,
217 results: &mut Vec<(T, U)>,
218 ) -> SpatialResult<()>
219 where
220 U: Clone,
221 P: Fn(&Rectangle, &Rectangle) -> SpatialResult<bool>,
222 {
223 for entry1 in &node1.entries {
225 for entry2 in &node2.entries {
226 if predicate(entry1.mbr(), entry2.mbr())? {
228 match (entry1, entry2) {
229 (Entry::Leaf { data: data1, .. }, Entry::Leaf { data: data2, .. }) => {
231 results.push((data1.clone(), data2.clone()));
232 }
233 (Entry::NonLeaf { child: child1, .. }, Entry::Leaf { .. }) => {
235 self.spatial_join_internal(
236 child1,
237 &Node {
238 entries: vec![entry2.clone()],
239 _isleaf: true,
240 level: 0,
241 },
242 predicate,
243 results,
244 )?;
245 }
246 (Entry::Leaf { .. }, Entry::NonLeaf { child: child2, .. }) => {
248 self.spatial_join_internal(
249 &Node {
250 entries: vec![entry1.clone()],
251 _isleaf: true,
252 level: 0,
253 },
254 child2,
255 predicate,
256 results,
257 )?;
258 }
259 (
261 Entry::NonLeaf { child: child1, .. },
262 Entry::NonLeaf { child: child2, .. },
263 ) => {
264 self.spatial_join_internal(child1, child2, predicate, results)?;
265 }
266 }
267 }
268 }
269 }
270
271 Ok(())
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278 use approx::assert_relative_eq;
279 use scirs2_core::ndarray::array;
280
281 #[test]
282 fn test_rtree_nearest_neighbors() {
283 let mut rtree: RTree<i32> = RTree::new(2, 2, 4).expect("Operation failed");
285
286 let points = vec![
288 (array![0.0, 0.0], 0),
289 (array![1.0, 0.0], 1),
290 (array![0.0, 1.0], 2),
291 (array![1.0, 1.0], 3),
292 (array![0.5, 0.5], 4),
293 (array![2.0, 2.0], 5),
294 (array![3.0, 3.0], 6),
295 (array![4.0, 4.0], 7),
296 (array![5.0, 5.0], 8),
297 (array![6.0, 6.0], 9),
298 ];
299
300 for (point, value) in points {
301 rtree.insert(point, value).expect("Operation failed");
302 }
303
304 let nn_results = rtree
306 .nearest(&array![0.6, 0.6].view(), 1)
307 .expect("Operation failed");
308
309 assert_eq!(nn_results.len(), 1);
311 assert_eq!(nn_results[0].1, 4);
312
313 let nn_results = rtree
315 .nearest(&array![0.0, 0.0].view(), 3)
316 .expect("Operation failed");
317
318 assert_eq!(nn_results.len(), 3);
320
321 assert_eq!(nn_results[0].1, 0); assert_eq!(nn_results[1].1, 4); assert!(nn_results[2].1 == 1 || nn_results[2].1 == 2);
327
328 assert_relative_eq!(nn_results[0].2, 0.0);
330 assert_relative_eq!(
331 nn_results[1].2,
332 (0.5_f64.powi(2) + 0.5_f64.powi(2)).sqrt(),
333 epsilon = 1e-10
334 );
335 assert_relative_eq!(nn_results[2].2, 1.0);
336
337 let nn_empty = rtree
339 .nearest(&array![0.0, 0.0].view(), 0)
340 .expect("Operation failed");
341 assert_eq!(nn_empty.len(), 0);
342
343 let nn_all = rtree
345 .nearest(&array![0.0, 0.0].view(), 20)
346 .expect("Operation failed");
347 assert_eq!(nn_all.len(), 10); }
349
350 #[test]
351 fn test_rtree_spatial_join() {
352 let mut rtree1: RTree<i32> = RTree::new(2, 2, 4).expect("Operation failed");
354 let mut rtree2: RTree<char> = RTree::new(2, 2, 4).expect("Operation failed");
355
356 let rectangles1 = vec![
358 (array![0.0, 0.0], array![0.6, 0.6], 0),
359 (array![0.4, 0.0], array![1.0, 0.6], 1),
360 (array![0.0, 0.4], array![0.6, 1.0], 2),
361 (array![0.4, 0.4], array![1.0, 1.0], 3),
362 ];
363
364 for (min_corner, max_corner, value) in rectangles1 {
365 rtree1
366 .insert_rectangle(min_corner, max_corner, value)
367 .expect("Operation failed");
368 }
369
370 let rectangles2 = vec![
372 (array![0.3, 0.3], array![0.7, 0.7], 'A'),
373 (array![0.8, 0.3], array![1.2, 0.7], 'B'),
374 (array![0.3, 0.8], array![0.7, 1.2], 'C'),
375 (array![0.8, 0.8], array![1.2, 1.2], 'D'),
376 ];
377
378 for (min_corner, max_corner, value) in rectangles2 {
379 rtree2
380 .insert_rectangle(min_corner, max_corner, value)
381 .expect("Operation failed");
382 }
383
384 let join_results = rtree1
386 .spatial_join(&rtree2, |mbr1, mbr2| mbr1.intersects(mbr2))
387 .expect("Operation failed");
388
389 assert!(
391 !join_results.is_empty(),
392 "Expected spatial join to find intersecting rectangles"
393 );
394
395 assert_eq!(
402 join_results.len(),
403 9,
404 "Expected 9 intersections, found {}",
405 join_results.len()
406 );
407
408 let strict_join_results = rtree1
410 .spatial_join(&rtree2, |mbr1, mbr2| mbr1.contains_rectangle(mbr2))
411 .expect("Operation failed");
412
413 assert!(strict_join_results.len() <= join_results.len());
415 }
416}