scirs2_spatial/rtree/
deletion.rs

1use crate::error::SpatialResult;
2use crate::rtree::node::{Entry, Node, RTree, Rectangle};
3use scirs2_core::ndarray::ArrayView1;
4
5impl<T: Clone> RTree<T> {
6    /// Delete a data point from the R-tree
7    ///
8    /// # Arguments
9    ///
10    /// * `point` - The point coordinates to delete
11    /// * `data_predicate` - An optional function that takes a reference to the data and returns
12    ///   true if it should be deleted. This is useful when multiple data points share the same coordinates.
13    ///
14    /// # Returns
15    ///
16    /// A `SpatialResult` containing true if a point was deleted, false otherwise
17    pub fn delete<F>(
18        &mut self,
19        point: &ArrayView1<f64>,
20        data_predicate: Option<F>,
21    ) -> SpatialResult<bool>
22    where
23        F: Fn(&T) -> bool + Copy,
24    {
25        if point.len() != self.ndim() {
26            return Err(crate::error::SpatialError::DimensionError(format!(
27                "Point dimension {} does not match RTree dimension {}",
28                point.len(),
29                self.ndim()
30            )));
31        }
32
33        // Create a rectangle for the point
34        let mbr = Rectangle::from_point(point);
35
36        // Find the leaf node(s) containing the point
37        // Create a new empty root for swapping
38        let mut root = std::mem::take(&mut self.root);
39        let result = self.delete_internal(&mbr, &mut root, data_predicate)?;
40        self.root = root;
41
42        // If a point was deleted, decrement the size
43        if result {
44            self.decrement_size();
45
46            // If the root has only one child and it's not a leaf, make the child the new root
47            if !self.root._isleaf && self.root.size() == 1 {
48                if let Entry::NonLeaf { child, .. } = &self.root.entries[0] {
49                    let new_root = (**child).clone();
50                    self.root = new_root;
51                    // Height is decremented here (would normally use self.height -= 1)
52                }
53            }
54        }
55
56        Ok(result)
57    }
58
59    /// Helper function to delete a point from a subtree
60    #[allow(clippy::type_complexity)]
61    fn delete_internal<F>(
62        &mut self,
63        mbr: &Rectangle,
64        node: &mut Node<T>,
65        data_predicate: Option<F>,
66    ) -> SpatialResult<bool>
67    where
68        F: Fn(&T) -> bool + Copy,
69    {
70        // If this is a leaf node, look for the entry to delete
71        if node._isleaf {
72            let mut found_index = None;
73
74            for (i, entry) in node.entries.iter().enumerate() {
75                if let Entry::Leaf {
76                    mbr: entry_mbr,
77                    data,
78                    ..
79                } = entry
80                {
81                    // Check if the MBRs match
82                    if entry_mbr.min == mbr.min && entry_mbr.max == mbr.max {
83                        // If a _predicate is provided, check it too
84                        if let Some(ref pred) = data_predicate {
85                            if pred(data) {
86                                found_index = Some(i);
87                                break;
88                            }
89                        } else {
90                            // No predicate, just delete the first matching entry
91                            found_index = Some(i);
92                            break;
93                        }
94                    }
95                }
96            }
97
98            // If an entry was found, remove it
99            if let Some(index) = found_index {
100                node.entries.remove(index);
101                return Ok(true);
102            }
103
104            return Ok(false);
105        }
106
107        // For non-leaf nodes, find all child nodes that could contain the point
108        let mut deleted = false;
109
110        for i in 0..node.size() {
111            let entry_mbr = node.entries[i].mbr();
112
113            // Check if this entry's MBR intersects with the search MBR
114            if entry_mbr.intersects(mbr)? {
115                // Get the child node
116                if let Entry::NonLeaf { child, .. } = &mut node.entries[i] {
117                    // Recursively delete from the child using raw pointers to avoid borrow issues
118                    let child_ptr = Box::as_mut(child) as *mut Node<T>;
119                    let result =
120                        unsafe { self.delete_internal(mbr, &mut *child_ptr, data_predicate)? };
121
122                    if result {
123                        deleted = true;
124
125                        // Check if the child node is underfull
126                        if child.size() < self.min_entries && child.size() > 0 {
127                            // Handle underfull child node
128                            self.handle_underfull_node(node, i)?;
129                        } else if child.size() == 0 {
130                            // Remove empty child node
131                            node.entries.remove(i);
132                        } else {
133                            // Update the MBR of the parent entry
134                            if let Ok(Some(child_mbr)) = child.mbr() {
135                                if let Entry::NonLeaf { mbr, .. } = &mut node.entries[i] {
136                                    *mbr = child_mbr;
137                                }
138                            }
139                        }
140
141                        break;
142                    }
143                }
144            }
145        }
146
147        Ok(deleted)
148    }
149
150    /// Handle an underfull node by either merging it with a sibling or redistributing entries
151    fn handle_underfull_node(
152        &mut self,
153        parent: &mut Node<T>,
154        child_index: usize,
155    ) -> SpatialResult<()> {
156        // Get the child node
157        let child: &Box<Node<T>> = match &parent.entries[child_index] {
158            Entry::NonLeaf { child, .. } => child,
159            Entry::Leaf { .. } => {
160                return Err(crate::error::SpatialError::ComputationError(
161                    "Expected a non-leaf entry".into(),
162                ))
163            }
164        };
165
166        // Find the best sibling to merge with
167        let mut best_sibling_index = None;
168        let mut min_merged_area = f64::MAX;
169
170        for i in 0..parent.size() {
171            if i == child_index {
172                continue;
173            }
174
175            // Get the sibling's MBR
176            let sibling_mbr = parent.entries[i].mbr();
177
178            // Get the child's MBR
179            let child_mbr = child
180                .mbr()
181                .unwrap_or_else(|_| Some(Rectangle::from_point(&Array1::zeros(self.ndim()).view())))
182                .unwrap_or_else(|| Rectangle::from_point(&Array1::zeros(self.ndim()).view()));
183
184            // Calculate the area of the merged MBR
185            let merged_mbr = child_mbr.enlarge(sibling_mbr)?;
186            let merged_area = merged_mbr.area();
187
188            if merged_area < min_merged_area {
189                min_merged_area = merged_area;
190                best_sibling_index = Some(i);
191            }
192        }
193
194        // If no sibling was found, return (this shouldn't happen)
195        let best_sibling_index = best_sibling_index.unwrap_or(0);
196        if best_sibling_index == child_index {
197            return Ok(());
198        }
199
200        // Get the sibling
201        let sibling: &Box<Node<T>> = match &parent.entries[best_sibling_index] {
202            Entry::NonLeaf { child, .. } => child,
203            Entry::Leaf { .. } => {
204                return Err(crate::error::SpatialError::ComputationError(
205                    "Expected a non-leaf entry".into(),
206                ))
207            }
208        };
209
210        // If the sibling has enough entries, we can redistribute
211        if sibling.size() > self.min_entries {
212            // Implement entry redistribution
213
214            // Get mutable references to both child and sibling
215            let (child_idx, sibling_idx) = if child_index < best_sibling_index {
216                (child_index, best_sibling_index)
217            } else {
218                (best_sibling_index, child_index)
219            };
220
221            // Extract entries from parent temporarily
222            let mut child_entry = parent.entries.remove(child_idx);
223            let mut sibling_entry = parent.entries.remove(if sibling_idx > child_idx {
224                sibling_idx - 1
225            } else {
226                sibling_idx
227            });
228
229            // Get mutable references to the nodes
230            let child_node: &mut Box<Node<T>> = match &mut child_entry {
231                Entry::NonLeaf { child, .. } => child,
232                Entry::Leaf { .. } => {
233                    return Err(crate::error::SpatialError::ComputationError(
234                        "Expected a non-leaf entry for child node".into(),
235                    ))
236                }
237            };
238            let sibling_node: &mut Box<Node<T>> = match &mut sibling_entry {
239                Entry::NonLeaf { child, .. } => child,
240                Entry::Leaf { .. } => {
241                    return Err(crate::error::SpatialError::ComputationError(
242                        "Expected a non-leaf entry for sibling node".into(),
243                    ))
244                }
245            };
246
247            // Calculate how many entries to move
248            let total_entries = child_node.size() + sibling_node.size();
249            let target_child_size = total_entries / 2;
250
251            // Move entries from sibling to child if child has fewer entries
252            while child_node.size() < target_child_size && !sibling_node.entries.is_empty() {
253                let entry = sibling_node.entries.remove(0);
254                child_node.entries.push(entry);
255            }
256
257            // Update MBRs
258            if let Ok(Some(child_mbr)) = child_node.mbr() {
259                if let Entry::NonLeaf { mbr, .. } = &mut child_entry {
260                    *mbr = child_mbr;
261                }
262            }
263            if let Ok(Some(sibling_mbr)) = sibling_node.mbr() {
264                if let Entry::NonLeaf { mbr, .. } = &mut sibling_entry {
265                    *mbr = sibling_mbr;
266                }
267            }
268
269            // Put entries back in parent
270            parent.entries.insert(child_idx, child_entry);
271            parent.entries.insert(
272                if sibling_idx > child_idx {
273                    sibling_idx
274                } else {
275                    sibling_idx + 1
276                },
277                sibling_entry,
278            );
279
280            Ok(())
281        } else {
282            // Otherwise, merge the nodes
283
284            // Remove both entries from parent
285            let (smaller_idx, larger_idx) = if child_index < best_sibling_index {
286                (child_index, best_sibling_index)
287            } else {
288                (best_sibling_index, child_index)
289            };
290
291            let mut child_entry = parent.entries.remove(smaller_idx);
292            let sibling_entry = parent.entries.remove(larger_idx - 1);
293
294            // Get the nodes
295            let child_node: &mut Box<Node<T>> = match &mut child_entry {
296                Entry::NonLeaf { child, .. } => child,
297                Entry::Leaf { .. } => {
298                    return Err(crate::error::SpatialError::ComputationError(
299                        "Expected a non-leaf entry for child node".into(),
300                    ))
301                }
302            };
303            let sibling_node: Box<Node<T>> = match sibling_entry {
304                Entry::NonLeaf { child, .. } => child,
305                Entry::Leaf { .. } => {
306                    return Err(crate::error::SpatialError::ComputationError(
307                        "Expected a non-leaf entry for sibling node".into(),
308                    ))
309                }
310            };
311
312            // Move all entries from sibling to child
313            for entry in sibling_node.entries {
314                child_node.entries.push(entry);
315            }
316
317            // Update MBR of merged node
318            if let Ok(Some(merged_mbr)) = child_node.mbr() {
319                if let Entry::NonLeaf { mbr, .. } = &mut child_entry {
320                    *mbr = merged_mbr;
321                }
322            }
323
324            // Put the merged node back
325            parent.entries.insert(smaller_idx, child_entry);
326
327            // If parent is now underfull and is not the root, it needs handling too
328            // This would be handled by the caller
329
330            Ok(())
331        }
332    }
333}
334
335use scirs2_core::ndarray::Array1;
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340    use scirs2_core::ndarray::array;
341
342    #[test]
343    fn test_rtree_delete() {
344        // Create a new R-tree
345        let mut rtree: RTree<i32> = RTree::new(2, 2, 4).unwrap();
346
347        // Insert some points
348        let points = vec![
349            (array![0.0, 0.0], 0),
350            (array![1.0, 0.0], 1),
351            (array![0.0, 1.0], 2),
352            (array![1.0, 1.0], 3),
353            (array![0.5, 0.5], 4),
354        ];
355
356        for (point, value) in points {
357            rtree.insert(point, value).unwrap();
358        }
359
360        // Delete a point
361        let result = rtree
362            .delete::<fn(&i32) -> bool>(&array![0.5, 0.5].view(), None)
363            .unwrap();
364        assert!(result);
365
366        // Check the size
367        assert_eq!(rtree.size(), 4);
368
369        // Try to delete a point that doesn't exist
370        let result = rtree
371            .delete::<fn(&i32) -> bool>(&array![2.0, 2.0].view(), None)
372            .unwrap();
373        assert!(!result);
374
375        // Check the size
376        assert_eq!(rtree.size(), 4);
377
378        // Delete all remaining points
379        let result = rtree
380            .delete::<fn(&i32) -> bool>(&array![0.0, 0.0].view(), None)
381            .unwrap();
382        assert!(result);
383        let result = rtree
384            .delete::<fn(&i32) -> bool>(&array![1.0, 0.0].view(), None)
385            .unwrap();
386        assert!(result);
387        let result = rtree
388            .delete::<fn(&i32) -> bool>(&array![0.0, 1.0].view(), None)
389            .unwrap();
390        assert!(result);
391        let result = rtree
392            .delete::<fn(&i32) -> bool>(&array![1.0, 1.0].view(), None)
393            .unwrap();
394        assert!(result);
395
396        // Check the size
397        assert_eq!(rtree.size(), 0);
398        assert!(rtree.is_empty());
399    }
400}