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

Implement Simplification traits for more things #135

Merged
merged 4 commits into from
Aug 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/algorithm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ pub mod haversine_destination;
pub mod haversine_distance;
/// Returns the Bbox of a geometry.
pub mod boundingbox;
/// Simplifies a `LineString` using the Ramer-Douglas-Peucker algorithm.
/// Simplifies geometries using the Ramer-Douglas-Peucker algorithm.
pub mod simplify;
/// Simplifies a `LineString` using the Visvalingam-Whyatt algorithm.
/// Simplifies geometries using the Visvalingam-Whyatt algorithm.
pub mod simplifyvw;
/// Calculates the convex hull of a geometry.
pub mod convexhull;
Expand Down
107 changes: 103 additions & 4 deletions src/algorithm/simplify.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use num_traits::Float;
use types::{Point, LineString};
use types::{Point, LineString, Polygon, MultiLineString, MultiPolygon};
use algorithm::distance::Distance;

// perpendicular distance from a point to a line
Expand Down Expand Up @@ -47,8 +47,16 @@ fn rdp<T>(points: &[Point<T>], epsilon: &T) -> Vec<Point<T>>
}
}

/// Simplifies a geometry.
///
/// The [Ramer–Douglas–Peucker
/// algorithm](https://en.wikipedia.org/wiki/Ramer–Douglas–Peucker_algorithm) simplifes a
/// linestring. Polygons are simplified by running the RDP algorithm on all their constituent
/// rings. This may result in invalid Polygons, and has no guarantee of preserving topology.
///
/// Multi* objects are simplified by simplifing all their constituent geometries individually.
pub trait Simplify<T, Epsilon = T> {
/// Returns the simplified representation of a LineString, using the [Ramer–Douglas–Peucker](https://en.wikipedia.org/wiki/Ramer–Douglas–Peucker_algorithm) algorithm
/// Returns the simplified representation of a geometry, using the [Ramer–Douglas–Peucker](https://en.wikipedia.org/wiki/Ramer–Douglas–Peucker_algorithm) algorithm
///
/// ```
/// use geo::{Point, LineString};
Expand Down Expand Up @@ -81,10 +89,34 @@ impl<T> Simplify<T> for LineString<T>
}
}

impl<T> Simplify<T> for MultiLineString<T>
where T: Float
{
fn simplify(&self, epsilon: &T) -> MultiLineString<T> {
MultiLineString(self.0.iter().map(|l| l.simplify(epsilon)).collect())
}
}

impl<T> Simplify<T> for Polygon<T>
where T: Float
{
fn simplify(&self, epsilon: &T) -> Polygon<T> {
Polygon::new(self.exterior.simplify(epsilon), self.interiors.iter().map(|l| l.simplify(epsilon)).collect())
}
}

impl<T> Simplify<T> for MultiPolygon<T>
where T: Float
{
fn simplify(&self, epsilon: &T) -> MultiPolygon<T> {
MultiPolygon(self.0.iter().map(|p| p.simplify(epsilon)).collect())
}
}

#[cfg(test)]
mod test {
use types::{Point};
use super::{point_line_distance, rdp};
use types::{Point, LineString, Polygon, MultiLineString, MultiPolygon};
use super::{point_line_distance, rdp, Simplify};

#[test]
fn perpdistance_test() {
Expand Down Expand Up @@ -128,4 +160,71 @@ mod test {
let simplified = rdp(&vec, &1.0);
assert_eq!(simplified, compare);
}

#[test]
fn multilinestring() {
let mline = MultiLineString(vec![LineString(vec![
Point::new(0.0, 0.0),
Point::new(5.0, 4.0),
Point::new(11.0, 5.5),
Point::new(17.3, 3.2),
Point::new(27.8, 0.1),
])]);

let mline2 = mline.simplify(&1.0);

assert_eq!(mline2, MultiLineString(vec![LineString(vec![
Point::new(0.0, 0.0),
Point::new(5.0, 4.0),
Point::new(11.0, 5.5),
Point::new(27.8, 0.1),
])]));
}

#[test]
fn polygon() {
let poly = Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(5., 11.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![]);

let poly2 = poly.simplify(&2.);

assert_eq!(poly2, Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![])
);
}


#[test]
fn multipolygon() {
let mpoly = MultiPolygon(vec![Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(5., 11.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![])]);

let mpoly2 = mpoly.simplify(&2.);

assert_eq!(mpoly2, MultiPolygon(vec![Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![])])
);
}
}
99 changes: 95 additions & 4 deletions src/algorithm/simplifyvw.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use num_traits::Float;
use types::{Point, LineString};
use types::{Point, LineString, Polygon, MultiLineString, MultiPolygon};

