Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite LineString structure to better guarantee validation. [WIP] #155

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions src/algorithm/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,14 @@ pub trait Area<T> where T: Float
/// use geo::algorithm::area::Area;
/// let p = |x, y| Point(Coordinate { x: x, y: y });
/// let v = Vec::new();
/// let linestring = LineString(vec![p(0., 0.), p(5., 0.), p(5., 6.), p(0., 6.), p(0., 0.)]);
/// let linestring = LineString::new(vec![p(0., 0.), p(5., 0.), p(5., 6.), p(0., 6.), p(0., 0.)]).unwrap();
/// let poly = Polygon::new(linestring, v);
/// assert_eq!(poly.area(), 30.);
/// ```
fn area(&self) -> T;
}

fn get_linestring_area<T>(linestring: &LineString<T>) -> T where T: Float {
if linestring.0.is_empty() || linestring.0.len() == 1 {
return T::zero();
}
let mut tmp = T::zero();
for line in linestring.lines() {
tmp = tmp + (line.start.x() * line.end.y() - line.end.x() * line.start.y());
Expand Down Expand Up @@ -72,19 +69,19 @@ mod test {
// Area of the polygon
#[test]
fn area_empty_polygon_test() {
let poly = Polygon::<f64>::new(LineString(Vec::new()), Vec::new());
let poly = Polygon::<f64>::new(LineString::new(Vec::new()), Vec::new()).unwrap();
assert_relative_eq!(poly.area(), 0.);
}

#[test]
fn area_one_point_polygon_test() {
let poly = Polygon::new(LineString(vec![Point::new(1., 0.)]), Vec::new());
let poly = Polygon::new(LineString::new(vec![Point::new(1., 0.)]), Vec::new()).unwrap();
assert_relative_eq!(poly.area(), 0.);
}
#[test]
fn area_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 linestring = LineString::new(vec![p(0., 0.), p(5., 0.), p(5., 6.), p(0., 6.), p(0., 0.)]).unwrap();
let poly = Polygon::new(linestring, Vec::new());
assert_relative_eq!(poly.area(), 30.);
}
Expand All @@ -96,22 +93,22 @@ mod test {
#[test]
fn area_polygon_inner_test() {
let p = |x, y| Point(Coordinate { x: x, y: y });
let outer = LineString(vec![p(0., 0.), p(10., 0.), p(10., 10.), p(0., 10.), p(0., 0.)]);
let inner0 = LineString(vec![p(1., 1.), p(2., 1.), p(2., 2.), p(1., 2.), p(1., 1.)]);
let inner1 = LineString(vec![p(5., 5.), p(6., 5.), p(6., 6.), p(5., 6.), p(5., 5.)]);
let outer = LineString::new(vec![p(0., 0.), p(10., 0.), p(10., 10.), p(0., 10.), p(0., 0.)]).unwrap();
let inner0 = LineString::new(vec![p(1., 1.), p(2., 1.), p(2., 2.), p(1., 2.), p(1., 1.)]).unwrap();
let inner1 = LineString::new(vec![p(5., 5.), p(6., 5.), p(6., 6.), p(5., 6.), p(5., 5.)]).unwrap();
let poly = Polygon::new(outer, vec![inner0, inner1]);
assert_relative_eq!(poly.area(), 98.);
}
#[test]
fn area_multipolygon_test() {
let p = |x, y| Point(Coordinate { x: x, y: y });
let poly0 = Polygon::new(LineString(vec![p(0., 0.), p(10., 0.), p(10., 10.), p(0., 10.),
let poly0 = Polygon::new(LineString::new(vec![p(0., 0.), p(10., 0.), p(10., 10.), p(0., 10.).unwrap(),
p(0., 0.)]),
Vec::new());
let poly1 = Polygon::new(LineString(vec![p(1., 1.), p(2., 1.), p(2., 2.), p(1., 2.),
let poly1 = Polygon::new(LineString::new(vec![p(1., 1.), p(2., 1.), p(2., 2.), p(1., 2.).unwrap(),
p(1., 1.)]),
Vec::new());
let poly2 = Polygon::new(LineString(vec![p(5., 5.), p(6., 5.), p(6., 6.), p(5., 6.),
let poly2 = Polygon::new(LineString::new(vec![p(5., 5.), p(6., 5.), p(6., 6.), p(5., 6.).unwrap(),
p(5., 5.)]),
Vec::new());
let mpoly = MultiPolygon(vec![poly0, poly1, poly2]);
Expand Down
33 changes: 17 additions & 16 deletions src/algorithm/boundingbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub trait BoundingBox<T: Float> {
/// 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 linestring = LineString::new(vec).unwrap();
/// let bbox = linestring.bbox().unwrap();
///
/// assert_eq!(40.02f64, bbox.xmin);
Expand Down Expand Up @@ -93,7 +93,8 @@ impl<T> BoundingBox<T> for LineString<T>
/// Return the BoundingBox for a LineString
///
fn bbox(&self) -> Self::Output {
get_bbox(&self.0)
// TODO this should return a Bbox
get_bbox(self.points())
}
}

Expand All @@ -106,7 +107,7 @@ impl<T> BoundingBox<T> for MultiLineString<T>
/// Return the BoundingBox for a MultiLineString
///
fn bbox(&self) -> Self::Output {
get_bbox(self.0.iter().flat_map(|line| line.0.iter()))
get_bbox(self.0.iter().flat_map(|line| line.points().iter()))
}
}

Expand All @@ -120,7 +121,7 @@ impl<T> BoundingBox<T> for Polygon<T>
///
fn bbox(&self) -> Self::Output {
let line = &self.exterior;
get_bbox(&line.0)
get_bbox(line.points())
}
}

Expand All @@ -133,7 +134,7 @@ impl<T> BoundingBox<T> for MultiPolygon<T>
/// Return the BoundingBox for a MultiPolygon
///
fn bbox(&self) -> Self::Output {
get_bbox(self.0.iter().flat_map(|poly| (poly.exterior).0.iter()))
get_bbox(self.0.iter().flat_map(|poly| poly.exterior.points().iter()))
}
}

Expand All @@ -147,7 +148,7 @@ mod test {
#[test]
fn empty_linestring_test() {
let vect = Vec::<Point<f64>>::new();
let linestring = LineString(vect);
let linestring = LineString::new(vect).unwrap();
let bbox = linestring.bbox();
assert!(bbox.is_none());
}
Expand All @@ -156,24 +157,24 @@ mod test {
let p = Point::new(40.02f64, 116.34);
let mut vect = Vec::<Point<f64>>::new();
vect.push(p);
let linestring = LineString(vect);
let linestring = LineString::new(vect).unwrap();
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 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 linestring = LineString::new(vec![p(1., 1.), p(2., -2.), p(-3., -3.), p(-4., 4.)]).unwrap();
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 multiline = MultiLineString::new(vec![LineString::new(vec![p(1., 1.), p(-40., 1.)]).unwrap(),
LineString::new(vec![p(1., 1.), p(50., 1.)]).unwrap(),
LineString::new(vec![p(1., 1.), p(1., -60.)]).unwrap(),
LineString::new(vec![p(1., 1.), p(1., 70.)])]).unwrap();
let bbox = Bbox{xmin: -40., ymax: 70., xmax: 50., ymin: -60.};
assert_eq!(bbox, multiline.bbox().unwrap());
}
Expand All @@ -187,17 +188,17 @@ mod test {
#[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 linestring = LineString::new(vec![p(0., 0.), p(5., 0.), p(5., 6.), p(0., 6.), p(0., 0.)]).unwrap();
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 p = |x, y| Point(Coordinate { x: x, y: y });
let mpoly = MultiPolygon(vec![Polygon::new(LineString(vec![p(0., 0.), p(50., 0.), p(0., -70.), p(0., 0.)]), Vec::new()),
Polygon::new(LineString(vec![p(0., 0.), p(5., 0.), p(0., 80.), p(0., 0.)]), Vec::new()),
Polygon::new(LineString(vec![p(0., 0.), p(-60., 0.), p(0., 6.), p(0., 0.)]), Vec::new()),
let mpoly = MultiPolygon(vec![Polygon::new(LineString::new(vec![p(0., 0.), p(50., 0.), p(0., -70.), p(0., 0.)]), Vec::new()).unwrap(),
Polygon::new(LineString::new(vec![p(0., 0.), p(5., 0.), p(0., 80.), p(0., 0.)]), Vec::new()).unwrap(),
Polygon::new(LineString::new(vec![p(0., 0.), p(-60., 0.), p(0., 6.), p(0., 0.)]), Vec::new()).unwrap(),
]);
let bbox = Bbox{xmin: -60., ymax: 80., xmax: 50., ymin: -70.};
assert_eq!(bbox, mpoly.bbox().unwrap());
Expand Down
49 changes: 15 additions & 34 deletions src/algorithm/centroid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub trait Centroid<T: Float> {
/// let mut vec = Vec::new();
/// vec.push(Point::new(40.02f64, 116.34));
/// vec.push(Point::new(40.02f64, 118.23));
/// let linestring = LineString(vec);
/// let linestring = LineString::new(vec).unwrap();
///
/// assert_eq!(linestring.centroid().unwrap(), Point::new(40.02, 117.285));
/// ```
Expand All @@ -29,9 +29,6 @@ pub trait Centroid<T: Float> {
fn simple_polygon_area<T>(linestring: &LineString<T>) -> T
where T: Float
{
if linestring.0.is_empty() || linestring.0.len() == 1 {
return T::zero();
}
let mut tmp = T::zero();
for line in linestring.lines() {
tmp = tmp + (line.start.x() * line.end.y() - line.end.x() * line.start.y());
Expand Down Expand Up @@ -76,12 +73,6 @@ impl<T> Centroid<T> for LineString<T>
// The Centroid of a LineString is the mean of the middle of the segment
// weighted by the length of the segments.
fn centroid(&self) -> Self::Output {
if self.0.is_empty() {
return None;
}
if self.0.len() == 1 {
Some(self.0[0].clone())
} else {
let mut sum_x = T::zero();
let mut sum_y = T::zero();
let mut total_length = T::zero();
Expand All @@ -95,7 +86,6 @@ impl<T> Centroid<T> for LineString<T>
Some(Point::new(sum_x / total_length, sum_y / total_length))
}
}
}

impl<T> Centroid<T> for Polygon<T>
where T: Float + FromPrimitive
Expand All @@ -112,14 +102,6 @@ impl<T> Centroid<T> for Polygon<T>
// See here for a formula: http://math.stackexchange.com/a/623849
// See here for detail on alternative methods: https://fotino.me/calculating-centroids/
fn centroid(&self) -> Self::Output {
let linestring = &self.exterior;
let vect = &linestring.0;
if vect.is_empty() {
return None;
}
if vect.len() == 1 {
Some(Point::new(vect[0].x(), vect[0].y()))
} else {
let external_centroid = simple_polygon_centroid(&self.exterior).unwrap();
if !self.interiors.is_empty() {
let external_area = simple_polygon_area(&self.exterior).abs();
Expand All @@ -142,7 +124,6 @@ impl<T> Centroid<T> for Polygon<T>
Some(external_centroid)
}
}
}

impl<T> Centroid<T> for MultiPolygon<T>
where T: Float + FromPrimitive
Expand Down Expand Up @@ -200,7 +181,7 @@ mod test {
#[test]
fn empty_linestring_test() {
let vec = Vec::<Point<f64>>::new();
let linestring = LineString(vec);
let linestring = LineString::new(vec).unwrap();
let centroid = linestring.centroid();
assert!(centroid.is_none());
}
Expand All @@ -209,14 +190,14 @@ mod test {
let p = Point::new(40.02f64, 116.34);
let mut vect = Vec::<Point<f64>>::new();
vect.push(p);
let linestring = LineString(vect);
let linestring = LineString::new(vect).unwrap();
let centroid = linestring.centroid();
assert_eq!(centroid, Some(p));
}
#[test]
fn linestring_test() {
let p = |x| Point(Coordinate { x: x, y: 1. });
let linestring = LineString(vec![p(1.), p(7.), p(8.), p(9.), p(10.), p(11.)]);
let linestring = LineString::new(vec![p(1.), p(7.), p(8.), p(9.), p(10.), p(11.)]).unwrap();
assert_eq!(linestring.centroid(),
Some(Point(Coordinate { x: 6., y: 1. })));
}
Expand All @@ -225,29 +206,29 @@ mod test {
fn empty_polygon_test() {
let v1 = Vec::new();
let v2 = Vec::new();
let linestring = LineString::<f64>(v1);
let linestring = LineString::<f64>::new(v1).unwrap();
let poly = Polygon::new(linestring, v2);
assert!(poly.centroid().is_none());
}
#[test]
fn polygon_one_point_test() {
let p = Point(Coordinate { x: 2., y: 1. });
let v = Vec::new();
let linestring = LineString(vec![p]);
let linestring = LineString::new(vec![p]).unwrap();
let poly = Polygon::new(linestring, v);
assert_eq!(poly.centroid(), Some(p));
}
#[test]
fn polygon_test() {
let p = |x, y| Point(Coordinate { x: x, y: y });
let v = Vec::new();
let linestring = LineString(vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]);
let linestring = LineString::new(vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]).unwrap();
let poly = Polygon::new(linestring, v);
assert_eq!(poly.centroid(), Some(p(1., 1.)));
}
#[test]
fn polygon_hole_test() {
let ls1 = LineString(vec![Point::new(5.0, 1.0),
let ls1 = LineString::new(vec![Point::new(5.0, 1.0).unwrap(),
Point::new(4.0, 2.0),
Point::new(4.0, 3.0),
Point::new(5.0, 4.0),
Expand All @@ -257,12 +238,12 @@ mod test {
Point::new(6.0, 1.0),
Point::new(5.0, 1.0)]);

let ls2 = LineString(vec![Point::new(5.0, 1.3),
let ls2 = LineString::new(vec![Point::new(5.0, 1.3).unwrap(),
Point::new(5.5, 2.0),
Point::new(6.0, 1.3),
Point::new(5.0, 1.3)]);

let ls3 = LineString(vec![Point::new(5., 2.3),
let ls3 = LineString::new(vec![Point::new(5., 2.3).unwrap(),
Point::new(5.5, 3.0),
Point::new(6., 2.3),
Point::new(5., 2.3)]);
Expand All @@ -279,16 +260,16 @@ mod test {
#[test]
fn multipolygon_one_polygon_test() {
let p = |x, y| Point(Coordinate { x: x, y: y });
let linestring = LineString(vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]);
let linestring = LineString::new(vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]).unwrap();
let poly = Polygon::new(linestring, Vec::new());
assert_eq!(MultiPolygon(vec![poly]).centroid(), Some(p(1., 1.)));
}
#[test]
fn multipolygon_two_polygons_test() {
let p = |x, y| Point(Coordinate { x: x, y: y });
let linestring = LineString(vec![p(2., 1.), p(5., 1.), p(5., 3.), p(2., 3.), p(2., 1.)]);
let linestring = LineString::new(vec![p(2., 1.), p(5., 1.), p(5., 3.), p(2., 3.), p(2., 1.)]).unwrap();
let poly1 = Polygon::new(linestring, Vec::new());
let linestring = LineString(vec![p(7., 1.), p(8., 1.), p(8., 2.), p(7., 2.), p(7., 1.)]);
let linestring = LineString::new(vec![p(7., 1.), p(8., 1.), p(8., 2.), p(7., 2.), p(7., 1.)]).unwrap();
let poly2 = Polygon::new(linestring, Vec::new());
let dist = MultiPolygon(vec![poly1, poly2])
.centroid()
Expand All @@ -299,9 +280,9 @@ mod test {
#[test]
fn multipolygon_two_polygons_of_opposite_clockwise_test() {
let p = |x, y| Point(Coordinate { x: x, y: y });
let linestring = LineString(vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]);
let linestring = LineString::new(vec![p(0., 0.), p(2., 0.), p(2., 2.), p(0., 2.), p(0., 0.)]).unwrap();
let poly1 = Polygon::new(linestring, Vec::new());
let linestring = LineString(vec![p(0., 0.), p(-2., 0.), p(-2., 2.), p(0., 2.), p(0., 0.)]);
let linestring = LineString::new(vec![p(0., 0.), p(-2., 0.), p(-2., 2.), p(0., 2.), p(0., 0.)]).unwrap();
let poly2 = Polygon::new(linestring, Vec::new());
assert_eq!(MultiPolygon(vec![poly1, poly2]).centroid(), Some(p(0., 1.)));
}
Expand Down
Loading