Skip to main content

nodedb_spatial/rtree/
insert.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! R*-tree insertion with overflow treatment (forced reinsert).
4
5use nodedb_types::BoundingBox;
6
7use super::node::{ChildRef, NodeKind, REINSERT_COUNT_LEAF, RTreeEntry};
8use super::split::split_node;
9use super::tree::RTree;
10
11impl RTree {
12    /// Insert an entry into the R-tree.
13    pub fn insert(&mut self, entry: RTreeEntry) {
14        let bbox = entry.bbox;
15        let mut reinserted_levels = Vec::new();
16        insert_entry(self, entry, 0, &mut reinserted_levels);
17        self.len += 1;
18        self.nodes[self.root].bbox = self.nodes[self.root].bbox.union(&bbox);
19    }
20
21    /// Insert with reinsert tracking (used internally and by delete).
22    pub(crate) fn reinsert_entry(&mut self, entry: RTreeEntry) {
23        let mut reinserted = Vec::new();
24        insert_entry(self, entry, 0, &mut reinserted);
25    }
26}
27
28fn insert_entry(
29    tree: &mut RTree,
30    entry: RTreeEntry,
31    target_level: u32,
32    reinserted_levels: &mut Vec<u32>,
33) {
34    let leaf_idx = choose_subtree(tree, tree.root, &entry.bbox, target_level);
35
36    match &mut tree.nodes[leaf_idx].kind {
37        NodeKind::Leaf { entries } => entries.push(entry),
38        NodeKind::Internal { .. } => {
39            // choose_subtree guarantees a leaf at target_level; if we reach
40            // here the tree structure is corrupted. Insert into root as
41            // a fallback rather than crashing a production system.
42            debug_assert!(false, "choose_subtree must return a leaf node");
43            return;
44        }
45    }
46    tree.nodes[leaf_idx].recompute_bbox();
47
48    if tree.nodes[leaf_idx].is_overflow() {
49        treat_overflow(tree, leaf_idx, reinserted_levels);
50    }
51}
52
53/// R*-tree ChooseSubtree: navigate to the best leaf for this entry.
54fn choose_subtree(
55    tree: &RTree,
56    node_idx: usize,
57    entry_bbox: &BoundingBox,
58    target_level: u32,
59) -> usize {
60    let node = &tree.nodes[node_idx];
61    if node.level == target_level {
62        return node_idx;
63    }
64
65    match &node.kind {
66        NodeKind::Leaf { .. } => node_idx,
67        NodeKind::Internal { children } => {
68            if children.is_empty() {
69                return node_idx;
70            }
71            let best = if tree.nodes[children[0].node_idx].is_leaf() {
72                choose_least_overlap(children, entry_bbox)
73            } else {
74                choose_least_enlargement(children, entry_bbox)
75            };
76            choose_subtree(tree, children[best].node_idx, entry_bbox, target_level)
77        }
78    }
79}
80
81fn choose_least_enlargement(children: &[ChildRef], entry_bbox: &BoundingBox) -> usize {
82    let mut best = 0;
83    let mut best_enlarge = f64::INFINITY;
84    let mut best_area = f64::INFINITY;
85    for (i, child) in children.iter().enumerate() {
86        let enlarge = child.bbox.enlargement(entry_bbox);
87        let area = child.bbox.area();
88        if enlarge < best_enlarge || (enlarge == best_enlarge && area < best_area) {
89            best = i;
90            best_enlarge = enlarge;
91            best_area = area;
92        }
93    }
94    best
95}
96
97fn choose_least_overlap(children: &[ChildRef], entry_bbox: &BoundingBox) -> usize {
98    let mut best = 0;
99    let mut best_oi = f64::INFINITY;
100    let mut best_enlarge = f64::INFINITY;
101    let mut best_area = f64::INFINITY;
102    for (i, child) in children.iter().enumerate() {
103        let enlarged = child.bbox.union(entry_bbox);
104        let mut before = 0.0_f64;
105        let mut after = 0.0_f64;
106        for (j, other) in children.iter().enumerate() {
107            if j != i {
108                before += child.bbox.overlap_area(&other.bbox);
109                after += enlarged.overlap_area(&other.bbox);
110            }
111        }
112        let oi = after - before;
113        let enlarge = child.bbox.enlargement(entry_bbox);
114        let area = child.bbox.area();
115        if oi < best_oi
116            || (oi == best_oi && enlarge < best_enlarge)
117            || (oi == best_oi && enlarge == best_enlarge && area < best_area)
118        {
119            best = i;
120            best_oi = oi;
121            best_enlarge = enlarge;
122            best_area = area;
123        }
124    }
125    best
126}
127
128/// R*-tree overflow: forced reinsert first, then split on second overflow.
129fn treat_overflow(tree: &mut RTree, node_idx: usize, reinserted_levels: &mut Vec<u32>) {
130    let level = tree.nodes[node_idx].level;
131    if node_idx != tree.root && !reinserted_levels.contains(&level) {
132        reinserted_levels.push(level);
133        let entries = forced_reinsert(tree, node_idx);
134        for entry in entries {
135            insert_entry(tree, entry, 0, reinserted_levels);
136        }
137    } else {
138        split_node(tree, node_idx);
139    }
140}
141
142/// Remove the farthest entries from node center and return for reinsertion.
143fn forced_reinsert(tree: &mut RTree, node_idx: usize) -> Vec<RTreeEntry> {
144    let reinsert_count = if tree.nodes[node_idx].is_leaf() {
145        REINSERT_COUNT_LEAF
146    } else {
147        0
148    };
149    if reinsert_count == 0 {
150        return Vec::new();
151    }
152
153    let center_lng = (tree.nodes[node_idx].bbox.min_lng + tree.nodes[node_idx].bbox.max_lng) / 2.0;
154    let center_lat = (tree.nodes[node_idx].bbox.min_lat + tree.nodes[node_idx].bbox.max_lat) / 2.0;
155
156    if let NodeKind::Leaf { entries } = &mut tree.nodes[node_idx].kind {
157        entries.sort_by(|a, b| {
158            let da = dist_sq_center(center_lng, center_lat, &a.bbox);
159            let db = dist_sq_center(center_lng, center_lat, &b.bbox);
160            db.partial_cmp(&da).unwrap_or(std::cmp::Ordering::Equal)
161        });
162        let removed: Vec<RTreeEntry> = entries.drain(..reinsert_count).collect();
163        tree.nodes[node_idx].recompute_bbox();
164        removed
165    } else {
166        Vec::new()
167    }
168}
169
170fn dist_sq_center(lng: f64, lat: f64, bbox: &BoundingBox) -> f64 {
171    let cx = (bbox.min_lng + bbox.max_lng) / 2.0;
172    let cy = (bbox.min_lat + bbox.max_lat) / 2.0;
173    (lng - cx).powi(2) + (lat - cy).powi(2)
174}