Skip to main content

nodedb_spatial/rtree/
tree.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! R*-tree public API and core structure.
4
5use nodedb_types::BoundingBox;
6use std::sync::Arc;
7
8use super::node::{Node, NodeKind, RTreeEntry};
9use super::search::NnResult;
10use nodedb_mem::MemoryGovernor;
11
12/// R*-tree spatial index.
13///
14/// Supports insert, delete, range search (bbox intersection), and
15/// incremental nearest-neighbor queries. Array-backed nodes for cache
16/// friendliness.
17///
18/// References:
19/// - Beckmann et al., "The R*-tree" (1990)
20/// - Hjaltason & Samet, "Distance Browsing in Spatial Databases" (1999)
21pub struct RTree {
22    pub(crate) nodes: Vec<Node>,
23    pub(crate) root: usize,
24    pub(crate) len: usize,
25    /// Optional memory governor for budget enforcement (Origin only).
26    pub(crate) governor: Option<Arc<MemoryGovernor>>,
27}
28
29impl RTree {
30    pub fn new() -> Self {
31        Self {
32            nodes: vec![Node::new_leaf()],
33            root: 0,
34            len: 0,
35            governor: None,
36        }
37    }
38
39    /// Inject a [`MemoryGovernor`] to enforce per-engine memory budgets on
40    /// large batch allocations (bulk load, full-scan serialization, range
41    /// search result collection). When not set, allocations proceed without
42    /// budget enforcement — correct for NodeDB-Lite and WASM builds.
43    pub fn set_governor(&mut self, governor: Arc<MemoryGovernor>) {
44        self.governor = Some(governor);
45    }
46
47    /// Shared reference to the governor, if any.
48    pub(crate) fn governor(&self) -> Option<&Arc<MemoryGovernor>> {
49        self.governor.as_ref()
50    }
51
52    pub fn len(&self) -> usize {
53        self.len
54    }
55
56    pub fn is_empty(&self) -> bool {
57        self.len == 0
58    }
59
60    /// Range search: return all entries whose bbox intersects the query bbox.
61    pub fn search(&self, query: &BoundingBox) -> Vec<&RTreeEntry> {
62        let mut results = Vec::new();
63        super::search::search_node(&self.nodes, self.root, query, &mut results);
64        results
65    }
66
67    /// Range search returning owned entries.
68    pub fn search_owned(&self, query: &BoundingBox) -> Vec<RTreeEntry> {
69        self.search(query).into_iter().cloned().collect()
70    }
71
72    /// Nearest-neighbor search using incremental distance ordering.
73    pub fn nearest(&self, query_lng: f64, query_lat: f64, k: usize) -> Vec<NnResult> {
74        super::search::nearest(
75            &self.nodes,
76            self.root,
77            query_lng,
78            query_lat,
79            k,
80            self.is_empty(),
81        )
82    }
83
84    /// Get all entries (for persistence serialization).
85    pub fn entries(&self) -> Vec<&RTreeEntry> {
86        let _guard = self.governor().and_then(|gov| {
87            let bytes = self.len * std::mem::size_of::<*const RTreeEntry>();
88            gov.reserve(nodedb_mem::EngineId::Spatial, bytes).ok()
89        });
90        let mut result = Vec::with_capacity(self.len);
91        collect_entries(&self.nodes, self.root, &mut result);
92        result
93    }
94
95    /// Find parent of a node by traversal from root.
96    pub(crate) fn find_parent(&self, current: usize, target: usize) -> Option<usize> {
97        if let NodeKind::Internal { children } = &self.nodes[current].kind {
98            for child in children {
99                if child.node_idx == target {
100                    return Some(current);
101                }
102                if let Some(p) = self.find_parent(child.node_idx, target) {
103                    return Some(p);
104                }
105            }
106        }
107        None
108    }
109
110    /// Condense root: if root is internal with 1 child, collapse.
111    pub(crate) fn condense_root(&mut self) {
112        if let NodeKind::Internal { children } = &self.nodes[self.root].kind
113            && children.len() == 1
114        {
115            self.root = children[0].node_idx;
116        }
117    }
118}
119
120impl Default for RTree {
121    fn default() -> Self {
122        Self::new()
123    }
124}
125
126fn collect_entries<'a>(nodes: &'a [Node], node_idx: usize, result: &mut Vec<&'a RTreeEntry>) {
127    match &nodes[node_idx].kind {
128        NodeKind::Leaf { entries } => result.extend(entries.iter()),
129        NodeKind::Internal { children } => {
130            for child in children {
131                collect_entries(nodes, child.node_idx, result);
132            }
133        }
134    }
135}
136
137pub(crate) fn collect_entries_owned(nodes: &[Node], node_idx: usize, result: &mut Vec<RTreeEntry>) {
138    match &nodes[node_idx].kind {
139        NodeKind::Leaf { entries } => result.extend(entries.iter().cloned()),
140        NodeKind::Internal { children } => {
141            for child in children {
142                collect_entries_owned(nodes, child.node_idx, result);
143            }
144        }
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::super::node::LEAF_CAPACITY;
151    use super::*;
152
153    fn make_entry(id: u64, lng: f64, lat: f64) -> RTreeEntry {
154        RTreeEntry {
155            id,
156            bbox: BoundingBox::from_point(lng, lat),
157        }
158    }
159
160    fn make_rect(id: u64, min_lng: f64, min_lat: f64, max_lng: f64, max_lat: f64) -> RTreeEntry {
161        RTreeEntry {
162            id,
163            bbox: BoundingBox::new(min_lng, min_lat, max_lng, max_lat),
164        }
165    }
166
167    #[test]
168    fn empty_tree() {
169        let tree = RTree::new();
170        assert!(tree.is_empty());
171        assert!(
172            tree.search(&BoundingBox::new(-180.0, -90.0, 180.0, 90.0))
173                .is_empty()
174        );
175    }
176
177    #[test]
178    fn insert_and_search_single() {
179        let mut tree = RTree::new();
180        tree.insert(make_entry(1, 10.0, 20.0));
181        assert_eq!(tree.len(), 1);
182        assert_eq!(
183            tree.search(&BoundingBox::new(5.0, 15.0, 15.0, 25.0)).len(),
184            1
185        );
186        assert!(
187            tree.search(&BoundingBox::new(50.0, 50.0, 60.0, 60.0))
188                .is_empty()
189        );
190    }
191
192    #[test]
193    fn insert_many_and_search() {
194        let mut tree = RTree::new();
195        for i in 0..200 {
196            tree.insert(make_entry(
197                i,
198                (i as f64) * 0.5 - 50.0,
199                (i as f64) * 0.3 - 30.0,
200            ));
201        }
202        assert_eq!(tree.len(), 200);
203        let all = tree.search(&BoundingBox::new(-180.0, -90.0, 180.0, 90.0));
204        assert_eq!(all.len(), 200);
205    }
206
207    #[test]
208    fn delete_entry() {
209        let mut tree = RTree::new();
210        for i in 0..50 {
211            tree.insert(make_entry(i, i as f64, i as f64));
212        }
213        assert!(tree.delete(25));
214        assert_eq!(tree.len(), 49);
215        let all = tree.search(&BoundingBox::new(-1.0, -1.0, 100.0, 100.0));
216        assert!(all.iter().all(|e| e.id != 25));
217        assert!(!tree.delete(999));
218    }
219
220    #[test]
221    fn nearest_neighbor() {
222        let mut tree = RTree::new();
223        tree.insert(make_entry(1, 0.0, 0.0));
224        tree.insert(make_entry(2, 10.0, 10.0));
225        tree.insert(make_entry(3, 5.0, 5.0));
226        let nn = tree.nearest(4.0, 4.0, 2);
227        assert_eq!(nn.len(), 2);
228        assert_eq!(nn[0].entry_id, 3);
229        assert_eq!(nn[1].entry_id, 1);
230    }
231
232    #[test]
233    fn nearest_empty() {
234        assert!(RTree::new().nearest(0.0, 0.0, 5).is_empty());
235    }
236
237    #[test]
238    fn rect_overlap_search() {
239        let mut tree = RTree::new();
240        tree.insert(make_rect(1, 0.0, 0.0, 10.0, 10.0));
241        tree.insert(make_rect(2, 5.0, 5.0, 15.0, 15.0));
242        tree.insert(make_rect(3, 20.0, 20.0, 30.0, 30.0));
243        let results = tree.search(&BoundingBox::new(3.0, 3.0, 7.0, 7.0));
244        assert_eq!(results.len(), 2);
245    }
246
247    #[test]
248    fn stress_insert_delete() {
249        let mut tree = RTree::new();
250        for i in 0..100_u64 {
251            tree.insert(make_entry(i, (i as f64) * 0.5, (i as f64) * 0.3));
252        }
253        for i in (0..100_u64).step_by(2) {
254            assert!(tree.delete(i));
255        }
256        assert_eq!(tree.len(), 50);
257        let all = tree.search(&BoundingBox::new(-180.0, -90.0, 180.0, 90.0));
258        assert!(all.iter().all(|e| e.id % 2 == 1));
259    }
260
261    #[test]
262    fn triggers_node_split() {
263        let mut tree = RTree::new();
264        let count = LEAF_CAPACITY * 3;
265        for i in 0..count as u64 {
266            tree.insert(make_entry(i, (i as f64) * 0.1, (i as f64) * 0.1));
267        }
268        assert_eq!(tree.len(), count);
269        let all = tree.search(&BoundingBox::new(-180.0, -90.0, 180.0, 90.0));
270        assert_eq!(all.len(), count);
271    }
272
273    #[test]
274    fn entries_enumeration() {
275        let mut tree = RTree::new();
276        for i in 0..10 {
277            tree.insert(make_entry(i, i as f64, i as f64));
278        }
279        assert_eq!(tree.entries().len(), 10);
280    }
281}