-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
74: generic geom + data container r=urschrei a=urschrei - [x ] I agree to follow the project's [code of conduct](https://github.com/georust/geo/blob/master/CODE_OF_CONDUCT.md). - [x] I added an entry to `rstar/CHANGELOG.md` if knowledge of this change could be valuable to users. --- This is a generic implementation of `PointWithData` and #73. <s>All fields are private and require getters, and</s> The geometry field can't be modified after creation in order to prevent a logic error if it's in an RTree. This PR also adds a deprecation notice to `PointWithData`. For discussion: - ~~The name~~ - ~~struct fields~~ - ~~Use of getters~~ - ~~What should we be deriving?~~ - ~~Can we derive serde traits?~~ - ~~Is there any need for a `Default` on either field?~~ Co-authored-by: Stephan Hügel <shugel@tcd.ie>
- Loading branch information
Showing
5 changed files
with
147 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
use crate::envelope::Envelope; | ||
use crate::object::PointDistance; | ||
use crate::{object::RTreeObject, point::Point}; | ||
|
||
/// An [RTreeObject] with a geometry and some associated data that can be inserted into an r-tree. | ||
/// | ||
/// Often, adding metadata (like a database ID) to a geometry is required before adding it | ||
/// into an r-tree. This struct removes some of the boilerplate required to do so. | ||
/// | ||
/// **Note:** while the container itself implements [RTreeObject], you will have to go through its | ||
/// [`geom`](GeomWithData::geom) method in order to access geometry-specific methods. | ||
/// | ||
/// # Example | ||
/// ``` | ||
/// use rstar::{RTree, PointDistance}; | ||
/// use rstar::primitives::GeomWithData; | ||
/// | ||
/// type RestaurantLocation = GeomWithData<[f64; 2], &'static str>; | ||
/// | ||
/// let mut restaurants = RTree::new(); | ||
/// restaurants.insert(RestaurantLocation::new([0.3, 0.2], "Pete's Pizza Place")); | ||
/// restaurants.insert(RestaurantLocation::new([-0.8, 0.0], "The Great Steak")); | ||
/// restaurants.insert(RestaurantLocation::new([0.2, -0.2], "Fishy Fortune")); | ||
/// | ||
/// let my_location = [0.0, 0.0]; | ||
/// | ||
/// // Now find the closest restaurant! | ||
/// let place = restaurants.nearest_neighbor(&my_location).unwrap(); | ||
/// println!("Let's go to {}", place.data); | ||
/// println!("It's really close, only {} miles", place.distance_2(&my_location)); | ||
/// ``` | ||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] | ||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] | ||
pub struct GeomWithData<R: RTreeObject, T> { | ||
geom: R, | ||
/// Data to be associated with the geometry being stored in the [`RTree`](crate::RTree). | ||
pub data: T, | ||
} | ||
|
||
impl<R: RTreeObject, T> RTreeObject for GeomWithData<R, T> { | ||
type Envelope = R::Envelope; | ||
|
||
fn envelope(&self) -> Self::Envelope { | ||
self.geom.envelope() | ||
} | ||
} | ||
|
||
impl<R: PointDistance, T> PointDistance for GeomWithData<R, T> { | ||
fn distance_2( | ||
&self, | ||
point: &<Self::Envelope as Envelope>::Point, | ||
) -> <<Self::Envelope as Envelope>::Point as Point>::Scalar { | ||
self.geom.distance_2(point) | ||
} | ||
|
||
fn contains_point(&self, p: &<Self::Envelope as Envelope>::Point) -> bool { | ||
self.geom.contains_point(p) | ||
} | ||
|
||
fn distance_2_if_less_or_equal( | ||
&self, | ||
point: &<Self::Envelope as Envelope>::Point, | ||
max_distance_2: <<Self::Envelope as Envelope>::Point as Point>::Scalar, | ||
) -> Option<<<Self::Envelope as Envelope>::Point as Point>::Scalar> { | ||
self.geom.distance_2_if_less_or_equal(point, max_distance_2) | ||
} | ||
} | ||
|
||
impl<R: RTreeObject, T> GeomWithData<R, T> { | ||
/// Create a new [GeomWithData] struct using the provided geometry and data. | ||
pub fn new(geom: R, data: T) -> Self { | ||
Self { geom, data } | ||
} | ||
|
||
/// Get a reference to the container's geometry. | ||
pub fn geom(&self) -> &R { | ||
&self.geom | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use super::GeomWithData; | ||
use crate::object::PointDistance; | ||
|
||
use approx::*; | ||
|
||
use crate::{primitives::Line, RTree}; | ||
|
||
#[test] | ||
fn container_in_rtree() { | ||
let line_1 = GeomWithData::new(Line::new([0.0, 0.0], [1.0, 1.0]), ()); | ||
let line_2 = GeomWithData::new(Line::new([0.0, 0.0], [-1.0, 1.0]), ()); | ||
let tree = RTree::bulk_load(vec![line_1, line_2]); | ||
|
||
assert!(tree.contains(&line_1)); | ||
} | ||
|
||
#[test] | ||
fn container_edge_distance() { | ||
let edge = GeomWithData::new(Line::new([0.5, 0.5], [0.5, 2.0]), 1usize); | ||
|
||
assert_abs_diff_eq!(edge.distance_2(&[0.5, 0.5]), 0.0); | ||
assert_abs_diff_eq!(edge.distance_2(&[0.0, 0.5]), 0.5 * 0.5); | ||
assert_abs_diff_eq!(edge.distance_2(&[0.5, 1.0]), 0.0); | ||
assert_abs_diff_eq!(edge.distance_2(&[0.0, 0.0]), 0.5); | ||
assert_abs_diff_eq!(edge.distance_2(&[0.0, 1.0]), 0.5 * 0.5); | ||
assert_abs_diff_eq!(edge.distance_2(&[1.0, 1.0]), 0.5 * 0.5); | ||
assert_abs_diff_eq!(edge.distance_2(&[1.0, 3.0]), 0.5 * 0.5 + 1.0); | ||
} | ||
|
||
#[test] | ||
fn container_length_2() { | ||
let line = GeomWithData::new(Line::new([1, -1], [5, 5]), 1usize); | ||
|
||
assert_eq!(line.geom().length_2(), 16 + 36); | ||
} | ||
|
||
#[test] | ||
fn container_nearest_neighbour() { | ||
let mut lines = RTree::new(); | ||
lines.insert(GeomWithData::new( | ||
Line::new([0.0, 0.0], [1.0, 1.0]), | ||
"Line A", | ||
)); | ||
lines.insert(GeomWithData::new( | ||
Line::new([0.0, 0.0], [-1.0, 1.0]), | ||
"Line B", | ||
)); | ||
let my_location = [0.0, 0.0]; | ||
// Now find the closest line | ||
let place = lines.nearest_neighbor(&my_location).unwrap(); | ||
|
||
assert_eq!(place.data, "Line A"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,11 @@ | ||
//! Contains primitives ready for insertion into an r-tree. | ||
|
||
mod geom_with_data; | ||
mod line; | ||
mod point_with_data; | ||
mod rectangle; | ||
|
||
pub use self::geom_with_data::GeomWithData; | ||
pub use self::line::Line; | ||
pub use self::point_with_data::PointWithData; | ||
pub use self::rectangle::Rectangle; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters