1use std::cmp::Reverse;
2use std::collections::BinaryHeap;
3
4use geo_traits::{CoordTrait, RectTrait};
5
6use crate::error::Result;
7use crate::indices::Indices;
8use crate::r#type::IndexableNum;
9use crate::rtree::index::{RTree, RTreeRef};
10use crate::rtree::traversal::{IntersectionIterator, Node};
11use crate::rtree::util::upper_bound;
12use crate::rtree::RTreeMetadata;
13use crate::GeoIndexError;
14
15pub trait RTreeIndex<N: IndexableNum>: Sized {
17 fn boxes(&self) -> &[N];
20
21 fn indices(&self) -> Indices;
23
24 fn metadata(&self) -> &RTreeMetadata<N>;
26
27 fn num_items(&self) -> u32 {
29 self.metadata().num_items()
30 }
31
32 fn num_nodes(&self) -> usize {
34 self.metadata().num_nodes()
35 }
36
37 fn node_size(&self) -> u16 {
39 self.metadata().node_size()
40 }
41
42 fn level_bounds(&self) -> &[usize] {
46 self.metadata().level_bounds()
47 }
48
49 fn num_levels(&self) -> usize {
51 self.level_bounds().len()
52 }
53
54 fn boxes_at_level(&self, level: usize) -> Result<&[N]> {
57 let level_bounds = self.level_bounds();
58 if level >= level_bounds.len() {
59 return Err(GeoIndexError::General("Level out of bounds".to_string()));
60 }
61 let result = if level == 0 {
62 &self.boxes()[0..level_bounds[0]]
63 } else if level == level_bounds.len() {
64 &self.boxes()[level_bounds[level]..]
65 } else {
66 &self.boxes()[level_bounds[level - 1]..level_bounds[level]]
67 };
68 Ok(result)
69 }
70
71 fn search(&self, min_x: N, min_y: N, max_x: N, max_y: N) -> Vec<u32> {
75 let boxes = self.boxes();
76 let indices = self.indices();
77 if boxes.is_empty() {
78 return vec![];
79 }
80
81 let mut outer_node_index = boxes.len().checked_sub(4);
82
83 let mut queue = vec![];
84 let mut results = vec![];
85
86 while let Some(node_index) = outer_node_index {
87 let end = (node_index + self.node_size() as usize * 4)
89 .min(upper_bound(node_index, self.level_bounds()));
90
91 for pos in (node_index..end).step_by(4) {
93 if max_x < boxes[pos] {
95 continue; }
97 if max_y < boxes[pos + 1] {
98 continue; }
100 if min_x > boxes[pos + 2] {
101 continue; }
103 if min_y > boxes[pos + 3] {
104 continue; }
106
107 let index = indices.get(pos >> 2);
108
109 if node_index >= self.num_items() as usize * 4 {
110 queue.push(index); } else {
112 results.push(index.try_into().unwrap()); }
115 }
116
117 outer_node_index = queue.pop();
118 }
119
120 results
121 }
122
123 fn search_rect(&self, rect: &impl RectTrait<T = N>) -> Vec<u32> {
127 self.search(
128 rect.min().x(),
129 rect.min().y(),
130 rect.max().x(),
131 rect.max().y(),
132 )
133 }
134
135 fn neighbors(
152 &self,
153 x: N,
154 y: N,
155 max_results: Option<usize>,
156 max_distance: Option<N>,
157 ) -> Vec<u32> {
158 let boxes = self.boxes();
159 let indices = self.indices();
160 let max_distance = max_distance.unwrap_or(N::max_value());
161
162 let mut outer_node_index = Some(boxes.len() - 4);
163 let mut queue = BinaryHeap::new();
164 let mut results: Vec<u32> = vec![];
165 let max_dist_squared = max_distance * max_distance;
166
167 'outer: while let Some(node_index) = outer_node_index {
168 let end = (node_index + self.node_size() as usize * 4)
170 .min(upper_bound(node_index, self.level_bounds()));
171
172 for pos in (node_index..end).step_by(4) {
174 let index = indices.get(pos >> 2);
175
176 let dx = axis_dist(x, boxes[pos], boxes[pos + 2]);
177 let dy = axis_dist(y, boxes[pos + 1], boxes[pos + 3]);
178 let dist = dx * dx + dy * dy;
179 if dist > max_dist_squared {
180 continue;
181 }
182
183 if node_index >= self.num_items() as usize * 4 {
184 queue.push(Reverse(NeighborNode {
186 id: index << 1,
187 dist,
188 }));
189 } else {
190 queue.push(Reverse(NeighborNode {
192 id: (index << 1) + 1,
193 dist,
194 }));
195 }
196 }
197
198 while !queue.is_empty() && queue.peek().is_some_and(|val| (val.0.id & 1) != 0) {
200 let dist = queue.peek().unwrap().0.dist;
201 if dist > max_dist_squared {
202 break 'outer;
203 }
204 let item = queue.pop().unwrap();
205 results.push((item.0.id >> 1).try_into().unwrap());
206 if max_results.is_some_and(|max_results| results.len() == max_results) {
207 break 'outer;
208 }
209 }
210
211 if let Some(item) = queue.pop() {
212 outer_node_index = Some(item.0.id >> 1);
213 } else {
214 outer_node_index = None;
215 }
216 }
217
218 results
219 }
220
221 fn neighbors_coord(
223 &self,
224 coord: &impl CoordTrait<T = N>,
225 max_results: Option<usize>,
226 max_distance: Option<N>,
227 ) -> Vec<u32> {
228 self.neighbors(coord.x(), coord.y(), max_results, max_distance)
229 }
230
231 fn intersection_candidates_with_other_tree<'a>(
236 &'a self,
237 other: &'a impl RTreeIndex<N>,
238 ) -> impl Iterator<Item = (u32, u32)> + 'a {
239 IntersectionIterator::from_trees(self, other)
240 }
241
242 fn root(&self) -> Node<'_, N, Self> {
244 Node::from_root(self)
245 }
246}
247
248#[derive(Debug, Clone, Copy, PartialEq)]
250struct NeighborNode<N: IndexableNum> {
251 id: usize,
252 dist: N,
253}
254
255impl<N: IndexableNum> Eq for NeighborNode<N> {}
256
257impl<N: IndexableNum> Ord for NeighborNode<N> {
258 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
259 self.dist.partial_cmp(&other.dist).unwrap()
261 }
262}
263
264impl<N: IndexableNum> PartialOrd for NeighborNode<N> {
265 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
266 Some(self.cmp(other))
267 }
268}
269
270impl<N: IndexableNum> RTreeIndex<N> for RTree<N> {
271 fn boxes(&self) -> &[N] {
272 self.metadata.boxes_slice(&self.buffer)
273 }
274
275 fn indices(&self) -> Indices {
276 self.metadata.indices_slice(&self.buffer)
277 }
278
279 fn metadata(&self) -> &RTreeMetadata<N> {
280 &self.metadata
281 }
282}
283
284impl<N: IndexableNum> RTreeIndex<N> for RTreeRef<'_, N> {
285 fn boxes(&self) -> &[N] {
286 self.boxes
287 }
288
289 fn indices(&self) -> Indices {
290 self.indices
291 }
292
293 fn metadata(&self) -> &RTreeMetadata<N> {
294 &self.metadata
295 }
296}
297
298#[allow(dead_code)]
300#[inline]
301fn axis_dist<N: IndexableNum>(k: N, min: N, max: N) -> N {
302 if k < min {
303 min - k
304 } else if k <= max {
305 N::zero()
306 } else {
307 k - max
308 }
309}
310
311#[cfg(test)]
312mod test {
313 mod js {
315 use crate::rtree::RTreeIndex;
316 use crate::test::{flatbush_js_test_data, flatbush_js_test_index};
317
318 #[test]
319 fn performs_bbox_search() {
320 let data = flatbush_js_test_data();
321 let index = flatbush_js_test_index();
322 let ids = index.search(40., 40., 60., 60.);
323
324 let mut results: Vec<usize> = vec![];
325 for id in ids {
326 results.push(data[4 * id as usize] as usize);
327 results.push(data[4 * id as usize + 1] as usize);
328 results.push(data[4 * id as usize + 2] as usize);
329 results.push(data[4 * id as usize + 3] as usize);
330 }
331
332 results.sort();
333
334 let mut expected = vec![
335 57, 59, 58, 59, 48, 53, 52, 56, 40, 42, 43, 43, 43, 41, 47, 43,
336 ];
337 expected.sort();
338
339 assert_eq!(results, expected);
340 }
341 }
342}