From 029ae7d39ba1ff74e5d7d2a8fecdf1fa520df0ed Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sat, 28 Dec 2024 17:19:54 -0800 Subject: [PATCH 1/6] Implement nearest neighbor searches on RTree --- Cargo.lock | 11 ++++ Cargo.toml | 1 + src/rtree/mod.rs | 2 +- src/rtree/trait.rs | 134 +++++++++++++++++++++++++++++++++------------ src/type.rs | 15 +++++ 5 files changed, 128 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c0f0cc..2e8cab6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,6 +470,7 @@ dependencies = [ "geo-traits", "geozero", "num-traits", + "ordered-float", "rayon", "rstar", "thiserror 1.0.49", @@ -764,6 +765,16 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "bytemuck", + "num-traits", +] + [[package]] name = "pbkdf2" version = "0.12.2" diff --git a/Cargo.toml b/Cargo.toml index 61c45a7..d6d6e23 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ bytemuck = "1" float_next_after = "1" geo-traits = "0.2" num-traits = "0.2" +ordered-float = { version = "4", features = ["bytemuck"] } rayon = { version = "1.8.0", optional = true } thiserror = "1" tinyvec = { version = "1", features = ["alloc", "rustc_1_40"] } diff --git a/src/rtree/mod.rs b/src/rtree/mod.rs index b5bae48..b66baa4 100644 --- a/src/rtree/mod.rs +++ b/src/rtree/mod.rs @@ -58,4 +58,4 @@ pub mod util; pub use builder::RTreeBuilder; pub use index::{OwnedRTree, RTreeMetadata, RTreeRef}; -pub use r#trait::RTreeIndex; +pub use r#trait::{RTreeIndex, RTreeNeighbors}; diff --git a/src/rtree/trait.rs b/src/rtree/trait.rs index ba9c96d..d75d7c7 100644 --- a/src/rtree/trait.rs +++ b/src/rtree/trait.rs @@ -1,3 +1,6 @@ +use std::cmp::Reverse; +use std::collections::BinaryHeap; + use geo_traits::{CoordTrait, RectTrait}; use crate::error::Result; @@ -124,40 +127,6 @@ pub trait RTreeIndex: Sized { ) } - // #[allow(unused_mut, unused_labels, unused_variables)] - // fn neighbors(&self, x: N, y: N, max_distance: Option) -> Vec { - // let boxes = self.boxes(); - // let indices = self.indices(); - // let max_distance = max_distance.unwrap_or(N::max_value()); - - // let mut outer_node_index = Some(boxes.len() - 4); - - // let mut results = vec![]; - // let max_dist_squared = max_distance * max_distance; - - // 'outer: while let Some(node_index) = outer_node_index { - // // find the end index of the node - // let end = (node_index + self.node_size() * 4) - // .min(upper_bound(node_index, self.level_bounds())); - - // // add child nodes to the queue - // for pos in (node_index..end).step_by(4) { - // let index = indices.get(pos >> 2); - - // let dx = axis_dist(x, boxes[pos], boxes[pos + 2]); - // let dy = axis_dist(y, boxes[pos + 1], boxes[pos + 3]); - // let dist = dx * dx + dy * dy; - // if dist > max_dist_squared { - // continue; - // } - // } - - // // break 'outer; - // } - - // results - // } - /// Returns an iterator over the indexes of objects in this and another tree that intersect. /// /// Each returned object is of the form `(usize, usize)`, where the first is the positional @@ -175,6 +144,103 @@ pub trait RTreeIndex: Sized { } } +/// A trait for finding the nearest neighbors from an RTree. +/// +/// This is a separate trait because the [`BinaryHeap`] priority queue requires elements to be +/// `Ord`, and `N: IndexableNum` is not `Ord` by default because `f32` and `f64` are not `Ord`. +/// Instead, wrap your coordinates with `OrderedFloat` from the `ordered-float` crate if you wish +/// to support nearest neighbor searching with `f32` and `f64` data. +pub trait RTreeNeighbors: RTreeIndex { + /// Search items in order of distance from the given point. + fn neighbors( + &self, + x: N, + y: N, + max_results: Option, + max_distance: Option, + ) -> Vec { + let boxes = self.boxes(); + let indices = self.indices(); + let max_distance = max_distance.unwrap_or(N::max_value()); + + let mut outer_node_index = Some(boxes.len() - 4); + let mut queue = BinaryHeap::new(); + let mut results = vec![]; + let max_dist_squared = max_distance * max_distance; + + 'outer: while let Some(node_index) = outer_node_index { + // find the end index of the node + let end = (node_index + self.node_size() as usize * 4) + .min(upper_bound(node_index, self.level_bounds())); + + // add child nodes to the queue + for pos in (node_index..end).step_by(4) { + let index = indices.get(pos >> 2); + + let dx = axis_dist(x, boxes[pos], boxes[pos + 2]); + let dy = axis_dist(y, boxes[pos + 1], boxes[pos + 3]); + let dist = dx * dx + dy * dy; + if dist > max_dist_squared { + continue; + } + + if node_index >= self.num_items() as usize * 4 { + // node (use even id) + queue.push(Reverse(NeighborNode { + id: index << 1, + dist, + })); + } else { + // leaf item (use odd id) + queue.push(Reverse(NeighborNode { + id: (index << 1) + 1, + dist, + })); + } + } + + // pop items from the queue + while !queue.is_empty() && queue.peek().is_some_and(|val| (val.0.id & 1) != 0) { + let dist = queue.peek().unwrap().0.dist; + if dist > max_dist_squared { + break 'outer; + } + let item = queue.pop().unwrap(); + results.push(item.0.id >> 1); + if max_results.is_some_and(|max_results| results.len() == max_results) { + break 'outer; + } + } + + if let Some(item) = queue.pop() { + outer_node_index = Some(item.0.id >> 1); + } else { + outer_node_index = None; + } + } + + results + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct NeighborNode { + id: usize, + dist: N, +} + +impl Ord for NeighborNode { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.dist.cmp(&other.dist) + } +} + +impl PartialOrd for NeighborNode { + fn partial_cmp(&self, other: &Self) -> Option { + self.dist.partial_cmp(&other.dist) + } +} + impl RTreeIndex for OwnedRTree { fn boxes(&self) -> &[N] { self.metadata.boxes_slice(&self.buffer) diff --git a/src/type.rs b/src/type.rs index f7a8bcc..24e5d5e 100644 --- a/src/type.rs +++ b/src/type.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use num_traits::{Bounded, Num, NumCast, ToPrimitive}; +use ordered_float::OrderedFloat; use crate::kdtree::constants::KDBUSH_MAGIC; use crate::GeoIndexError; @@ -69,6 +70,16 @@ impl IndexableNum for f64 { const BYTES_PER_ELEMENT: usize = 8; } +impl IndexableNum for OrderedFloat { + const TYPE_INDEX: u8 = 7; + const BYTES_PER_ELEMENT: usize = 4; +} + +impl IndexableNum for OrderedFloat { + const TYPE_INDEX: u8 = 8; + const BYTES_PER_ELEMENT: usize = 8; +} + /// For compatibility with JS, which contains a Uint8ClampedArray const U8_CLAMPED_TYPE_INDEX: u8 = 2; @@ -137,6 +148,8 @@ impl CoordType { // https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed mod private { + use ordered_float::OrderedFloat; + pub trait Sealed {} impl Sealed for i8 {} @@ -147,4 +160,6 @@ mod private { impl Sealed for u32 {} impl Sealed for f32 {} impl Sealed for f64 {} + impl Sealed for OrderedFloat {} + impl Sealed for OrderedFloat {} } From 4bd3edce2a5d9c096faef28bf592da9a5a77aa3e Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sat, 28 Dec 2024 17:30:09 -0800 Subject: [PATCH 2/6] Add doctest --- src/rtree/builder.rs | 9 +++++++-- src/rtree/trait.rs | 31 +++++++++++++++++++++++++++---- src/rtree/traversal.rs | 3 ++- 3 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/rtree/builder.rs b/src/rtree/builder.rs index f673afb..d342fb4 100644 --- a/src/rtree/builder.rs +++ b/src/rtree/builder.rs @@ -72,7 +72,12 @@ impl RTreeBuilder { /// `RTreeIndex::search` will return this same insertion index, which allows you to reference /// your original collection. #[inline] - pub fn add(&mut self, min_x: N, min_y: N, max_x: N, max_y: N) -> usize { + pub fn add>(&mut self, min_x: M, min_y: M, max_x: M, max_y: M) -> usize { + let min_x = min_x.into(); + let min_y = min_y.into(); + let max_x = max_x.into(); + let max_y = max_y.into(); + let index = self.pos >> 2; let (boxes, mut indices) = split_data_borrow(&mut self.data, &self.metadata); @@ -109,7 +114,7 @@ impl RTreeBuilder { /// `RTreeIndex::search` will return this same insertion index, which allows you to reference /// your original collection. #[inline] - pub fn add_rect(&mut self, rect: &impl RectTrait) -> usize { + pub fn add_rect>(&mut self, rect: &impl RectTrait) -> usize { self.add( rect.min().x(), rect.min().y(), diff --git a/src/rtree/trait.rs b/src/rtree/trait.rs index d75d7c7..d69948f 100644 --- a/src/rtree/trait.rs +++ b/src/rtree/trait.rs @@ -150,15 +150,35 @@ pub trait RTreeIndex: Sized { /// `Ord`, and `N: IndexableNum` is not `Ord` by default because `f32` and `f64` are not `Ord`. /// Instead, wrap your coordinates with `OrderedFloat` from the `ordered-float` crate if you wish /// to support nearest neighbor searching with `f32` and `f64` data. +/// +/// ``` +/// use geo_index::rtree::{RTreeBuilder, RTreeIndex, RTreeNeighbors, RTreeRef}; +/// use geo_index::rtree::sort::HilbertSort; +/// use ordered_float::OrderedFloat; +/// +/// // Create an RTree +/// let mut builder = RTreeBuilder::>::new(3); +/// builder.add(0., 0., 2., 2.); +/// builder.add(1., 1., 3., 3.); +/// builder.add(2., 2., 4., 4.); +/// let tree = builder.finish::(); +/// +/// let results = tree.neighbors(5., 5., None, None); +/// assert_eq!(results, vec![2, 1, 0]); +/// ``` pub trait RTreeNeighbors: RTreeIndex { /// Search items in order of distance from the given point. - fn neighbors( + fn neighbors>( &self, - x: N, - y: N, + x: M, + y: M, max_results: Option, - max_distance: Option, + max_distance: Option, ) -> Vec { + let x = x.into(); + let y = y.into(); + let max_distance = max_distance.map(|a| a.into()); + let boxes = self.boxes(); let indices = self.indices(); let max_distance = max_distance.unwrap_or(N::max_value()); @@ -269,6 +289,9 @@ impl RTreeIndex for RTreeRef<'_, N> { } } +impl RTreeNeighbors for OwnedRTree {} +impl RTreeNeighbors for RTreeRef<'_, N> {} + /// Binary search for the first value in the array bigger than the given. #[inline] fn upper_bound(value: usize, arr: &[usize]) -> usize { diff --git a/src/rtree/traversal.rs b/src/rtree/traversal.rs index e587be2..542ef1e 100644 --- a/src/rtree/traversal.rs +++ b/src/rtree/traversal.rs @@ -375,7 +375,8 @@ mod test_issue_42 { // Find tree self-intersection canddiates using geo-index fn geo_index_contiguity(geoms: &Vec, node_size: u16) -> HashSet<(usize, usize)> { - let mut tree_builder = RTreeBuilder::new_with_node_size(geoms.len() as _, node_size); + let mut tree_builder: RTreeBuilder = + RTreeBuilder::new_with_node_size(geoms.len() as _, node_size); for geom in geoms { tree_builder.add_rect(&geom.bounding_rect().unwrap()); } From 9e916f6e842a6c79b429dda387cc1085a4f96fa9 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 29 Dec 2024 16:19:51 -0800 Subject: [PATCH 3/6] Remove Ord bound and document panic for NaN --- src/kdtree/mod.rs | 5 ++++ src/rtree/builder.rs | 9 ++---- src/rtree/mod.rs | 7 ++++- src/rtree/trait.rs | 68 +++++++++++++++++------------------------- src/rtree/traversal.rs | 3 +- src/type.rs | 28 ++--------------- 6 files changed, 43 insertions(+), 77 deletions(-) diff --git a/src/kdtree/mod.rs b/src/kdtree/mod.rs index ee8cb8d..e82e147 100644 --- a/src/kdtree/mod.rs +++ b/src/kdtree/mod.rs @@ -19,6 +19,11 @@ //! slice. If you don't know the coordinate type used in the index, you can use //! [`CoordType::from_buffer`][crate::CoordType::from_buffer] to infer the coordinate type. //! +//! ## Coordinate types +//! +//! Supported coordinate types implement [`IndexableNum`][crate::IndexableNum]. Note that float +//! `NaN` is not supported and may panic. +//! //! ## Example //! //! ``` diff --git a/src/rtree/builder.rs b/src/rtree/builder.rs index d342fb4..f673afb 100644 --- a/src/rtree/builder.rs +++ b/src/rtree/builder.rs @@ -72,12 +72,7 @@ impl RTreeBuilder { /// `RTreeIndex::search` will return this same insertion index, which allows you to reference /// your original collection. #[inline] - pub fn add>(&mut self, min_x: M, min_y: M, max_x: M, max_y: M) -> usize { - let min_x = min_x.into(); - let min_y = min_y.into(); - let max_x = max_x.into(); - let max_y = max_y.into(); - + pub fn add(&mut self, min_x: N, min_y: N, max_x: N, max_y: N) -> usize { let index = self.pos >> 2; let (boxes, mut indices) = split_data_borrow(&mut self.data, &self.metadata); @@ -114,7 +109,7 @@ impl RTreeBuilder { /// `RTreeIndex::search` will return this same insertion index, which allows you to reference /// your original collection. #[inline] - pub fn add_rect>(&mut self, rect: &impl RectTrait) -> usize { + pub fn add_rect(&mut self, rect: &impl RectTrait) -> usize { self.add( rect.min().x(), rect.min().y(), diff --git a/src/rtree/mod.rs b/src/rtree/mod.rs index b66baa4..2a056f6 100644 --- a/src/rtree/mod.rs +++ b/src/rtree/mod.rs @@ -18,6 +18,11 @@ //! slice. If you don't know the coordinate type used in the index, you can use //! [`CoordType::from_buffer`][crate::CoordType::from_buffer] to infer the coordinate type. //! +//! ## Coordinate types +//! +//! Supported coordinate types implement [`IndexableNum`][crate::IndexableNum]. Note that float +//! `NaN` is not supported and may panic. +//! //! ## Example //! //! ``` @@ -58,4 +63,4 @@ pub mod util; pub use builder::RTreeBuilder; pub use index::{OwnedRTree, RTreeMetadata, RTreeRef}; -pub use r#trait::{RTreeIndex, RTreeNeighbors}; +pub use r#trait::RTreeIndex; diff --git a/src/rtree/trait.rs b/src/rtree/trait.rs index d69948f..43f9173 100644 --- a/src/rtree/trait.rs +++ b/src/rtree/trait.rs @@ -142,43 +142,29 @@ pub trait RTreeIndex: Sized { fn root(&self) -> Node<'_, N, Self> { Node::from_root(self) } -} -/// A trait for finding the nearest neighbors from an RTree. -/// -/// This is a separate trait because the [`BinaryHeap`] priority queue requires elements to be -/// `Ord`, and `N: IndexableNum` is not `Ord` by default because `f32` and `f64` are not `Ord`. -/// Instead, wrap your coordinates with `OrderedFloat` from the `ordered-float` crate if you wish -/// to support nearest neighbor searching with `f32` and `f64` data. -/// -/// ``` -/// use geo_index::rtree::{RTreeBuilder, RTreeIndex, RTreeNeighbors, RTreeRef}; -/// use geo_index::rtree::sort::HilbertSort; -/// use ordered_float::OrderedFloat; -/// -/// // Create an RTree -/// let mut builder = RTreeBuilder::>::new(3); -/// builder.add(0., 0., 2., 2.); -/// builder.add(1., 1., 3., 3.); -/// builder.add(2., 2., 4., 4.); -/// let tree = builder.finish::(); -/// -/// let results = tree.neighbors(5., 5., None, None); -/// assert_eq!(results, vec![2, 1, 0]); -/// ``` -pub trait RTreeNeighbors: RTreeIndex { /// Search items in order of distance from the given point. - fn neighbors>( + /// ``` + /// use geo_index::rtree::{RTreeBuilder, RTreeIndex, RTreeRef}; + /// use geo_index::rtree::sort::HilbertSort; + /// + /// // Create an RTree + /// let mut builder = RTreeBuilder::::new(3); + /// builder.add(0., 0., 2., 2.); + /// builder.add(1., 1., 3., 3.); + /// builder.add(2., 2., 4., 4.); + /// let tree = builder.finish::(); + /// + /// let results = tree.neighbors(5., 5., None, None); + /// assert_eq!(results, vec![2, 1, 0]); + /// ``` + fn neighbors( &self, - x: M, - y: M, + x: N, + y: N, max_results: Option, - max_distance: Option, + max_distance: Option, ) -> Vec { - let x = x.into(); - let y = y.into(); - let max_distance = max_distance.map(|a| a.into()); - let boxes = self.boxes(); let indices = self.indices(); let max_distance = max_distance.unwrap_or(N::max_value()); @@ -243,21 +229,24 @@ pub trait RTreeNeighbors: RTreeIndex { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -struct NeighborNode { +#[derive(Debug, Clone, Copy, PartialEq)] +struct NeighborNode { id: usize, dist: N, } -impl Ord for NeighborNode { +impl Eq for NeighborNode {} + +impl Ord for NeighborNode { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.dist.cmp(&other.dist) + // We don't allow NaN. This should only panic on NaN + self.dist.partial_cmp(&other.dist).unwrap() } } -impl PartialOrd for NeighborNode { +impl PartialOrd for NeighborNode { fn partial_cmp(&self, other: &Self) -> Option { - self.dist.partial_cmp(&other.dist) + Some(self.cmp(other)) } } @@ -289,9 +278,6 @@ impl RTreeIndex for RTreeRef<'_, N> { } } -impl RTreeNeighbors for OwnedRTree {} -impl RTreeNeighbors for RTreeRef<'_, N> {} - /// Binary search for the first value in the array bigger than the given. #[inline] fn upper_bound(value: usize, arr: &[usize]) -> usize { diff --git a/src/rtree/traversal.rs b/src/rtree/traversal.rs index 542ef1e..e587be2 100644 --- a/src/rtree/traversal.rs +++ b/src/rtree/traversal.rs @@ -375,8 +375,7 @@ mod test_issue_42 { // Find tree self-intersection canddiates using geo-index fn geo_index_contiguity(geoms: &Vec, node_size: u16) -> HashSet<(usize, usize)> { - let mut tree_builder: RTreeBuilder = - RTreeBuilder::new_with_node_size(geoms.len() as _, node_size); + let mut tree_builder = RTreeBuilder::new_with_node_size(geoms.len() as _, node_size); for geom in geoms { tree_builder.add_rect(&geom.bounding_rect().unwrap()); } diff --git a/src/type.rs b/src/type.rs index 24e5d5e..b8be430 100644 --- a/src/type.rs +++ b/src/type.rs @@ -1,7 +1,6 @@ use std::fmt::Debug; -use num_traits::{Bounded, Num, NumCast, ToPrimitive}; -use ordered_float::OrderedFloat; +use num_traits::{Bounded, Num, NumCast}; use crate::kdtree::constants::KDBUSH_MAGIC; use crate::GeoIndexError; @@ -13,16 +12,7 @@ use crate::GeoIndexError; /// JavaScript ([rtree](https://github.com/mourner/flatbush), /// [kdtree](https://github.com/mourner/kdbush)) pub trait IndexableNum: - private::Sealed - + Num - + NumCast - + ToPrimitive - + PartialOrd - + Debug - + Send - + Sync - + bytemuck::Pod - + Bounded + private::Sealed + Num + NumCast + PartialOrd + Debug + Send + Sync + bytemuck::Pod + Bounded { /// The type index to match the array order of `ARRAY_TYPES` in flatbush JS const TYPE_INDEX: u8; @@ -70,16 +60,6 @@ impl IndexableNum for f64 { const BYTES_PER_ELEMENT: usize = 8; } -impl IndexableNum for OrderedFloat { - const TYPE_INDEX: u8 = 7; - const BYTES_PER_ELEMENT: usize = 4; -} - -impl IndexableNum for OrderedFloat { - const TYPE_INDEX: u8 = 8; - const BYTES_PER_ELEMENT: usize = 8; -} - /// For compatibility with JS, which contains a Uint8ClampedArray const U8_CLAMPED_TYPE_INDEX: u8 = 2; @@ -148,8 +128,6 @@ impl CoordType { // https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed mod private { - use ordered_float::OrderedFloat; - pub trait Sealed {} impl Sealed for i8 {} @@ -160,6 +138,4 @@ mod private { impl Sealed for u32 {} impl Sealed for f32 {} impl Sealed for f64 {} - impl Sealed for OrderedFloat {} - impl Sealed for OrderedFloat {} } From a001bdf04ca4b63e250db66332394e17325d19ea Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 29 Dec 2024 16:20:20 -0800 Subject: [PATCH 4/6] remove ordered_float --- Cargo.lock | 11 ----------- Cargo.toml | 1 - 2 files changed, 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e8cab6..3c0f0cc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -470,7 +470,6 @@ dependencies = [ "geo-traits", "geozero", "num-traits", - "ordered-float", "rayon", "rstar", "thiserror 1.0.49", @@ -765,16 +764,6 @@ version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" -[[package]] -name = "ordered-float" -version = "4.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" -dependencies = [ - "bytemuck", - "num-traits", -] - [[package]] name = "pbkdf2" version = "0.12.2" diff --git a/Cargo.toml b/Cargo.toml index d6d6e23..61c45a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ bytemuck = "1" float_next_after = "1" geo-traits = "0.2" num-traits = "0.2" -ordered-float = { version = "4", features = ["bytemuck"] } rayon = { version = "1.8.0", optional = true } thiserror = "1" tinyvec = { version = "1", features = ["alloc", "rustc_1_40"] } From 4abeccbc5819496aa8c27f2efce64d940d505179 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 29 Dec 2024 16:24:04 -0800 Subject: [PATCH 5/6] reorder --- src/rtree/trait.rs | 43 +++++++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/src/rtree/trait.rs b/src/rtree/trait.rs index 43f9173..60de9ad 100644 --- a/src/rtree/trait.rs +++ b/src/rtree/trait.rs @@ -127,23 +127,8 @@ pub trait RTreeIndex: Sized { ) } - /// Returns an iterator over the indexes of objects in this and another tree that intersect. - /// - /// Each returned object is of the form `(usize, usize)`, where the first is the positional - /// index of the "left" tree and the second is the index of the "right" tree. - fn intersection_candidates_with_other_tree<'a>( - &'a self, - other: &'a impl RTreeIndex, - ) -> impl Iterator + 'a { - IntersectionIterator::from_trees(self, other) - } - - /// Access the root node of the RTree for manual traversal. - fn root(&self) -> Node<'_, N, Self> { - Node::from_root(self) - } - /// Search items in order of distance from the given point. + /// /// ``` /// use geo_index::rtree::{RTreeBuilder, RTreeIndex, RTreeRef}; /// use geo_index::rtree::sort::HilbertSort; @@ -227,6 +212,32 @@ pub trait RTreeIndex: Sized { results } + + /// Search items in order of distance from the given coordinate. + fn neighbors_coord( + &self, + coord: &impl CoordTrait, + max_results: Option, + max_distance: Option, + ) -> Vec { + self.neighbors(coord.x(), coord.y(), max_results, max_distance) + } + + /// Returns an iterator over the indexes of objects in this and another tree that intersect. + /// + /// Each returned object is of the form `(usize, usize)`, where the first is the positional + /// index of the "left" tree and the second is the index of the "right" tree. + fn intersection_candidates_with_other_tree<'a>( + &'a self, + other: &'a impl RTreeIndex, + ) -> impl Iterator + 'a { + IntersectionIterator::from_trees(self, other) + } + + /// Access the root node of the RTree for manual traversal. + fn root(&self) -> Node<'_, N, Self> { + Node::from_root(self) + } } #[derive(Debug, Clone, Copy, PartialEq)] From 24427758d5fde888fdb32dfea337008a1cf2a697 Mon Sep 17 00:00:00 2001 From: Kyle Barron Date: Sun, 29 Dec 2024 16:25:03 -0800 Subject: [PATCH 6/6] comment --- src/rtree/trait.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rtree/trait.rs b/src/rtree/trait.rs index 60de9ad..b23e7b5 100644 --- a/src/rtree/trait.rs +++ b/src/rtree/trait.rs @@ -240,6 +240,7 @@ pub trait RTreeIndex: Sized { } } +/// A wrapper around a node and its distance for use in the priority queue. #[derive(Debug, Clone, Copy, PartialEq)] struct NeighborNode { id: usize,