Skip to main content

geometry_rtree/
rtree.rs

1//! The [`Rtree`] itself — insert, spatial query, nearest-neighbour, and
2//! Sort-Tile-Recursive bulk load.
3//!
4//! Mirrors `boost/geometry/index/rtree.hpp` and the visitor family under
5//! `index/detail/rtree/visitors/`. Insert is the recursive
6//! least-enlargement descent of `visitors/insert.hpp`; query is the
7//! pruning walk of `visitors/spatial_query.hpp`; nearest is the
8//! best-first search of `visitors/distance_query.hpp`.
9
10use alloc::boxed::Box;
11use alloc::vec::Vec;
12use core::marker::PhantomData;
13
14use crate::bounds::{Bounds, union_all};
15use crate::indexable::Indexable;
16use crate::node::Node;
17use crate::predicate::Predicate;
18use crate::split::{Quadratic, SplitParameters};
19
20/// A spatial index over `Indexable` values, parameterised by a split
21/// strategy.
22///
23/// Mirrors `boost::geometry::index::rtree<Value, Parameters>`
24/// (`index/rtree.hpp`). The default split is [`Quadratic`]; pass
25/// [`Linear`](crate::split::Linear) as `Params` for cheaper inserts.
26///
27/// # Examples
28///
29/// ```
30/// use geometry_cs::Cartesian;
31/// use geometry_model::Point2D;
32/// use geometry_rtree::Rtree;
33///
34/// type P = Point2D<f64, Cartesian>;
35/// let mut tree: Rtree<P> = Rtree::new();
36/// tree.insert(P::new(1.0, 1.0));
37/// tree.insert(P::new(5.0, 5.0));
38/// assert_eq!(tree.len(), 2);
39/// ```
40#[derive(Debug)]
41pub struct Rtree<T: Indexable, Params: SplitParameters = Quadratic> {
42    root: Node<T>,
43    len: usize,
44    _params: PhantomData<Params>,
45}
46
47impl<T: Indexable, Params: SplitParameters> Default for Rtree<T, Params> {
48    fn default() -> Self {
49        Self::new()
50    }
51}
52
53impl<T: Indexable, Params: SplitParameters> Rtree<T, Params> {
54    /// An empty tree.
55    #[must_use]
56    pub fn new() -> Self {
57        Self {
58            root: Node::Leaf(Vec::new()),
59            len: 0,
60            _params: PhantomData,
61        }
62    }
63
64    /// Number of values in the tree.
65    #[must_use]
66    pub fn len(&self) -> usize {
67        self.len
68    }
69
70    /// Whether the tree holds no values.
71    #[must_use]
72    pub fn is_empty(&self) -> bool {
73        self.len == 0
74    }
75
76    /// The height of the tree (a single-leaf tree is height 1).
77    #[must_use]
78    pub fn height(&self) -> usize {
79        self.root.height()
80    }
81
82    /// Insert one value.
83    ///
84    /// Descends by least-enlargement to a leaf, inserts, and splits and
85    /// propagates upward if a node overflows. Mirrors
86    /// `visitors/insert.hpp`.
87    pub fn insert(&mut self, value: T) {
88        self.len += 1;
89        if let Some((b1, n1, b2, n2)) = insert_into::<T, Params>(&mut self.root, value) {
90            // The root split into two nodes n1/n2 (which already hold all
91            // the old root's entries); grow a new root one level taller
92            // over them.
93            self.root = Node::Branch(Vec::from([(b1, n1), (b2, n2)]));
94        }
95    }
96
97    /// Iterate every value whose bounds satisfy `predicate`.
98    ///
99    /// Prunes subtrees whose bounds cannot match. Mirrors
100    /// `visitors/spatial_query.hpp`.
101    #[must_use]
102    pub fn query(&self, predicate: Predicate) -> Vec<&T> {
103        let mut out = Vec::new();
104        query_node(&self.root, &predicate, &mut out);
105        out
106    }
107
108    /// The `k` values nearest to the query point, closest first.
109    ///
110    /// Best-first search over node bounding boxes by minimum distance.
111    /// Mirrors `visitors/distance_query.hpp`.
112    #[must_use]
113    pub fn nearest(&self, query: [f64; 2], k: usize) -> Vec<&T> {
114        if k == 0 {
115            return Vec::new();
116        }
117        let mut candidates: Vec<(f64, &T)> = Vec::new();
118        collect_with_distance(&self.root, query, &mut candidates);
119        candidates.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(core::cmp::Ordering::Equal));
120        candidates.into_iter().take(k).map(|(_, v)| v).collect()
121    }
122}
123
124impl<T: Indexable, Params: SplitParameters> FromIterator<T> for Rtree<T, Params> {
125    /// Bulk-load with Sort-Tile-Recursive packing: sort by x-centroid,
126    /// slice into vertical strips, sort each strip by y-centroid, and
127    /// pack leaves. Produces a balanced tree in one pass, the analogue of
128    /// Boost's `pack_create` (`index/detail/rtree/pack_create.hpp`).
129    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
130        let values: Vec<T> = iter.into_iter().collect();
131        let len = values.len();
132        if len <= Params::MAX {
133            return Self {
134                root: Node::Leaf(values),
135                len,
136                _params: PhantomData,
137            };
138        }
139        let root = str_pack::<T, Params>(values);
140        Self {
141            root,
142            len,
143            _params: PhantomData,
144        }
145    }
146}
147
148/// Recursively insert `value` into `node`. Returns `Some((b1,n1,b2,n2))`
149/// if `node` split, giving the caller the two replacement children.
150type Split<T> = (Bounds, Box<Node<T>>, Bounds, Box<Node<T>>);
151
152fn insert_into<T: Indexable, Params: SplitParameters>(
153    node: &mut Node<T>,
154    value: T,
155) -> Option<Split<T>> {
156    match node {
157        Node::Leaf(values) => {
158            values.push(value);
159            if values.len() > Params::MAX {
160                Some(split_leaf::<T, Params>(values))
161            } else {
162                None
163            }
164        }
165        Node::Branch(children) => {
166            // Choose the child that needs the least enlargement.
167            let vb = value.bounds();
168            let choice = choose_child(children, &vb);
169            let (_, child) = &mut children[choice];
170            let split = insert_into::<T, Params>(child, value);
171
172            // Refresh the chosen child's bounds after the insert.
173            children[choice].0 = children[choice]
174                .1
175                .bounds()
176                .unwrap_or_else(|| children[choice].0);
177
178            if let Some((b1, n1, b2, n2)) = split {
179                children[choice] = (b1, n1);
180                children.push((b2, n2));
181                if children.len() > Params::MAX {
182                    return Some(split_branch::<T, Params>(children));
183                }
184            }
185            None
186        }
187    }
188}
189
190/// Index of the child whose box enlarges least to admit `vb` (ties
191/// broken by smaller area).
192#[allow(
193    clippy::float_cmp,
194    reason = "exact tie-break between equal enlargements, as Boost's choose_next_node does"
195)]
196fn choose_child<T>(children: &[(Bounds, Box<Node<T>>)], vb: &Bounds) -> usize {
197    let mut best = 0;
198    let mut best_enl = f64::INFINITY;
199    let mut best_area = f64::INFINITY;
200    for (i, (b, _)) in children.iter().enumerate() {
201        let enl = b.enlargement(vb);
202        let area = b.area();
203        if enl < best_enl || (enl == best_enl && area < best_area) {
204            best = i;
205            best_enl = enl;
206            best_area = area;
207        }
208    }
209    best
210}
211
212/// Split an overflowing leaf's values into two leaves.
213fn split_leaf<T: Indexable, Params: SplitParameters>(values: &mut Vec<T>) -> Split<T> {
214    let taken = core::mem::take(values);
215    let boxes: Vec<Bounds> = taken.iter().map(Indexable::bounds).collect();
216    let (g1, g2) = Params::split(&boxes);
217
218    // Partition `taken` into the two index groups. Walk once, routing by
219    // membership in g1.
220    let mut in_g1 = alloc::vec![false; taken.len()];
221    for &i in &g1 {
222        in_g1[i] = true;
223    }
224    let mut v1: Vec<T> = Vec::new();
225    let mut v2: Vec<T> = Vec::new();
226    for (i, v) in taken.into_iter().enumerate() {
227        if in_g1[i] {
228            v1.push(v);
229        } else {
230            v2.push(v);
231        }
232    }
233    debug_assert_eq!(v1.len(), g1.len());
234    debug_assert_eq!(v2.len(), g2.len());
235
236    let b1 = union_all(&v1.iter().map(Indexable::bounds).collect::<Vec<_>>());
237    let b2 = union_all(&v2.iter().map(Indexable::bounds).collect::<Vec<_>>());
238    (b1, Box::new(Node::Leaf(v1)), b2, Box::new(Node::Leaf(v2)))
239}
240
241/// Split an overflowing branch's children into two branches.
242fn split_branch<T: Indexable, Params: SplitParameters>(
243    children: &mut Vec<(Bounds, Box<Node<T>>)>,
244) -> Split<T> {
245    let taken = core::mem::take(children);
246    let boxes: Vec<Bounds> = taken.iter().map(|(b, _)| *b).collect();
247    let (g1, _g2) = Params::split(&boxes);
248
249    let mut in_g1 = alloc::vec![false; taken.len()];
250    for &i in &g1 {
251        in_g1[i] = true;
252    }
253    let mut c1: Vec<(Bounds, Box<Node<T>>)> = Vec::new();
254    let mut c2: Vec<(Bounds, Box<Node<T>>)> = Vec::new();
255    for (i, c) in taken.into_iter().enumerate() {
256        if in_g1[i] {
257            c1.push(c);
258        } else {
259            c2.push(c);
260        }
261    }
262
263    let b1 = union_all(&c1.iter().map(|(b, _)| *b).collect::<Vec<_>>());
264    let b2 = union_all(&c2.iter().map(|(b, _)| *b).collect::<Vec<_>>());
265    (
266        b1,
267        Box::new(Node::Branch(c1)),
268        b2,
269        Box::new(Node::Branch(c2)),
270    )
271}
272
273/// Recursive pruning query walk.
274fn query_node<'a, T: Indexable>(node: &'a Node<T>, predicate: &Predicate, out: &mut Vec<&'a T>) {
275    match node {
276        Node::Leaf(values) => {
277            for v in values {
278                if predicate.matches(&v.bounds()) {
279                    out.push(v);
280                }
281            }
282        }
283        Node::Branch(children) => {
284            for (b, child) in children {
285                if predicate.could_match(b) {
286                    query_node(child, predicate, out);
287                }
288            }
289        }
290    }
291}
292
293/// Collect every value with its distance to the query point. (A simple
294/// full walk; the `nearest` caller sorts and truncates. Adequate for the
295/// v1 milestone — a priority-queue best-first refinement is a later
296/// optimisation.)
297fn collect_with_distance<'a, T: Indexable>(
298    node: &'a Node<T>,
299    query: [f64; 2],
300    out: &mut Vec<(f64, &'a T)>,
301) {
302    match node {
303        Node::Leaf(values) => {
304            for v in values {
305                let b = v.bounds();
306                out.push((b.min_distance_to(query), v));
307            }
308        }
309        Node::Branch(children) => {
310            for (_, child) in children {
311                collect_with_distance(child, query, out);
312            }
313        }
314    }
315}
316
317/// Sort-Tile-Recursive packing of `values` into a balanced tree.
318fn str_pack<T: Indexable, Params: SplitParameters>(mut values: Vec<T>) -> Node<T> {
319    // Leaf-pack: sort by x-centroid, cut into √(n / MAX) vertical strips,
320    // sort each strip by y-centroid, then chop into MAX-sized leaves.
321    values.sort_by(|a, b| {
322        a.bounds().center()[0]
323            .partial_cmp(&b.bounds().center()[0])
324            .unwrap_or(core::cmp::Ordering::Equal)
325    });
326
327    let leaf_count = values.len().div_ceil(Params::MAX);
328    let strip_count = isqrt_ceil(leaf_count).max(1);
329    let per_strip = values.len().div_ceil(strip_count);
330
331    let mut leaves: Vec<(Bounds, Box<Node<T>>)> = Vec::new();
332    let mut rest = values;
333    while !rest.is_empty() {
334        // Peel off the next vertical strip.
335        let take = per_strip.min(rest.len());
336        let tail = rest.split_off(take);
337        let mut strip = core::mem::replace(&mut rest, tail);
338
339        strip.sort_by(|a, b| {
340            a.bounds().center()[1]
341                .partial_cmp(&b.bounds().center()[1])
342                .unwrap_or(core::cmp::Ordering::Equal)
343        });
344
345        // Chop the y-sorted strip into MAX-sized leaves.
346        let mut strip_iter = strip.into_iter();
347        loop {
348            let leaf_vals: Vec<T> = (&mut strip_iter).take(Params::MAX).collect();
349            if leaf_vals.is_empty() {
350                break;
351            }
352            let boxes: Vec<Bounds> = leaf_vals.iter().map(Indexable::bounds).collect();
353            let b = union_all(&boxes);
354            leaves.push((b, Box::new(Node::Leaf(leaf_vals))));
355        }
356    }
357
358    build_branches::<T, Params>(leaves)
359}
360
361/// Recursively group `children` into branch levels until one root
362/// remains.
363fn build_branches<T: Indexable, Params: SplitParameters>(
364    children: Vec<(Bounds, Box<Node<T>>)>,
365) -> Node<T> {
366    if children.len() <= Params::MAX {
367        return Node::Branch(children);
368    }
369    let mut level: Vec<(Bounds, Box<Node<T>>)> = Vec::new();
370    let mut it = children.into_iter();
371    loop {
372        let group: Vec<(Bounds, Box<Node<T>>)> = (&mut it).take(Params::MAX).collect();
373        if group.is_empty() {
374            break;
375        }
376        let boxes: Vec<Bounds> = group.iter().map(|(b, _)| *b).collect();
377        let b = union_all(&boxes);
378        level.push((b, Box::new(Node::Branch(group))));
379    }
380    build_branches::<T, Params>(level)
381}
382
383/// Ceil of the integer square root, without floating point (MSRV
384/// predates `usize::isqrt`). Integer Newton iteration.
385fn isqrt_ceil(n: usize) -> usize {
386    if n < 2 {
387        return n;
388    }
389    let mut x = n;
390    let mut y = x.div_ceil(2);
391    while y < x {
392        x = y;
393        y = usize::midpoint(x, n / x);
394    }
395    // `x` is floor(sqrt(n)); round up if it is not exact.
396    if x * x == n { x } else { x + 1 }
397}
398
399#[cfg(test)]
400#[allow(clippy::float_cmp, reason = "exact integer-valued point coordinates")]
401mod tests {
402    use super::Rtree;
403    use crate::bounds::Bounds;
404    use crate::predicate::Predicate;
405    use crate::split::Linear;
406    use geometry_cs::Cartesian;
407    use geometry_model::Point2D;
408    use geometry_trait::Point as _;
409
410    type P = Point2D<f64, Cartesian>;
411
412    #[test]
413    fn empty_tree() {
414        let t: Rtree<P> = Rtree::new();
415        assert!(t.is_empty());
416        assert_eq!(t.len(), 0);
417    }
418
419    #[test]
420    fn insert_many_points_keeps_len() {
421        let mut t: Rtree<P> = Rtree::new();
422        for i in 0..1000 {
423            let x = f64::from(i % 100);
424            let y = f64::from(i / 100);
425            t.insert(P::new(x, y));
426        }
427        assert_eq!(t.len(), 1000);
428        assert!(
429            t.height() >= 2,
430            "1000 points should build a multi-level tree"
431        );
432    }
433
434    #[test]
435    fn query_intersects_finds_the_point() {
436        let mut t: Rtree<P> = Rtree::new();
437        for i in 0..200 {
438            t.insert(P::new(f64::from(i), 0.0));
439        }
440        let hits = t.query(Predicate::Intersects(Bounds::new([9.5, -1.0], [10.5, 1.0])));
441        assert_eq!(hits.len(), 1);
442    }
443
444    #[test]
445    fn query_within_a_window() {
446        let mut t: Rtree<P> = Rtree::new();
447        for x in 0..10 {
448            for y in 0..10 {
449                t.insert(P::new(f64::from(x), f64::from(y)));
450            }
451        }
452        // The window [2,5]×[2,5] contains a 4×4 block of points.
453        let hits = t.query(Predicate::Within(Bounds::new([2.0, 2.0], [5.0, 5.0])));
454        assert_eq!(hits.len(), 16);
455    }
456
457    #[test]
458    fn nearest_returns_closest_first() {
459        let mut t: Rtree<P> = Rtree::new();
460        for i in 0..100 {
461            t.insert(P::new(f64::from(i), 0.0));
462        }
463        let near = t.nearest([10.2, 0.0], 3);
464        assert_eq!(near.len(), 3);
465        // The three closest to x=10.2 are x=10, 11, 9 in some order.
466        let mut xs: Vec<f64> = near.iter().map(|p| p.get::<0>()).collect();
467        xs.sort_by(|a, b| a.partial_cmp(b).unwrap());
468        assert_eq!(xs, [9.0, 10.0, 11.0]);
469    }
470
471    #[test]
472    fn linear_split_also_works() {
473        let mut t: Rtree<P, Linear<8, 3>> = Rtree::new();
474        for i in 0..500 {
475            t.insert(P::new(f64::from(i % 25), f64::from(i / 25)));
476        }
477        assert_eq!(t.len(), 500);
478        let hits = t.query(Predicate::Intersects(Bounds::new([0.0, 0.0], [3.0, 3.0])));
479        assert!(!hits.is_empty());
480    }
481
482    #[test]
483    fn bulk_load_balances() {
484        let points: Vec<P> = (0..10_000)
485            .map(|i| P::new(f64::from(i % 100), f64::from(i / 100)))
486            .collect();
487        let t: Rtree<P> = points.into_iter().collect();
488        assert_eq!(t.len(), 10_000);
489        // A balanced tree of 10k / 8-per-leaf is shallow.
490        assert!(t.height() <= 6, "STR tree too tall: {}", t.height());
491    }
492}