// A helper struct for `visvalingam`, defined out here because
// #[deriving] doesn't work inside functions.
Expand Down Expand Up @@ -144,8 +144,13 @@ fn area<T>(p1: &Point<T>, p2: &Point<T>, p3: &Point<T>) -> T
(T::one() + T::one())
}

/// Simplifies a geometry.
///
/// Polygons are simplified by running the algorithm on all their constituent rings. This may
/// result in invalid Polygons, and has no guarantee of perserving topology. Multi* objects are
/// simplified by simplifyng all their constituent geometries individually.
pub trait SimplifyVW<T, Epsilon = T> {
/// Returns the simplified representation of a LineString, using the [Visvalingam-Whyatt](http://www.tandfonline.com/doi/abs/10.1179/000870493786962263) algorithm
/// Returns the simplified representation of a geometry, using the [Visvalingam-Whyatt](http://www.tandfonline.com/doi/abs/10.1179/000870493786962263) algorithm
///
/// See [here](https://bost.ocks.org/mike/simplify/) for a graphical explanation
///
Expand Down Expand Up @@ -179,10 +184,35 @@ impl<T> SimplifyVW<T> for LineString<T>
}
}

impl<T> SimplifyVW<T> for MultiLineString<T>
where T: Float
{
fn simplifyvw(&self, epsilon: &T) -> MultiLineString<T> {
MultiLineString(self.0.iter().map(|l| l.simplifyvw(epsilon)).collect())
}
}

impl<T> SimplifyVW<T> for Polygon<T>
where T: Float
{
fn simplifyvw(&self, epsilon: &T) -> Polygon<T> {
Polygon::new(self.exterior.simplifyvw(epsilon), self.interiors.iter().map(|l| l.simplifyvw(epsilon)).collect())
}
}


impl<T> SimplifyVW<T> for MultiPolygon<T>
where T: Float
{
fn simplifyvw(&self, epsilon: &T) -> MultiPolygon<T> {
MultiPolygon(self.0.iter().map(|p| p.simplifyvw(epsilon)).collect())
}
}

#[cfg(test)]
mod test {
use types::Point;
use super::visvalingam;
use types::{Point, LineString, Polygon, MultiLineString, MultiPolygon};
use super::{visvalingam, SimplifyVW};

#[test]
fn visvalingam_test() {
Expand Down Expand Up @@ -224,4 +254,65 @@ mod test {
let simplified = visvalingam(&vec, &1.0);
assert_eq!(simplified, compare);
}

#[test]
fn multilinestring() {
// this is the PostGIS example
let points = vec![(5.0, 2.0), (3.0, 8.0), (6.0, 20.0), (7.0, 25.0), (10.0, 10.0)];
let points_ls: Vec<_> = points.iter().map(|e| Point::new(e.0, e.1)).collect();

let correct = vec![(5.0, 2.0), (7.0, 25.0), (10.0, 10.0)];
let correct_ls: Vec<_> = correct.iter().map(|e| Point::new(e.0, e.1)).collect();

let mline = MultiLineString(vec![LineString(points_ls)]);
assert_eq!(mline.simplifyvw(&30.),
MultiLineString(vec![LineString(correct_ls)])
);
}

#[test]
fn polygon() {
let poly = Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(5., 11.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![]);

let poly2 = poly.simplifyvw(&10.);

assert_eq!(poly2, Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![])
);
}

#[test]
fn multipolygon() {
let mpoly = MultiPolygon(vec![Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(5., 11.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![])]);

let mpoly2 = mpoly.simplifyvw(&10.);

assert_eq!(mpoly2, MultiPolygon(vec![Polygon::new(LineString(vec![
Point::new(0., 0.),
Point::new(0., 10.),
Point::new(10., 10.),
Point::new(10., 0.),
Point::new(0., 0.),
]), vec![])])
);
}
}