rstar/primitives/
geom_with_data.rs

1use crate::envelope::Envelope;
2use crate::object::PointDistance;
3use crate::{object::RTreeObject, point::Point};
4
5/// An [RTreeObject] with a geometry and some associated data that can be inserted into an r-tree.
6///
7/// Often, adding metadata (like a database ID) to a geometry is required before adding it
8/// into an r-tree. This struct removes some of the boilerplate required to do so.
9///
10/// **Note:** while the container itself implements [RTreeObject], you will have to go through its
11/// [`geom`](GeomWithData::geom) method in order to access geometry-specific methods.
12///
13/// # Example
14/// ```
15/// use rstar::{RTree, PointDistance};
16/// use rstar::primitives::GeomWithData;
17///
18/// type RestaurantLocation = GeomWithData<[f64; 2], &'static str>;
19///
20/// let mut restaurants = RTree::new();
21/// restaurants.insert(RestaurantLocation::new([0.3, 0.2], "Pete's Pizza Place"));
22/// restaurants.insert(RestaurantLocation::new([-0.8, 0.0], "The Great Steak"));
23/// restaurants.insert(RestaurantLocation::new([0.2, -0.2], "Fishy Fortune"));
24///
25/// let my_location = [0.0, 0.0];
26///
27/// // Now find the closest restaurant!
28/// let place = restaurants.nearest_neighbor(&my_location).unwrap();
29/// println!("Let's go to {}", place.data);
30/// println!("It's really close, only {} miles", place.distance_2(&my_location));
31/// ```
32#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
34pub struct GeomWithData<R: RTreeObject, T> {
35    geom: R,
36    /// Data to be associated with the geometry being stored in the [`RTree`](crate::RTree).
37    pub data: T,
38}
39
40impl<R: RTreeObject, T> RTreeObject for GeomWithData<R, T> {
41    type Envelope = R::Envelope;
42
43    fn envelope(&self) -> Self::Envelope {
44        self.geom.envelope()
45    }
46}
47
48impl<R: PointDistance, T> PointDistance for GeomWithData<R, T> {
49    fn distance_2(
50        &self,
51        point: &<Self::Envelope as Envelope>::Point,
52    ) -> <<Self::Envelope as Envelope>::Point as Point>::Scalar {
53        self.geom.distance_2(point)
54    }
55
56    fn contains_point(&self, p: &<Self::Envelope as Envelope>::Point) -> bool {
57        self.geom.contains_point(p)
58    }
59
60    fn distance_2_if_less_or_equal(
61        &self,
62        point: &<Self::Envelope as Envelope>::Point,
63        max_distance_2: <<Self::Envelope as Envelope>::Point as Point>::Scalar,
64    ) -> Option<<<Self::Envelope as Envelope>::Point as Point>::Scalar> {
65        self.geom.distance_2_if_less_or_equal(point, max_distance_2)
66    }
67}
68
69impl<R: RTreeObject, T> GeomWithData<R, T> {
70    /// Create a new [GeomWithData] struct using the provided geometry and data.
71    pub fn new(geom: R, data: T) -> Self {
72        Self { geom, data }
73    }
74
75    /// Get a reference to the container's geometry.
76    pub fn geom(&self) -> &R {
77        &self.geom
78    }
79}
80
81#[cfg(test)]
82mod test {
83    use super::GeomWithData;
84    use crate::object::PointDistance;
85
86    use approx::*;
87
88    use crate::{primitives::Line, RTree};
89
90    #[test]
91    fn container_in_rtree() {
92        let line_1 = GeomWithData::new(Line::new([0.0, 0.0], [1.0, 1.0]), ());
93        let line_2 = GeomWithData::new(Line::new([0.0, 0.0], [-1.0, 1.0]), ());
94        let tree = RTree::bulk_load(vec![line_1, line_2]);
95
96        assert!(tree.contains(&line_1));
97    }
98
99    #[test]
100    fn container_edge_distance() {
101        let edge = GeomWithData::new(Line::new([0.5, 0.5], [0.5, 2.0]), 1usize);
102
103        assert_abs_diff_eq!(edge.distance_2(&[0.5, 0.5]), 0.0);
104        assert_abs_diff_eq!(edge.distance_2(&[0.0, 0.5]), 0.5 * 0.5);
105        assert_abs_diff_eq!(edge.distance_2(&[0.5, 1.0]), 0.0);
106        assert_abs_diff_eq!(edge.distance_2(&[0.0, 0.0]), 0.5);
107        assert_abs_diff_eq!(edge.distance_2(&[0.0, 1.0]), 0.5 * 0.5);
108        assert_abs_diff_eq!(edge.distance_2(&[1.0, 1.0]), 0.5 * 0.5);
109        assert_abs_diff_eq!(edge.distance_2(&[1.0, 3.0]), 0.5 * 0.5 + 1.0);
110    }
111
112    #[test]
113    fn container_length_2() {
114        let line = GeomWithData::new(Line::new([1, -1], [5, 5]), 1usize);
115
116        assert_eq!(line.geom().length_2(), 16 + 36);
117    }
118
119    #[test]
120    fn container_nearest_neighbour() {
121        let mut lines = RTree::new();
122        lines.insert(GeomWithData::new(
123            Line::new([0.0, 0.0], [1.0, 1.0]),
124            "Line A",
125        ));
126        lines.insert(GeomWithData::new(
127            Line::new([0.0, 0.0], [-1.0, 1.0]),
128            "Line B",
129        ));
130        let my_location = [0.0, 0.0];
131        // Now find the closest line
132        let place = lines.nearest_neighbor(&my_location).unwrap();
133
134        assert_eq!(place.data, "Line A");
135    }
136}