From 7c65977380454ad7fe213348c49b1d91fa56aa08 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Thu, 14 Jul 2016 07:34:27 +0200 Subject: [PATCH 01/18] Implement BoundingBox for LineString --- src/algorithm/boundingbox.rs | 101 +++++++++++++++++++++++++++++++++++ src/algorithm/mod.rs | 2 + src/types.rs | 10 ++++ 3 files changed, 113 insertions(+) create mode 100644 src/algorithm/boundingbox.rs diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs new file mode 100644 index 000000000..4778ccdd8 --- /dev/null +++ b/src/algorithm/boundingbox.rs @@ -0,0 +1,101 @@ +use num::{Float}; + +use types::{Bbox, LineString}; + +/// Calculation of the bounding box of a geometry. + +pub trait BoundingBox { + /// Return a Bounding Box of a geometry + /// + /// ``` + /// use geo::{Point, LineString, Coordinate}; + /// 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(vec); + /// let bbox = linestring.bbox().unwrap(); + /// + /// println!("Bbox top left coordinates: {}, {}", bbox.xmin, bbox.ymax); + /// println!("Bbox bottom right coordinates: {}, {}", bbox.xmax, bbox.ymin); + /// ``` + /// + fn bbox(&self) -> Option>; +} + +fn get_bbox(line: &LineString) -> Option> + where T: Float +{ + let vect = &line.0; + if vect.is_empty() { + return None; + } + if vect.len() == 1 { + return Some(Bbox{xmin: vect[0].x(), ymax: vect[0].y(), + xmax: vect[0].x(), ymin: vect[0].y()}) + } else { + let (mut xmax, mut xmin) = (T::neg_infinity(), T::infinity()); + let (mut ymax, mut ymin) = (T::neg_infinity(), T::infinity()); + for pnt in vect.iter() { + let (px, py) = (pnt.x(), pnt.y()); + if px > xmax { + xmax = px; + } else if px < xmin { + xmin = px; + } + if py > ymax { + ymax = py; + } else if py < ymin { + ymin = py; + } + } + Some(Bbox{xmin: xmin, ymax: ymax, + xmax: xmax, ymin: ymin}) + } +} + +impl BoundingBox for LineString + where T: Float +{ + /// + /// Return the BoundingBox for a LineString + /// + fn bbox(&self) -> Option> { + get_bbox(&self) + } +} + + +#[cfg(test)] +mod test { + use types::{Point, LineString, Bbox}; + use algorithm::boundingbox::BoundingBox; + + #[test] + fn empty_linestring_test() { + let vec : Vec> = Vec::new(); + let linestring : LineString = LineString(vec); + let bbox = linestring.bbox(); + assert!(bbox.is_none()); + } + #[test] + fn linestring_one_point_test() { + let p = Point::new(40.02f64, 116.34); + let mut vect : Vec> = Vec::new(); + vect.push(p); + let linestring : LineString = LineString(vect); + 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 = LineString(vec![Point::new(1., 1.), + Point::new(2., -2.), + Point::new(-3., -3.), + Point::new(-4., 4.)]); + let bbox : Bbox = Bbox{xmin: -4., ymax: 4., xmax: 2., ymin: -3.}; + assert_eq!(bbox, linestring.bbox().unwrap()); + } +} diff --git a/src/algorithm/mod.rs b/src/algorithm/mod.rs index f825937a5..9778c08b5 100644 --- a/src/algorithm/mod.rs +++ b/src/algorithm/mod.rs @@ -8,3 +8,5 @@ pub mod intersects; pub mod area; /// Returns the distance between two geometries. pub mod distance; +/// Returns the Bbox of a geometry. +pub mod boundingbox; diff --git a/src/types.rs b/src/types.rs index 5f79c0435..c04e458a0 100644 --- a/src/types.rs +++ b/src/types.rs @@ -15,6 +15,16 @@ pub struct Coordinate pub y: T, } +#[derive(PartialEq, Clone, Copy, Debug)] +pub struct Bbox + where T: Float +{ + pub xmin: T, + pub xmax: T, + pub ymin: T, + pub ymax: T, +} + #[derive(PartialEq, Clone, Copy, Debug)] pub struct Point (pub Coordinate) where T: Float; From 0337873e3e93a9fa90cbf77da403f0e4c534d11e Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Thu, 14 Jul 2016 07:43:25 +0200 Subject: [PATCH 02/18] Implement BoundingBox for Polygon --- src/algorithm/boundingbox.rs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 4778ccdd8..972dd8a97 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -1,6 +1,6 @@ use num::{Float}; -use types::{Bbox, LineString}; +use types::{Bbox, LineString, Polygon}; /// Calculation of the bounding box of a geometry. @@ -67,10 +67,22 @@ impl BoundingBox for LineString } } +impl BoundingBox for Polygon + where T: Float +{ + /// + /// Return the BoundingBox for a Polygon + /// + fn bbox(&self) -> Option> { + let line = &self.0; + get_bbox(&line) + } +} + #[cfg(test)] mod test { - use types::{Point, LineString, Bbox}; + use types::{Bbox, Coordinate, Point, LineString, Polygon}; use algorithm::boundingbox::BoundingBox; #[test] @@ -98,4 +110,12 @@ mod test { let bbox : Bbox = Bbox{xmin: -4., ymax: 4., xmax: 2., ymin: -3.}; assert_eq!(bbox, linestring.bbox().unwrap()); } + #[test] + fn polygon_test(){ + let p = |x, y| Point(Coordinate { x: x, y: y }); + let linestring = LineString(vec![p(0., 0.), p(5., 0.), p(5., 6.), p(0., 6.), p(0., 0.)]); + let line_bbox = linestring.bbox().unwrap(); + let poly = Polygon(linestring, Vec::new()); + assert_eq!(line_bbox, poly.bbox().unwrap()); + } } From eccc0c99097790a85b60577ec5a0435173b428e1 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Thu, 14 Jul 2016 07:49:30 +0200 Subject: [PATCH 03/18] Substitute &LineString with &Vec> in get_bbox function --- src/algorithm/boundingbox.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 972dd8a97..733d27622 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -1,6 +1,6 @@ use num::{Float}; -use types::{Bbox, LineString, Polygon}; +use types::{Bbox, Point, LineString, Polygon}; /// Calculation of the bounding box of a geometry. @@ -25,10 +25,9 @@ pub trait BoundingBox { fn bbox(&self) -> Option>; } -fn get_bbox(line: &LineString) -> Option> +fn get_bbox(vect: &Vec>) -> Option> where T: Float { - let vect = &line.0; if vect.is_empty() { return None; } @@ -63,7 +62,7 @@ impl BoundingBox for LineString /// Return the BoundingBox for a LineString /// fn bbox(&self) -> Option> { - get_bbox(&self) + get_bbox(&self.0) } } @@ -75,7 +74,7 @@ impl BoundingBox for Polygon /// fn bbox(&self) -> Option> { let line = &self.0; - get_bbox(&line) + get_bbox(&line.0) } } From b65d3c03a85dcab5d6fcc37f4a681ca9dbc5694e Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Thu, 14 Jul 2016 07:55:21 +0200 Subject: [PATCH 04/18] Implement BoundingBox for MultiPoint --- src/algorithm/boundingbox.rs | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 733d27622..0eae24114 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -1,6 +1,6 @@ use num::{Float}; -use types::{Bbox, Point, LineString, Polygon}; +use types::{Bbox, Point, MultiPoint, LineString, Polygon}; /// Calculation of the bounding box of a geometry. @@ -55,6 +55,17 @@ fn get_bbox(vect: &Vec>) -> Option> } } +impl BoundingBox for MultiPoint + where T: Float +{ + /// + /// Return the BoundingBox for a MultiPoint + /// + fn bbox(&self) -> Option> { + get_bbox(&self.0) + } +} + impl BoundingBox for LineString where T: Float { @@ -79,9 +90,11 @@ impl BoundingBox for Polygon } + + #[cfg(test)] mod test { - use types::{Bbox, Coordinate, Point, LineString, Polygon}; + use types::{Bbox, Coordinate, Point, MultiPoint, LineString, Polygon}; use algorithm::boundingbox::BoundingBox; #[test] @@ -110,6 +123,15 @@ mod test { assert_eq!(bbox, linestring.bbox().unwrap()); } #[test] + fn multipoint_test() { + let multipoint : MultiPoint = MultiPoint(vec![Point::new(1., 1.), + Point::new(2., -2.), + Point::new(-3., -3.), + Point::new(-4., 4.)]); + let bbox : Bbox = Bbox{xmin: -4., ymax: 4., xmax: 2., ymin: -3.}; + assert_eq!(bbox, multipoint.bbox().unwrap()); + } + #[test] fn polygon_test(){ let p = |x, y| Point(Coordinate { x: x, y: y }); let linestring = LineString(vec![p(0., 0.), p(5., 0.), p(5., 6.), p(0., 6.), p(0., 0.)]); From 74a316de26d2b97c464e61ddada9116d331e1ad5 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Thu, 14 Jul 2016 08:04:31 +0200 Subject: [PATCH 05/18] Cleanup test syntax when creating new instances --- src/algorithm/boundingbox.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 0eae24114..2e3ea3ed9 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -99,36 +99,36 @@ mod test { #[test] fn empty_linestring_test() { - let vec : Vec> = Vec::new(); - let linestring : LineString = LineString(vec); + let vect = Vec::>::new(); + let linestring = LineString(vect); let bbox = linestring.bbox(); assert!(bbox.is_none()); } #[test] fn linestring_one_point_test() { let p = Point::new(40.02f64, 116.34); - let mut vect : Vec> = Vec::new(); + let mut vect = Vec::>::new(); vect.push(p); - let linestring : LineString = LineString(vect); + let linestring = LineString(vect); 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 = LineString(vec![Point::new(1., 1.), - Point::new(2., -2.), - Point::new(-3., -3.), - Point::new(-4., 4.)]); - let bbox : Bbox = Bbox{xmin: -4., ymax: 4., xmax: 2., ymin: -3.}; + let linestring = LineString(vec![Point::new(1., 1.), + Point::new(2., -2.), + Point::new(-3., -3.), + Point::new(-4., 4.)]); + let bbox = Bbox{xmin: -4., ymax: 4., xmax: 2., ymin: -3.}; assert_eq!(bbox, linestring.bbox().unwrap()); } #[test] fn multipoint_test() { - let multipoint : MultiPoint = MultiPoint(vec![Point::new(1., 1.), - Point::new(2., -2.), - Point::new(-3., -3.), - Point::new(-4., 4.)]); - let bbox : Bbox = Bbox{xmin: -4., ymax: 4., xmax: 2., ymin: -3.}; + let multipoint = MultiPoint(vec![Point::new(1., 1.), + Point::new(2., -2.), + Point::new(-3., -3.), + Point::new(-4., 4.)]); + let bbox = Bbox{xmin: -4., ymax: 4., xmax: 2., ymin: -3.}; assert_eq!(bbox, multipoint.bbox().unwrap()); } #[test] From 49b61726cd9b25ab71688b1b184bf8d1e3220477 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Thu, 14 Jul 2016 08:28:32 +0200 Subject: [PATCH 06/18] Implement the Add trait to Bbox --- src/types.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/types.rs b/src/types.rs index c04e458a0..777b13a36 100644 --- a/src/types.rs +++ b/src/types.rs @@ -231,6 +231,36 @@ impl Sub for Point } } +impl Add for Bbox + where T: Float + ToPrimitive +{ + type Output = Bbox; + + /// Add a boundingox to the given boundingbox. + /// + /// ``` + /// 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}, + } + } +} + + #[derive(PartialEq, Clone, Debug)] pub struct MultiPoint(pub Vec>) where T: Float; From ce28cca6e61ab0643cdd2721382ae63c536c7c71 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Fri, 15 Jul 2016 07:39:54 +0200 Subject: [PATCH 07/18] Implement AddAssign trait for Bbox struct --- src/types.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/types.rs b/src/types.rs index 777b13a36..bbb421187 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,5 @@ use std::ops::Add; +use std::ops::AddAssign; use std::ops::Neg; use std::ops::Sub; @@ -260,6 +261,31 @@ impl Add for Bbox } } +impl AddAssign for Bbox + where T: Float + ToPrimitive +{ + /// Add a boundingox to the given boundingbox. + /// + /// ``` + /// 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}; + } +} + #[derive(PartialEq, Clone, Debug)] pub struct MultiPoint(pub Vec>) where T: Float; From 2134a69894a4c9d23669c502bb977c39a89a5a39 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Fri, 15 Jul 2016 07:41:14 +0200 Subject: [PATCH 08/18] Implement BoundingBox for MultiLineString and cleanup tests --- src/algorithm/boundingbox.rs | 54 +++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 10 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 2e3ea3ed9..09580e6c6 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -1,6 +1,6 @@ use num::{Float}; -use types::{Bbox, Point, MultiPoint, LineString, Polygon}; +use types::{Bbox, Point, MultiPoint, LineString, MultiLineString, Polygon}; /// Calculation of the bounding box of a geometry. @@ -77,6 +77,34 @@ impl BoundingBox for LineString } } +impl BoundingBox for MultiLineString + where T: Float +{ + /// + /// Return the BoundingBox for a MultiLineString + /// + fn bbox(&self) -> Option> { + let vect = &self.0; + if vect.is_empty() { + return None; + } + if vect.len() == 1 { + return vect[0].bbox() + } else { + let mut bbox = vect[0].bbox().unwrap(); + for geo in vect[1..].iter() { + let gopt = geo.bbox(); + if gopt.is_some() { + bbox += gopt.unwrap(); + } + } + Some(bbox) + // vect[1..].iter() + // .fold(bbox, |bbfinal, (geo0, geo1)| bbfinal + geo0.bbox().unwrap() + geo1.bbox().unwrap()); + } + } +} + impl BoundingBox for Polygon where T: Float { @@ -94,7 +122,7 @@ impl BoundingBox for Polygon #[cfg(test)] mod test { - use types::{Bbox, Coordinate, Point, MultiPoint, LineString, Polygon}; + use types::{Bbox, Coordinate, Point, MultiPoint, LineString, MultiLineString, Polygon}; use algorithm::boundingbox::BoundingBox; #[test] @@ -115,19 +143,25 @@ mod test { } #[test] fn linestring_test() { - let linestring = LineString(vec![Point::new(1., 1.), - Point::new(2., -2.), - Point::new(-3., -3.), - Point::new(-4., 4.)]); + let p = |x, y| Point(Coordinate { x: x, y: y }); + let linestring = LineString(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, linestring.bbox().unwrap()); } #[test] + fn multilinestring_test() { + let p = |x, y| Point(Coordinate { x: x, y: y }); + let multiline = MultiLineString(vec![LineString(vec![p(1., 1.), p(-40., 1.)]), + LineString(vec![p(1., 1.), p(50., 1.)]), + LineString(vec![p(1., 1.), p(1., -60.)]), + LineString(vec![p(1., 1.), p(1., 70.)])]); + let bbox = Bbox{xmin: -40., ymax: 70., xmax: 50., ymin: -60.}; + assert_eq!(bbox, multiline.bbox().unwrap()); + } + #[test] fn multipoint_test() { - let multipoint = MultiPoint(vec![Point::new(1., 1.), - Point::new(2., -2.), - Point::new(-3., -3.), - Point::new(-4., 4.)]); + 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()); } From de27c9e836afae0b048849161a2fcdc374c01f20 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Fri, 15 Jul 2016 08:16:55 +0200 Subject: [PATCH 09/18] Implement BoundingBox for MultiPolygon --- src/algorithm/boundingbox.rs | 41 ++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 09580e6c6..8bf9f5abc 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -1,6 +1,6 @@ use num::{Float}; -use types::{Bbox, Point, MultiPoint, LineString, MultiLineString, Polygon}; +use types::{Bbox, Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon}; /// Calculation of the bounding box of a geometry. @@ -99,8 +99,6 @@ impl BoundingBox for MultiLineString } } Some(bbox) - // vect[1..].iter() - // .fold(bbox, |bbfinal, (geo0, geo1)| bbfinal + geo0.bbox().unwrap() + geo1.bbox().unwrap()); } } } @@ -117,12 +115,37 @@ impl BoundingBox for Polygon } } +impl BoundingBox for MultiPolygon + where T: Float +{ + /// + /// Return the BoundingBox for a MultiLineString + /// + fn bbox(&self) -> Option> { + let vect = &self.0; + if vect.is_empty() { + return None; + } + if vect.len() == 1 { + return vect[0].bbox() + } else { + let mut bbox = vect[0].bbox().unwrap(); + for geo in vect[1..].iter() { + let gopt = geo.bbox(); + if gopt.is_some() { + bbox += gopt.unwrap(); + } + } + Some(bbox) + } + } +} #[cfg(test)] mod test { - use types::{Bbox, Coordinate, Point, MultiPoint, LineString, MultiLineString, Polygon}; + use types::{Bbox, Coordinate, Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon}; use algorithm::boundingbox::BoundingBox; #[test] @@ -173,4 +196,14 @@ mod test { let poly = Polygon(linestring, Vec::new()); assert_eq!(line_bbox, poly.bbox().unwrap()); } + #[test] + fn multipolygon_test(){ + let p = |x, y| Point(Coordinate { x: x, y: y }); + let mpoly = MultiPolygon(vec![Polygon(LineString(vec![p(0., 0.), p(50., 0.), p(0., -70.), p(0., 0.)]), Vec::new()), + Polygon(LineString(vec![p(0., 0.), p(5., 0.), p(0., 80.), p(0., 0.)]), Vec::new()), + Polygon(LineString(vec![p(0., 0.), p(-60., 0.), p(0., 6.), p(0., 0.)]), Vec::new()), + ]); + let bbox = Bbox{xmin: -60., ymax: 80., xmax: 50., ymin: -70.}; + assert_eq!(bbox, mpoly.bbox().unwrap()); + } } From ac69af55fc2c70ebb31c697e4862a935c3af7dc4 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Fri, 15 Jul 2016 08:27:11 +0200 Subject: [PATCH 10/18] Implement Centroid for Bbox --- src/algorithm/centroid.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/algorithm/centroid.rs b/src/algorithm/centroid.rs index 936b3c9cf..68c7686c9 100644 --- a/src/algorithm/centroid.rs +++ b/src/algorithm/centroid.rs @@ -1,6 +1,6 @@ use num::{Float, FromPrimitive}; -use types::{Point, LineString, Polygon, MultiPolygon}; +use types::{Point, LineString, Polygon, MultiPolygon, Bbox}; use algorithm::area::Area; use algorithm::distance::Distance; @@ -109,9 +109,21 @@ impl Centroid for MultiPolygon } } +impl Centroid for Bbox + where T: Float +{ + /// + /// Centroid on a Bbox. + /// + fn centroid(&self) -> Option> { + let two = T::one() + T::one(); + Some(Point::new((self.xmax + self.xmin)/two, (self.ymax + self.ymin)/two)) + } +} + #[cfg(test)] mod test { - use types::{COORD_PRECISION, Coordinate, Point, LineString, Polygon, MultiPolygon}; + use types::{COORD_PRECISION, Coordinate, Point, LineString, Polygon, MultiPolygon, Bbox}; use algorithm::centroid::Centroid; use algorithm::distance::Distance; /// Tests: Centroid of LineString @@ -185,4 +197,10 @@ mod test { let dist = MultiPolygon(vec![poly1, poly2]).centroid().unwrap().distance(&p(4.07142857142857, 1.92857142857143)); assert!(dist < COORD_PRECISION); } + #[test] + fn bbox_test() { + let bbox = Bbox{ xmax: 4., xmin: 0., ymax: 100., ymin: 50.}; + let point = Point(Coordinate { x: 2., y: 75. }); + assert_eq!(point, bbox.centroid().unwrap()); + } } From 12ef2a5f194882ce31328556b18a3678e3969fe1 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Fri, 15 Jul 2016 17:44:31 +0200 Subject: [PATCH 11/18] Implement Area for Bbox --- src/algorithm/area.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/algorithm/area.rs b/src/algorithm/area.rs index ae1ae0dc6..e5b7c0510 100644 --- a/src/algorithm/area.rs +++ b/src/algorithm/area.rs @@ -1,5 +1,5 @@ use num::Float; -use types::Polygon; +use types::{Polygon, Bbox}; /// Calculation of the area. @@ -37,9 +37,17 @@ impl Area for Polygon } } +impl Area for Bbox + where T: Float +{ + fn area(&self) -> T { + (self.xmax - self.xmin) * (self.ymax - self.ymin) + } +} + #[cfg(test)] mod test { - use types::{Coordinate, Point, LineString, Polygon}; + use types::{Coordinate, Point, LineString, Polygon, Bbox}; use algorithm::area::Area; // Area of the polygon #[test] @@ -60,4 +68,9 @@ mod test { let poly = Polygon(linestring, Vec::new()); assert_eq!(poly.area(), 30.); } + #[test] + fn bbox_test() { + let bbox = Bbox {xmin: 10., xmax: 20., ymin: 30., ymax: 40.}; + assert_eq!(100., bbox.area()); + } } From 3ecca84a173a0c0a0eca1e6974b52f5985a5a791 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Sat, 16 Jul 2016 15:26:18 +0200 Subject: [PATCH 12/18] Implement trait Contains for Bbox --- src/algorithm/contains.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/algorithm/contains.rs b/src/algorithm/contains.rs index d3c460d3a..e845cff2a 100644 --- a/src/algorithm/contains.rs +++ b/src/algorithm/contains.rs @@ -1,6 +1,6 @@ use num::{Float, ToPrimitive}; -use types::{COORD_PRECISION, Point, LineString, Polygon, MultiPolygon}; +use types::{COORD_PRECISION, Point, LineString, Polygon, MultiPolygon, Bbox}; use algorithm::intersects::Intersects; use algorithm::distance::Distance; @@ -152,10 +152,19 @@ impl Contains> for Polygon } } +impl Contains> for Bbox + where T: Float +{ + fn contains(&self, bbox: &Bbox) -> 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 + } +} + #[cfg(test)] mod test { - use types::{Coordinate, Point, LineString, Polygon, MultiPolygon}; + use types::{Coordinate, Point, LineString, Polygon, MultiPolygon, Bbox}; use algorithm::contains::Contains; /// Tests: Point in LineString #[test] @@ -301,4 +310,11 @@ mod test { assert!(!poly.contains(&LineString(vec![p(2., 2.), p(2., 5.)]))); assert!(!poly.contains(&LineString(vec![p(3., 0.5), p(3., 5.)]))); } + #[test] + fn bbox_in_inner_bbox_test() { + let bbox_xl = Bbox { xmin: -100., xmax: 100., ymin: -200., ymax: 200.}; + let bbox_sm = Bbox { xmin: -10., xmax: 10., ymin: -20., ymax: 20.}; + assert_eq!(true, bbox_xl.contains(&bbox_sm)); + assert_eq!(false, bbox_sm.contains(&bbox_xl)); + } } From 6556567f884eb5f8846aea7e3ec5b6f87aab9bf0 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Sat, 16 Jul 2016 23:56:10 +0200 Subject: [PATCH 13/18] Implement Intersects trait for Bbox --- src/algorithm/intersects.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/algorithm/intersects.rs b/src/algorithm/intersects.rs index aed02ae2c..355445ea2 100644 --- a/src/algorithm/intersects.rs +++ b/src/algorithm/intersects.rs @@ -1,5 +1,5 @@ use num::Float; -use types::{LineString, Polygon}; +use types::{LineString, Polygon, Bbox}; use algorithm::contains::Contains; /// Checks if the geometry A intersects the geometry B. @@ -67,9 +67,23 @@ impl Intersects> for Polygon } } } + +impl Intersects> for Bbox + where T: Float +{ + fn intersects(&self, bbox: &Bbox) -> bool { + // line intersects inner or outer polygon edge + if bbox.contains(&self) { + return 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) + } + } +} #[cfg(test)] mod test { - use types::{Coordinate, Point, LineString, Polygon}; + use types::{Coordinate, Point, LineString, Polygon, Bbox}; use algorithm::intersects::Intersects; /// Tests: intersection LineString and LineString #[test] @@ -186,4 +200,14 @@ mod test { assert!(poly.intersects(&LineString(vec![p(4., 7.), p(6., 7.)]))); assert!(poly.intersects(&LineString(vec![p(8., 1.), p(8., 9.)]))); } + #[test] + fn bbox_test() { + let bbox_xl = Bbox { xmin: -100., xmax: 100., ymin: -200., ymax: 200.}; + let bbox_sm = Bbox { xmin: -10., xmax: 10., ymin: -20., ymax: 20.}; + let bbox_s2 = Bbox { xmin: 0., xmax: 20., ymin: 0., ymax: 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)); + } } From ab6101122a9220dca86d215d337e64abf4eea5f8 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Mon, 18 Jul 2016 18:05:43 +0200 Subject: [PATCH 14/18] Cleanup the get_bbox function to remove some duplicate code --- src/algorithm/boundingbox.rs | 38 ++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 8bf9f5abc..3c23887ff 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -1,4 +1,4 @@ -use num::{Float}; +use num::Float; use types::{Bbox, Point, MultiPoint, LineString, MultiLineString, Polygon, MultiPolygon}; @@ -25,36 +25,32 @@ pub trait BoundingBox { fn bbox(&self) -> Option>; } + +fn get_min_max(p: T, min: T, max: T) -> (T, T) + where T: Float +{ + if p > max {(min, p)} else if p < min {(p, max)} else {(min, max)} +} + fn get_bbox(vect: &Vec>) -> Option> where T: Float { if vect.is_empty() { return None; - } - if vect.len() == 1 { - return Some(Bbox{xmin: vect[0].x(), ymax: vect[0].y(), - xmax: vect[0].x(), ymin: vect[0].y()}) } else { - let (mut xmax, mut xmin) = (T::neg_infinity(), T::infinity()); - let (mut ymax, mut ymin) = (T::neg_infinity(), T::infinity()); - for pnt in vect.iter() { + let mut xrange = (vect[0].x(), vect[0].x()); + let mut yrange = (vect[0].y(), vect[0].y()); + for pnt in vect[1..].iter() { let (px, py) = (pnt.x(), pnt.y()); - if px > xmax { - xmax = px; - } else if px < xmin { - xmin = px; - } - if py > ymax { - ymax = py; - } else if py < ymin { - ymin = py; - } + xrange = get_min_max(px, xrange.0, xrange.1); + yrange = get_min_max(py, yrange.0, yrange.1); } - Some(Bbox{xmin: xmin, ymax: ymax, - xmax: xmax, ymin: ymin}) + Some(Bbox{xmin: xrange.0, xmax: xrange.1, + ymin: yrange.0, ymax: yrange.1}) } } + impl BoundingBox for MultiPoint where T: Float { @@ -119,7 +115,7 @@ impl BoundingBox for MultiPolygon where T: Float { /// - /// Return the BoundingBox for a MultiLineString + /// Return the BoundingBox for a MultiPolygon /// fn bbox(&self) -> Option> { let vect = &self.0; From c213d437b6909273d927d1e16f501418ae38f3c7 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Tue, 19 Jul 2016 07:02:56 +0200 Subject: [PATCH 15/18] Update the get_bbox function removing the else block --- src/algorithm/boundingbox.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 3c23887ff..14ca5a59d 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -37,17 +37,16 @@ fn get_bbox(vect: &Vec>) -> Option> { if vect.is_empty() { return None; - } else { - let mut xrange = (vect[0].x(), vect[0].x()); - let mut yrange = (vect[0].y(), vect[0].y()); - for pnt in vect[1..].iter() { - let (px, py) = (pnt.x(), pnt.y()); - xrange = get_min_max(px, xrange.0, xrange.1); - yrange = get_min_max(py, yrange.0, yrange.1); - } - Some(Bbox{xmin: xrange.0, xmax: xrange.1, - ymin: yrange.0, ymax: yrange.1}) } + let mut xrange = (vect[0].x(), vect[0].x()); + let mut yrange = (vect[0].y(), vect[0].y()); + for pnt in vect[1..].iter() { + let (px, py) = (pnt.x(), pnt.y()); + xrange = get_min_max(px, xrange.0, xrange.1); + yrange = get_min_max(py, yrange.0, yrange.1); + } + Some(Bbox{xmin: xrange.0, xmax: xrange.1, + ymin: yrange.0, ymax: yrange.1}) } From 5bae671a6821eb9ee6ca556cac9d38a0c9a3a3a7 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Tue, 19 Jul 2016 07:09:55 +0200 Subject: [PATCH 16/18] Substitute println! with assert_eq! in trait documentation --- src/algorithm/boundingbox.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 14ca5a59d..0794e5962 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -8,7 +8,7 @@ pub trait BoundingBox { /// Return a Bounding Box of a geometry /// /// ``` - /// use geo::{Point, LineString, Coordinate}; + /// use geo::{Point, LineString}; /// use geo::algorithm::boundingbox::BoundingBox; /// /// let mut vec = Vec::new(); @@ -18,8 +18,10 @@ pub trait BoundingBox { /// let linestring = LineString(vec); /// let bbox = linestring.bbox().unwrap(); /// - /// println!("Bbox top left coordinates: {}, {}", bbox.xmin, bbox.ymax); - /// println!("Bbox bottom right coordinates: {}, {}", bbox.xmax, bbox.ymin); + /// 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) -> Option>; From 9bec1b8e076f558fa23f82a87878a27600b73524 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Tue, 19 Jul 2016 18:06:12 +0200 Subject: [PATCH 17/18] Rewrite get_bbox function to accept as input an IntoIterator instead of a vector of Point --- src/algorithm/boundingbox.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 0794e5962..606f6a6e0 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -34,21 +34,23 @@ fn get_min_max(p: T, min: T, max: T) -> (T, T) if p > max {(min, p)} else if p < min {(p, max)} else {(min, max)} } -fn get_bbox(vect: &Vec>) -> Option> - where T: Float +fn get_bbox<'a, I, T>(collection: I) -> Option> + where T: 'a + Float, + I: 'a + IntoIterator> { - if vect.is_empty() { - return None; - } - let mut xrange = (vect[0].x(), vect[0].x()); - let mut yrange = (vect[0].y(), vect[0].y()); - for pnt in vect[1..].iter() { - let (px, py) = (pnt.x(), pnt.y()); - xrange = get_min_max(px, xrange.0, xrange.1); - yrange = get_min_max(py, yrange.0, yrange.1); + 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(), pnt.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}) } - Some(Bbox{xmin: xrange.0, xmax: xrange.1, - ymin: yrange.0, ymax: yrange.1}) + None } @@ -97,6 +99,7 @@ impl BoundingBox for MultiLineString } Some(bbox) } + // get_bbox(&self.0.iter().flat_map(|line| line.0.iter())) } } From 6f16131ee21d6a24baba3664e84c7c134b2207d2 Mon Sep 17 00:00:00 2001 From: Zambelli Pietro Date: Wed, 20 Jul 2016 06:08:34 +0200 Subject: [PATCH 18/18] Remove duplicate code in MultiLineString and MultiPolygon, credits to TeXitoi --- src/algorithm/boundingbox.rs | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/src/algorithm/boundingbox.rs b/src/algorithm/boundingbox.rs index 606f6a6e0..353429b27 100644 --- a/src/algorithm/boundingbox.rs +++ b/src/algorithm/boundingbox.rs @@ -83,23 +83,7 @@ impl BoundingBox for MultiLineString /// Return the BoundingBox for a MultiLineString /// fn bbox(&self) -> Option> { - let vect = &self.0; - if vect.is_empty() { - return None; - } - if vect.len() == 1 { - return vect[0].bbox() - } else { - let mut bbox = vect[0].bbox().unwrap(); - for geo in vect[1..].iter() { - let gopt = geo.bbox(); - if gopt.is_some() { - bbox += gopt.unwrap(); - } - } - Some(bbox) - } - // get_bbox(&self.0.iter().flat_map(|line| line.0.iter())) + get_bbox(self.0.iter().flat_map(|line| line.0.iter())) } } @@ -122,22 +106,7 @@ impl BoundingBox for MultiPolygon /// Return the BoundingBox for a MultiPolygon /// fn bbox(&self) -> Option> { - let vect = &self.0; - if vect.is_empty() { - return None; - } - if vect.len() == 1 { - return vect[0].bbox() - } else { - let mut bbox = vect[0].bbox().unwrap(); - for geo in vect[1..].iter() { - let gopt = geo.bbox(); - if gopt.is_some() { - bbox += gopt.unwrap(); - } - } - Some(bbox) - } + get_bbox(self.0.iter().flat_map(|poly| (poly.0).0.iter())) } }