diff --git a/geo-types/src/algorithms.rs b/geo-types/src/algorithms.rs index a659245ae..dd95d6409 100644 --- a/geo-types/src/algorithms.rs +++ b/geo-types/src/algorithms.rs @@ -2,7 +2,7 @@ // wouldn't have this duplication use num_traits::{Float, ToPrimitive}; -use {Coordinate, CoordinateType, Line, LineString, Point}; +use {Coordinate, CoordinateType, Line, LineString, Point, Rect}; pub static COORD_PRECISION: f32 = 1e-1; // 0.1m @@ -125,10 +125,10 @@ where } } -pub trait BoundingBox { +pub trait BoundingRect { type Output; - fn bbox(&self) -> Self::Output; + fn bounding_rect(&self) -> Self::Output; } fn get_min_max(p: T, min: T, max: T) -> (T, T) @@ -144,7 +144,7 @@ where } } -fn get_bbox(collection: I) -> Option> +fn get_bounding_rect(collection: I) -> Option> where T: CoordinateType, I: IntoIterator>, @@ -158,57 +158,48 @@ where xrange = get_min_max(px, xrange.0, xrange.1); yrange = get_min_max(py, yrange.0, yrange.1); } - return Some(Bbox { - xmin: xrange.0, - xmax: xrange.1, - ymin: yrange.0, - ymax: yrange.1, + return Some(Rect { + min: Coordinate { + x: xrange.0, + y: yrange.0, + }, + max: Coordinate { + x: xrange.1, + y: yrange.1, + }, }); } None } -impl BoundingBox for Line +impl BoundingRect for Line where T: CoordinateType, { - type Output = Bbox; + type Output = Rect; - fn bbox(&self) -> Self::Output { + fn bounding_rect(&self) -> Self::Output { let a = self.start; let b = self.end; let (xmin, xmax) = if a.x <= b.x { (a.x, b.x) } else { (b.x, a.x) }; let (ymin, ymax) = if a.y <= b.y { (a.y, b.y) } else { (b.y, a.y) }; - Bbox { - xmin, - xmax, - ymin, - ymax, + Rect { + min: Coordinate { x: xmin, y: ymin }, + max: Coordinate { x: xmax, y: ymax }, } } } -impl BoundingBox for LineString +impl BoundingRect for LineString where T: CoordinateType, { - type Output = Option>; + type Output = Option>; /// - /// Return the BoundingBox for a LineString + /// Return the bounding rectangle for a LineString /// - fn bbox(&self) -> Self::Output { - get_bbox(self.0.iter().cloned()) + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.0.iter().cloned()) } } - -#[derive(PartialEq, Clone, Copy, Debug)] -pub struct Bbox -where - T: CoordinateType, -{ - pub xmin: T, - pub xmax: T, - pub ymin: T, - pub ymax: T, -} diff --git a/geo-types/src/lib.rs b/geo-types/src/lib.rs index f6209ae62..c4a226d7d 100644 --- a/geo-types/src/lib.rs +++ b/geo-types/src/lib.rs @@ -53,6 +53,9 @@ pub use geometry_collection::GeometryCollection; mod triangle; pub use triangle::Triangle; +mod rect; +pub use rect::Rect; + #[cfg(test)] mod test { use super::*; diff --git a/geo-types/src/line.rs b/geo-types/src/line.rs index 817c927bd..a8efb89ee 100644 --- a/geo-types/src/line.rs +++ b/geo-types/src/line.rs @@ -1,7 +1,7 @@ use {Coordinate, CoordinateType, Point}; #[cfg(feature = "spade")] -use algorithms::{BoundingBox, EuclideanDistance}; +use algorithms::{BoundingRect, EuclideanDistance}; /// A line segment made up of exactly two [`Point`s](struct.Point.html) #[derive(PartialEq, Clone, Copy, Debug)] @@ -171,10 +171,10 @@ where type Point = Point; fn mbr(&self) -> ::spade::BoundingRect { - let bbox = self.bbox(); + let bounding_rect = self.bounding_rect(); ::spade::BoundingRect::from_corners( - &Point::new(bbox.xmin, bbox.ymin), - &Point::new(bbox.xmax, bbox.ymax), + &Point::new(bounding_rect.min.x, bounding_rect.min.y), + &Point::new(bounding_rect.max.x, bounding_rect.max.y), ) } diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 73313badc..d2337e1d0 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -2,7 +2,7 @@ use std::iter::FromIterator; use {Coordinate, CoordinateType, Line, Point, Triangle}; #[cfg(feature = "spade")] -use algorithms::{BoundingBox, EuclideanDistance}; +use algorithms::{BoundingRect, EuclideanDistance}; /// An ordered collection of two or more [`Coordinate`s](struct.Coordinate.html), representing a /// path between locations. @@ -174,20 +174,16 @@ where type Point = Point; fn mbr(&self) -> ::spade::BoundingRect { - let bbox = self.bbox(); - match bbox { - None => { - ::spade::BoundingRect::from_corners( - &Point::new(T::min_value(), T::min_value()), - &Point::new(T::max_value(), T::max_value()), - ) - }, - Some(b) => { - ::spade::BoundingRect::from_corners( - &Point::new(b.xmin, b.ymin), - &Point::new(b.xmax, b.ymax), - ) - }, + let bounding_rect = self.bounding_rect(); + match bounding_rect { + None => ::spade::BoundingRect::from_corners( + &Point::new(T::min_value(), T::min_value()), + &Point::new(T::max_value(), T::max_value()), + ), + Some(b) => ::spade::BoundingRect::from_corners( + &Point::new(b.min.x, b.min.y), + &Point::new(b.max.x, b.max.y), + ), } } diff --git a/geo-types/src/rect.rs b/geo-types/src/rect.rs new file mode 100644 index 000000000..de4bb69d4 --- /dev/null +++ b/geo-types/src/rect.rs @@ -0,0 +1,11 @@ +use {Coordinate, CoordinateType}; + +#[derive(PartialEq, Clone, Copy, Debug)] +#[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))] +pub struct Rect +where + T: CoordinateType, +{ + pub min: Coordinate, + pub max: Coordinate, +} diff --git a/geo/src/algorithm/area.rs b/geo/src/algorithm/area.rs index ece1172ed..b49b00acc 100644 --- a/geo/src/algorithm/area.rs +++ b/geo/src/algorithm/area.rs @@ -1,5 +1,5 @@ use num_traits::Float; -use {Bbox, Line, LineString, MultiPolygon, Polygon, Triangle}; +use {Line, LineString, MultiPolygon, Polygon, Rect, Triangle}; use algorithm::winding_order::twice_signed_ring_area; @@ -74,12 +74,12 @@ where } } -impl Area for Bbox +impl Area for Rect where T: Float, { fn area(&self) -> T { - (self.xmax - self.xmin) * (self.ymax - self.ymin) + (self.max.x - self.min.x) * (self.max.y - self.min.y) } } @@ -97,7 +97,7 @@ where #[cfg(test)] mod test { use algorithm::area::Area; - use {Bbox, Coordinate, Line, LineString, MultiPolygon, Polygon, Triangle}; + use {Coordinate, Line, LineString, MultiPolygon, Polygon, Rect, Triangle}; // Area of the polygon #[test] @@ -118,14 +118,12 @@ mod test { assert_relative_eq!(poly.area(), 30.); } #[test] - fn bbox_test() { - let bbox = Bbox { - xmin: 10., - xmax: 20., - ymin: 30., - ymax: 40., + fn rectangle_test() { + let rect = Rect { + min: Coordinate { x: 10., y: 30. }, + max: Coordinate { x: 20., y: 40. }, }; - assert_relative_eq!(bbox.area(), 100.); + assert_relative_eq!(rect.area(), 100.); } #[test] fn area_polygon_inner_test() { diff --git a/geo/src/algorithm/bounding_rect.rs b/geo/src/algorithm/bounding_rect.rs new file mode 100644 index 000000000..15b239c5c --- /dev/null +++ b/geo/src/algorithm/bounding_rect.rs @@ -0,0 +1,290 @@ +use { + Coordinate, CoordinateType, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, + Polygon, Rect, Triangle, +}; + +/// Calculation of the bounding rectangle of a geometry. +pub trait BoundingRect { + type Output; + + /// Return the bounding rectangle of a geometry + /// + /// # Examples + /// + /// ``` + /// use geo::{Point, LineString}; + /// use geo::algorithm::bounding_rect::BoundingRect; + /// + /// let mut vec = Vec::new(); + /// vec.push(Point::new(40.02f64, 116.34)); + /// vec.push(Point::new(42.02f64, 116.34)); + /// vec.push(Point::new(42.02f64, 118.34)); + /// let linestring = LineString::from(vec); + /// let bounding_rect = linestring.bounding_rect().unwrap(); + /// + /// assert_eq!(40.02f64, bounding_rect.min.x); + /// assert_eq!(42.02f64, bounding_rect.max.x); + /// assert_eq!(116.34, bounding_rect.min.y); + /// assert_eq!(118.34, bounding_rect.max.y); + /// ``` + /// + fn bounding_rect(&self) -> Self::Output; +} + +fn get_min_max(p: T, min: T, max: T) -> (T, T) +where + T: CoordinateType, +{ + if p > max { + (min, p) + } else if p < min { + (p, max) + } else { + (min, max) + } +} + +fn get_bounding_rect(collection: I) -> Option> +where + T: CoordinateType, + I: IntoIterator>, +{ + let mut iter = collection.into_iter(); + if let Some(pnt) = iter.next() { + let mut xrange = (pnt.x, pnt.x); + let mut yrange = (pnt.y, pnt.y); + for pnt in iter { + let (px, py) = pnt.x_y(); + xrange = get_min_max(px, xrange.0, xrange.1); + yrange = get_min_max(py, yrange.0, yrange.1); + } + return Some(Rect { + min: Coordinate { + x: xrange.0, + y: yrange.0, + }, + max: Coordinate { + x: xrange.1, + y: yrange.1, + }, + }); + } + None +} + +impl BoundingRect for MultiPoint +where + T: CoordinateType, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a MultiPoint + /// + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.0.iter().map(|p| p.0)) + } +} + +impl BoundingRect for Line +where + T: CoordinateType, +{ + type Output = Rect; + + fn bounding_rect(&self) -> Self::Output { + let a = self.start; + let b = self.end; + let (xmin, xmax) = if a.x <= b.x { (a.x, b.x) } else { (b.x, a.x) }; + let (ymin, ymax) = if a.y <= b.y { (a.y, b.y) } else { (b.y, a.y) }; + Rect { + min: Coordinate { x: xmin, y: ymin }, + max: Coordinate { x: xmax, y: ymax }, + } + } +} + +impl BoundingRect for LineString +where + T: CoordinateType, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a LineString + /// + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.0.iter().cloned()) + } +} + +impl BoundingRect for MultiLineString +where + T: CoordinateType, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a MultiLineString + /// + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.0.iter().flat_map(|line| line.0.iter().map(|c| *c))) + } +} + +impl BoundingRect for Polygon +where + T: CoordinateType, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a Polygon + /// + fn bounding_rect(&self) -> Self::Output { + let line = &self.exterior; + get_bounding_rect(line.0.iter().cloned()) + } +} + +impl BoundingRect for MultiPolygon +where + T: CoordinateType, +{ + type Output = Option>; + + /// + /// Return the BoundingRect for a MultiPolygon + /// + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect( + self.0 + .iter() + .flat_map(|poly| (poly.exterior).0.iter().map(|c| *c)), + ) + } +} + +impl BoundingRect for Triangle +where + T: CoordinateType, +{ + type Output = Rect; + + fn bounding_rect(&self) -> Self::Output { + get_bounding_rect(self.to_array().into_iter().cloned()).unwrap() + } +} + +#[cfg(test)] +mod test { + use algorithm::bounding_rect::BoundingRect; + use { + Coordinate, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, + Rect, + }; + + #[test] + fn empty_linestring_test() { + let linestring: LineString = LineString(vec![]); + let bounding_rect = linestring.bounding_rect(); + assert!(bounding_rect.is_none()); + } + #[test] + fn linestring_one_point_test() { + let vec = vec![(40.02f64, 116.34)]; + let linestring = LineString::from(vec); + let bounding_rect = Rect { + min: Coordinate { + x: 40.02f64, + y: 116.34, + }, + max: Coordinate { + x: 40.02, + y: 116.34, + }, + }; + assert_eq!(bounding_rect, linestring.bounding_rect().unwrap()); + } + #[test] + fn linestring_test() { + let linestring = LineString::from(vec![(1., 1.), (2., -2.), (-3., -3.), (-4., 4.)]); + let bounding_rect = Rect { + min: Coordinate { x: -4., y: -3. }, + max: Coordinate { x: 2., y: 4. }, + }; + assert_eq!(bounding_rect, linestring.bounding_rect().unwrap()); + } + #[test] + fn multilinestring_test() { + let multiline = MultiLineString(vec![ + LineString::from(vec![(1., 1.), (-40., 1.)]), + LineString::from(vec![(1., 1.), (50., 1.)]), + LineString::from(vec![(1., 1.), (1., -60.)]), + LineString::from(vec![(1., 1.), (1., 70.)]), + ]); + let bounding_rect = Rect { + min: Coordinate { x: -40., y: -60. }, + max: Coordinate { x: 50., y: 70. }, + }; + assert_eq!(bounding_rect, multiline.bounding_rect().unwrap()); + } + #[test] + fn multipoint_test() { + let p = |x, y| Point(Coordinate { x: x, y: y }); + let multipoint = MultiPoint(vec![p(1., 1.), p(2., -2.), p(-3., -3.), p(-4., 4.)]); + let bounding_rect = Rect { + min: Coordinate { x: -4., y: -3. }, + max: Coordinate { x: 2., y: 4. }, + }; + assert_eq!(bounding_rect, multipoint.bounding_rect().unwrap()); + } + #[test] + fn polygon_test() { + let linestring = LineString::from(vec![(0., 0.), (5., 0.), (5., 6.), (0., 6.), (0., 0.)]); + let line_bounding_rect = linestring.bounding_rect().unwrap(); + let poly = Polygon::new(linestring, Vec::new()); + assert_eq!(line_bounding_rect, poly.bounding_rect().unwrap()); + } + #[test] + fn multipolygon_test() { + let mpoly = MultiPolygon(vec![ + Polygon::new( + LineString::from(vec![(0., 0.), (50., 0.), (0., -70.), (0., 0.)]), + Vec::new(), + ), + Polygon::new( + LineString::from(vec![(0., 0.), (5., 0.), (0., 80.), (0., 0.)]), + Vec::new(), + ), + Polygon::new( + LineString::from(vec![(0., 0.), (-60., 0.), (0., 6.), (0., 0.)]), + Vec::new(), + ), + ]); + let bounding_rect = Rect { + min: Coordinate { x: -60., y: -70. }, + max: Coordinate { x: 50., y: 80. }, + }; + assert_eq!(bounding_rect, mpoly.bounding_rect().unwrap()); + } + #[test] + fn line_test() { + let line1 = Line::new(Coordinate { x: 0., y: 1. }, Coordinate { x: 2., y: 3. }); + let line2 = Line::new(Coordinate { x: 2., y: 3. }, Coordinate { x: 0., y: 1. }); + assert_eq!( + line1.bounding_rect(), + Rect { + min: Coordinate { x: 0., y: 1. }, + max: Coordinate { x: 2., y: 3. }, + } + ); + assert_eq!( + line2.bounding_rect(), + Rect { + min: Coordinate { x: 0., y: 1. }, + max: Coordinate { x: 2., y: 3. }, + } + ); + } +} diff --git a/geo/src/algorithm/boundingbox.rs b/geo/src/algorithm/boundingbox.rs deleted file mode 100644 index 1cd278587..000000000 --- a/geo/src/algorithm/boundingbox.rs +++ /dev/null @@ -1,297 +0,0 @@ -use { - Bbox, Coordinate, CoordinateType, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, - Polygon, Triangle, -}; - -/// Calculation of the bounding box of a geometry. - -pub trait BoundingBox { - type Output; - - /// Return the Bounding Box of a geometry - /// - /// # Examples - /// - /// ``` - /// use geo::{Point, LineString}; - /// use geo::algorithm::boundingbox::BoundingBox; - /// - /// let mut vec = Vec::new(); - /// vec.push(Point::new(40.02f64, 116.34)); - /// vec.push(Point::new(42.02f64, 116.34)); - /// vec.push(Point::new(42.02f64, 118.34)); - /// let linestring = LineString::from(vec); - /// let bbox = linestring.bbox().unwrap(); - /// - /// assert_eq!(40.02f64, bbox.xmin); - /// assert_eq!(42.02f64, bbox.xmax); - /// assert_eq!(116.34, bbox.ymin); - /// assert_eq!(118.34, bbox.ymax); - /// ``` - /// - fn bbox(&self) -> Self::Output; -} - -fn get_min_max(p: T, min: T, max: T) -> (T, T) -where - T: CoordinateType, -{ - if p > max { - (min, p) - } else if p < min { - (p, max) - } else { - (min, max) - } -} - -fn get_bbox(collection: I) -> Option> -where - T: CoordinateType, - I: IntoIterator>, -{ - let mut iter = collection.into_iter(); - if let Some(pnt) = iter.next() { - let mut xrange = (pnt.x, pnt.x); - let mut yrange = (pnt.y, pnt.y); - for pnt in iter { - let (px, py) = pnt.x_y(); - xrange = get_min_max(px, xrange.0, xrange.1); - yrange = get_min_max(py, yrange.0, yrange.1); - } - return Some(Bbox { - xmin: xrange.0, - xmax: xrange.1, - ymin: yrange.0, - ymax: yrange.1, - }); - } - None -} - -impl BoundingBox for MultiPoint -where - T: CoordinateType, -{ - type Output = Option>; - - /// - /// Return the BoundingBox for a MultiPoint - /// - fn bbox(&self) -> Self::Output { - get_bbox(self.0.iter().map(|p| p.0)) - } -} - -impl BoundingBox for Line -where - T: CoordinateType, -{ - type Output = Bbox; - - fn bbox(&self) -> Self::Output { - let a = self.start; - let b = self.end; - let (xmin, xmax) = if a.x <= b.x { (a.x, b.x) } else { (b.x, a.x) }; - let (ymin, ymax) = if a.y <= b.y { (a.y, b.y) } else { (b.y, a.y) }; - Bbox { - xmin, - xmax, - ymin, - ymax, - } - } -} - -impl BoundingBox for LineString -where - T: CoordinateType, -{ - type Output = Option>; - - /// - /// Return the BoundingBox for a LineString - /// - fn bbox(&self) -> Self::Output { - get_bbox(self.0.iter().cloned()) - } -} - -impl BoundingBox for MultiLineString -where - T: CoordinateType, -{ - type Output = Option>; - - /// - /// Return the BoundingBox for a MultiLineString - /// - fn bbox(&self) -> Self::Output { - get_bbox(self.0.iter().flat_map(|line| line.0.iter().map(|c| *c))) - } -} - -impl BoundingBox for Polygon -where - T: CoordinateType, -{ - type Output = Option>; - - /// - /// Return the BoundingBox for a Polygon - /// - fn bbox(&self) -> Self::Output { - let line = &self.exterior; - get_bbox(line.0.iter().cloned()) - } -} - -impl BoundingBox for MultiPolygon -where - T: CoordinateType, -{ - type Output = Option>; - - /// - /// Return the BoundingBox for a MultiPolygon - /// - fn bbox(&self) -> Self::Output { - get_bbox( - self.0 - .iter() - .flat_map(|poly| (poly.exterior).0.iter().map(|c| *c)), - ) - } -} - -impl BoundingBox for Triangle -where - T: CoordinateType, -{ - type Output = Bbox; - - fn bbox(&self) -> Self::Output { - get_bbox(self.to_array().into_iter().cloned()).unwrap() - } -} - -#[cfg(test)] -mod test { - use algorithm::boundingbox::BoundingBox; - use { - Bbox, Coordinate, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, - Polygon, - }; - - #[test] - fn empty_linestring_test() { - let linestring: LineString = LineString(vec![]); - let bbox = linestring.bbox(); - assert!(bbox.is_none()); - } - #[test] - fn linestring_one_point_test() { - let vec = vec![(40.02f64, 116.34)]; - let linestring = LineString::from(vec); - let bbox = Bbox { - xmin: 40.02f64, - ymax: 116.34, - xmax: 40.02, - ymin: 116.34, - }; - assert_eq!(bbox, linestring.bbox().unwrap()); - } - #[test] - fn linestring_test() { - let linestring = LineString::from(vec![(1., 1.), (2., -2.), (-3., -3.), (-4., 4.)]); - let bbox = Bbox { - xmin: -4., - ymax: 4., - xmax: 2., - ymin: -3., - }; - assert_eq!(bbox, linestring.bbox().unwrap()); - } - #[test] - fn multilinestring_test() { - let multiline = MultiLineString(vec![ - LineString::from(vec![(1., 1.), (-40., 1.)]), - LineString::from(vec![(1., 1.), (50., 1.)]), - LineString::from(vec![(1., 1.), (1., -60.)]), - LineString::from(vec![(1., 1.), (1., 70.)]), - ]); - let bbox = Bbox { - xmin: -40., - ymax: 70., - xmax: 50., - ymin: -60., - }; - assert_eq!(bbox, multiline.bbox().unwrap()); - } - #[test] - fn multipoint_test() { - let p = |x, y| Point(Coordinate { x: x, y: y }); - let multipoint = MultiPoint(vec![p(1., 1.), p(2., -2.), p(-3., -3.), p(-4., 4.)]); - let bbox = Bbox { - xmin: -4., - ymax: 4., - xmax: 2., - ymin: -3., - }; - assert_eq!(bbox, multipoint.bbox().unwrap()); - } - #[test] - fn polygon_test() { - let linestring = LineString::from(vec![(0., 0.), (5., 0.), (5., 6.), (0., 6.), (0., 0.)]); - let line_bbox = linestring.bbox().unwrap(); - let poly = Polygon::new(linestring, Vec::new()); - assert_eq!(line_bbox, poly.bbox().unwrap()); - } - #[test] - fn multipolygon_test() { - let mpoly = MultiPolygon(vec![ - Polygon::new( - LineString::from(vec![(0., 0.), (50., 0.), (0., -70.), (0., 0.)]), - Vec::new(), - ), - Polygon::new( - LineString::from(vec![(0., 0.), (5., 0.), (0., 80.), (0., 0.)]), - Vec::new(), - ), - Polygon::new( - LineString::from(vec![(0., 0.), (-60., 0.), (0., 6.), (0., 0.)]), - Vec::new(), - ), - ]); - let bbox = Bbox { - xmin: -60., - ymax: 80., - xmax: 50., - ymin: -70., - }; - assert_eq!(bbox, mpoly.bbox().unwrap()); - } - #[test] - fn line_test() { - let line1 = Line::new(Coordinate { x: 0., y: 1. }, Coordinate { x: 2., y: 3. }); - let line2 = Line::new(Coordinate { x: 2., y: 3. }, Coordinate { x: 0., y: 1. }); - assert_eq!( - line1.bbox(), - Bbox { - xmin: 0., - xmax: 2., - ymin: 1., - ymax: 3., - } - ); - assert_eq!( - line2.bbox(), - Bbox { - xmin: 0., - xmax: 2., - ymin: 1., - ymax: 3., - } - ); - } -} diff --git a/geo/src/algorithm/centroid.rs b/geo/src/algorithm/centroid.rs index cc79bb5be..e91e30d93 100644 --- a/geo/src/algorithm/centroid.rs +++ b/geo/src/algorithm/centroid.rs @@ -3,7 +3,7 @@ use std::iter::Sum; use algorithm::area::Area; use algorithm::euclidean_length::EuclideanLength; -use {Bbox, Line, LineString, MultiPolygon, Point, Polygon}; +use {Line, LineString, MultiPolygon, Point, Polygon, Rect}; /// Calculation of the centroid. /// The centroid is the arithmetic mean position of all points in the shape. @@ -190,7 +190,7 @@ where } } -impl Centroid for Bbox +impl Centroid for Rect where T: Float, { @@ -198,7 +198,10 @@ where fn centroid(&self) -> Self::Output { let two = T::one() + T::one(); - Point::new((self.xmax + self.xmin) / two, (self.ymax + self.ymin) / two) + Point::new( + (self.max.x + self.min.x) / two, + (self.max.y + self.min.y) / two, + ) } } @@ -217,7 +220,7 @@ where mod test { use algorithm::centroid::Centroid; use algorithm::euclidean_distance::EuclideanDistance; - use {Bbox, Coordinate, Line, LineString, MultiPolygon, Point, Polygon, COORD_PRECISION}; + use {Coordinate, Line, LineString, MultiPolygon, Point, Polygon, Rect, COORD_PRECISION}; // Tests: Centroid of LineString #[test] fn empty_linestring_test() { @@ -361,15 +364,13 @@ mod test { ); } #[test] - fn bbox_test() { - let bbox = Bbox { - xmax: 4., - xmin: 0., - ymax: 100., - ymin: 50., + fn bounding_rect_test() { + let bounding_rect = Rect { + min: Coordinate { x: 0., y: 50. }, + max: Coordinate { x: 4., y: 100. }, }; let point = Point(Coordinate { x: 2., y: 75. }); - assert_eq!(point, bbox.centroid()); + assert_eq!(point, bounding_rect.centroid()); } #[test] fn line_test() { diff --git a/geo/src/algorithm/contains.rs b/geo/src/algorithm/contains.rs index b710310e7..d20cae3c1 100644 --- a/geo/src/algorithm/contains.rs +++ b/geo/src/algorithm/contains.rs @@ -2,7 +2,7 @@ use num_traits::{Float, ToPrimitive}; use algorithm::euclidean_distance::EuclideanDistance; use algorithm::intersects::Intersects; -use {Bbox, CoordinateType, Line, LineString, MultiPolygon, Point, Polygon, COORD_PRECISION}; +use {CoordinateType, Line, LineString, MultiPolygon, Point, Polygon, Rect, COORD_PRECISION}; /// Checks if the geometry A is completely inside the B geometry pub trait Contains { @@ -253,32 +253,32 @@ where } } -impl Contains> for Bbox +impl Contains> for Rect where T: CoordinateType, { fn contains(&self, p: &Point) -> bool { - p.x() >= self.xmin && p.x() <= self.xmax && p.y() >= self.ymin && p.y() <= self.ymax + p.x() >= self.min.x && p.x() <= self.max.x && p.y() >= self.min.y && p.y() <= self.max.y } } -impl Contains> for Bbox +impl Contains> for Rect where T: CoordinateType, { - fn contains(&self, bbox: &Bbox) -> bool { + fn contains(&self, bounding_rect: &Rect) -> bool { // All points of LineString must be in the polygon ? - self.xmin <= bbox.xmin - && self.xmax >= bbox.xmax - && self.ymin <= bbox.ymin - && self.ymax >= bbox.ymax + self.min.x <= bounding_rect.min.x + && self.max.x >= bounding_rect.max.x + && self.min.y <= bounding_rect.min.y + && self.max.y >= bounding_rect.max.y } } #[cfg(test)] mod test { use algorithm::contains::Contains; - use {Bbox, Coordinate, Line, LineString, MultiPolygon, Point, Polygon}; + use {Coordinate, Line, LineString, MultiPolygon, Point, Polygon, Rect}; #[test] // V doesn't contain rect because two of its edges intersect with V's exterior boundary fn polygon_does_not_contain_polygon() { @@ -501,21 +501,17 @@ mod test { assert!(!poly.contains(&LineString::from(vec![(3., 0.5), (3., 5.)]))); } #[test] - fn bbox_in_inner_bbox_test() { - let bbox_xl = Bbox { - xmin: -100., - xmax: 100., - ymin: -200., - ymax: 200., + fn bounding_rect_in_inner_bounding_rect_test() { + let bounding_rect_xl = Rect { + min: Coordinate { x: -100., y: -200. }, + max: Coordinate { x: 100., y: 200. }, }; - let bbox_sm = Bbox { - xmin: -10., - xmax: 10., - ymin: -20., - ymax: 20., + let bounding_rect_sm = Rect { + min: Coordinate { x: -10., y: -20. }, + max: Coordinate { x: 10., y: 20. }, }; - assert_eq!(true, bbox_xl.contains(&bbox_sm)); - assert_eq!(false, bbox_sm.contains(&bbox_xl)); + assert_eq!(true, bounding_rect_xl.contains(&bounding_rect_sm)); + assert_eq!(false, bounding_rect_sm.contains(&bounding_rect_xl)); } #[test] fn point_in_line_test() { @@ -606,23 +602,19 @@ mod test { } #[test] - fn integer_bboxs() { + fn integer_bounding_rects() { let p: Point = Point::new(10, 20); - let bbox: Bbox = Bbox { - xmin: 0, - ymin: 0, - xmax: 100, - ymax: 100, + let bounding_rect: Rect = Rect { + min: Coordinate { x: 0, y: 0 }, + max: Coordinate { x: 100, y: 100 }, }; - assert!(bbox.contains(&p)); - assert!(!bbox.contains(&Point::new(-10, -10))); + assert!(bounding_rect.contains(&p)); + assert!(!bounding_rect.contains(&Point::new(-10, -10))); - let smaller_bbox: Bbox = Bbox { - xmin: 10, - ymin: 10, - xmax: 20, - ymax: 20, + let smaller_bounding_rect: Rect = Rect { + min: Coordinate { x: 10, y: 10 }, + max: Coordinate { x: 20, y: 20 }, }; - assert!(bbox.contains(&smaller_bbox)); + assert!(bounding_rect.contains(&smaller_bounding_rect)); } } diff --git a/geo/src/algorithm/intersects.rs b/geo/src/algorithm/intersects.rs index b1e4c2e07..b94dbdf60 100644 --- a/geo/src/algorithm/intersects.rs +++ b/geo/src/algorithm/intersects.rs @@ -1,6 +1,6 @@ use algorithm::contains::Contains; use num_traits::Float; -use {Bbox, Line, LineString, Point, Polygon}; +use {Line, LineString, Point, Polygon, Rect}; /// Checks if the geometry A intersects the geometry B. @@ -201,24 +201,24 @@ where } } -impl Intersects> for Bbox +impl Intersects> for Rect where T: Float, { - fn intersects(&self, bbox: &Bbox) -> bool { + fn intersects(&self, bounding_rect: &Rect) -> bool { // line intersects inner or outer polygon edge - if bbox.contains(self) { + if bounding_rect.contains(self) { false } else { - (self.xmin >= bbox.xmin && self.xmin <= bbox.xmax - || self.xmax >= bbox.xmin && self.xmax <= bbox.xmax) - && (self.ymin >= bbox.ymin && self.ymin <= bbox.ymax - || self.ymax >= bbox.ymin && self.ymax <= bbox.ymax) + (self.min.x >= bounding_rect.min.x && self.min.x <= bounding_rect.max.x + || self.max.x >= bounding_rect.min.x && self.max.x <= bounding_rect.max.x) + && (self.min.y >= bounding_rect.min.y && self.min.y <= bounding_rect.max.y + || self.max.y >= bounding_rect.min.y && self.max.y <= bounding_rect.max.y) } } } -impl Intersects> for Bbox +impl Intersects> for Rect where T: Float, { @@ -227,18 +227,18 @@ where } } -impl Intersects> for Polygon +impl Intersects> for Polygon where T: Float, { - fn intersects(&self, bbox: &Bbox) -> bool { + fn intersects(&self, bounding_rect: &Rect) -> bool { let p = Polygon::new( LineString::from(vec![ - (bbox.xmin, bbox.ymin), - (bbox.xmin, bbox.ymax), - (bbox.xmax, bbox.ymax), - (bbox.xmax, bbox.ymin), - (bbox.xmin, bbox.ymin), + (bounding_rect.min.x, bounding_rect.min.y), + (bounding_rect.min.x, bounding_rect.max.y), + (bounding_rect.max.x, bounding_rect.max.y), + (bounding_rect.max.x, bounding_rect.min.y), + (bounding_rect.min.x, bounding_rect.min.y), ]), vec![], ); @@ -262,7 +262,7 @@ where #[cfg(test)] mod test { use algorithm::intersects::Intersects; - use {Bbox, Coordinate, Line, LineString, Point, Polygon}; + use {Coordinate, Line, LineString, Point, Polygon, Rect}; /// Tests: intersection LineString and LineString #[test] fn empty_linestring1_test() { @@ -469,7 +469,7 @@ mod test { assert!(p2.intersects(&p1)); } #[test] - fn polygon_intersects_bbox_test() { + fn polygon_intersects_bounding_rect_test() { // Polygon poly = // // (0,8) (12,8) @@ -499,29 +499,21 @@ mod test { (7., 4.), ])], ); - let b1 = Bbox { - xmin: 11.0, - xmax: 13.0, - ymin: 1.0, - ymax: 2.0, + let b1 = Rect { + min: Coordinate { x: 11., y: 1. }, + max: Coordinate { x: 13., y: 2. }, }; - let b2 = Bbox { - xmin: 2.0, - xmax: 8.0, - ymin: 2.0, - ymax: 5.0, + let b2 = Rect { + min: Coordinate { x: 2., y: 2. }, + max: Coordinate { x: 8., y: 5. }, }; - let b3 = Bbox { - xmin: 8.0, - xmax: 10.0, - ymin: 5.0, - ymax: 6.0, + let b3 = Rect { + min: Coordinate { x: 8., y: 5. }, + max: Coordinate { x: 10., y: 6. }, }; - let b4 = Bbox { - xmin: 1.0, - xmax: 3.0, - ymin: 1.0, - ymax: 3.0, + let b4 = Rect { + min: Coordinate { x: 1., y: 1. }, + max: Coordinate { x: 3., y: 3. }, }; // overlaps assert!(poly.intersects(&b1)); @@ -538,29 +530,23 @@ mod test { assert!(b4.intersects(&poly)); } #[test] - fn bbox_test() { - let bbox_xl = Bbox { - xmin: -100., - xmax: 100., - ymin: -200., - ymax: 200., + fn bounding_rect_test() { + let bounding_rect_xl = Rect { + min: Coordinate { x: -100., y: -200. }, + max: Coordinate { x: 100., y: 200. }, }; - let bbox_sm = Bbox { - xmin: -10., - xmax: 10., - ymin: -20., - ymax: 20., + let bounding_rect_sm = Rect { + min: Coordinate { x: -10., y: -20. }, + max: Coordinate { x: 10., y: 20. }, }; - let bbox_s2 = Bbox { - xmin: 0., - xmax: 20., - ymin: 0., - ymax: 30., + let bounding_rect_s2 = Rect { + min: Coordinate { x: 0., y: 0. }, + max: Coordinate { x: 20., y: 30. }, }; - assert_eq!(false, bbox_xl.intersects(&bbox_sm)); - assert_eq!(false, bbox_sm.intersects(&bbox_xl)); - assert_eq!(true, bbox_sm.intersects(&bbox_s2)); - assert_eq!(true, bbox_s2.intersects(&bbox_sm)); + assert_eq!(false, bounding_rect_xl.intersects(&bounding_rect_sm)); + assert_eq!(false, bounding_rect_sm.intersects(&bounding_rect_xl)); + assert_eq!(true, bounding_rect_sm.intersects(&bounding_rect_s2)); + assert_eq!(true, bounding_rect_s2.intersects(&bounding_rect_sm)); } #[test] fn point_intersects_line_test() { diff --git a/geo/src/algorithm/mod.rs b/geo/src/algorithm/mod.rs index 9340507b8..9150883af 100644 --- a/geo/src/algorithm/mod.rs +++ b/geo/src/algorithm/mod.rs @@ -2,8 +2,8 @@ pub mod area; /// Returns the bearing to another Point in degrees. pub mod bearing; -/// Returns the Bbox of a geometry. -pub mod boundingbox; +/// Returns the bounding rectangle of a geometry. +pub mod bounding_rect; /// Calculation of the centroid of a geometry. pub mod centroid; /// Determine the minimum distance between two objects. diff --git a/geo/src/algorithm/simplifyvw.rs b/geo/src/algorithm/simplifyvw.rs index 443cda1c5..805e7105c 100644 --- a/geo/src/algorithm/simplifyvw.rs +++ b/geo/src/algorithm/simplifyvw.rs @@ -5,8 +5,7 @@ use std::collections::BinaryHeap; use {Coordinate, Line, LineString, MultiLineString, MultiPolygon, Point, Polygon, Triangle}; use spade::rtree::RTree; -use spade::BoundingRect; -use spade::SpadeFloat; +use spade::{self, SpadeFloat}; /// Store triangle information // current is the candidate point for removal @@ -167,7 +166,8 @@ where orig.0[ai as usize], orig.0[current_point as usize], orig.0[bi as usize], - ).area().abs(); + ).area() + .abs(); pq.push(VScore { area: area, current: current_point as usize, @@ -375,14 +375,14 @@ where { let point_a = orig[triangle.left]; let point_c = orig[triangle.right]; - let bbox = Triangle( + let bounding_rect = Triangle( orig[triangle.left], orig[triangle.current], orig[triangle.right], - ).bbox(); - let br = Point::new(bbox.xmin, bbox.ymin); - let tl = Point::new(bbox.xmax, bbox.ymax); - let candidates = tree.lookup_in_rectangle(&BoundingRect::from_corners(&br, &tl)); + ).bounding_rect(); + let br = Point::new(bounding_rect.min.x, bounding_rect.min.y); + let tl = Point::new(bounding_rect.max.x, bounding_rect.max.y); + let candidates = tree.lookup_in_rectangle(&spade::BoundingRect::from_corners(&br, &tl)); candidates.iter().any(|c| { // triangle start point, end point let (ca, cb) = c.points(); diff --git a/geo/src/lib.rs b/geo/src/lib.rs index e421fa270..6144a344b 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -17,7 +17,7 @@ pub use types::*; pub use geo_types::{ Coordinate, CoordinateType, Geometry, GeometryCollection, Line, LineString, MultiLineString, - MultiPoint, MultiPolygon, Point, Polygon, Triangle, + MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle, }; /// This module includes all the functions of geometric calculations @@ -47,7 +47,7 @@ const EARTH_FLATTENING: f64 = pub mod prelude { pub use algorithm::area::Area; pub use algorithm::bearing::Bearing; - pub use algorithm::boundingbox::BoundingBox; + pub use algorithm::bounding_rect::BoundingRect; pub use algorithm::centroid::Centroid; pub use algorithm::closest_point::ClosestPoint; pub use algorithm::contains::Contains; diff --git a/geo/src/types.rs b/geo/src/types.rs index 447fb3e2a..e9c2a50a8 100644 --- a/geo/src/types.rs +++ b/geo/src/types.rs @@ -1,24 +1,8 @@ -use std::ops::Add; -use std::ops::AddAssign; - -use num_traits::{Float, ToPrimitive}; +use num_traits::Float; use {CoordinateType, Point}; pub static COORD_PRECISION: f32 = 1e-1; // 0.1m -/// A container for the bounding box of a [`Geometry`](enum.Geometry.html) -#[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))] -#[derive(PartialEq, Clone, Copy, Debug)] -pub struct Bbox -where - T: CoordinateType, -{ - pub xmin: T, - pub xmax: T, - pub ymin: T, - pub ymax: T, -} - /// A container for indices of the minimum and maximum points of a [`Geometry`](enum.Geometry.html) #[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))] #[derive(PartialEq, Clone, Copy, Debug)] @@ -53,98 +37,6 @@ where pub xmin: Point, } -impl Add for Bbox -where - T: CoordinateType + ToPrimitive, -{ - type Output = Bbox; - - /// Add a BoundingBox to the given BoundingBox. - /// - /// # Examples - /// - /// ``` - /// use geo::Bbox; - /// - /// let bbox0 = Bbox{xmin: 0., xmax: 10000., ymin: 10., ymax: 100.}; - /// let bbox1 = Bbox{xmin: 100., xmax: 1000., ymin: 100., ymax: 1000.}; - /// let bbox = bbox0 + bbox1; - /// - /// assert_eq!(0., bbox.xmin); - /// assert_eq!(10000., bbox.xmax); - /// assert_eq!(10., bbox.ymin); - /// assert_eq!(1000., bbox.ymax); - /// ``` - fn add(self, rhs: Bbox) -> Bbox { - Bbox { - xmin: if self.xmin <= rhs.xmin { - self.xmin - } else { - rhs.xmin - }, - xmax: if self.xmax >= rhs.xmax { - self.xmax - } else { - rhs.xmax - }, - ymin: if self.ymin <= rhs.ymin { - self.ymin - } else { - rhs.ymin - }, - ymax: if self.ymax >= rhs.ymax { - self.ymax - } else { - rhs.ymax - }, - } - } -} - -impl AddAssign for Bbox -where - T: CoordinateType + ToPrimitive, -{ - /// Add a BoundingBox to the given BoundingBox. - /// - /// # Examples - /// - /// ``` - /// use geo::Bbox; - /// - /// let mut bbox0 = Bbox{xmin: 0., xmax: 10000., ymin: 10., ymax: 100.}; - /// let bbox1 = Bbox{xmin: 100., xmax: 1000., ymin: 100., ymax: 1000.}; - /// bbox0 += bbox1; - /// - /// assert_eq!(0., bbox0.xmin); - /// assert_eq!(10000., bbox0.xmax); - /// assert_eq!(10., bbox0.ymin); - /// assert_eq!(1000., bbox0.ymax); - /// ``` - fn add_assign(&mut self, rhs: Bbox) { - self.xmin = if self.xmin <= rhs.xmin { - self.xmin - } else { - rhs.xmin - }; - self.xmax = if self.xmax >= rhs.xmax { - self.xmax - } else { - rhs.xmax - }; - self.ymin = if self.ymin <= rhs.ymin { - self.ymin - } else { - rhs.ymin - }; - self.ymax = if self.ymax >= rhs.ymax { - self.ymax - } else { - rhs.ymax - }; - } -} - /// The result of trying to find the closest spot on an object to a point. #[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq)]