Skip to main content

nodedb_spatial/rtree/
insert.rs

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