use num_traits::Float;
use ::{LineString, MultiLineString, MultiPolygon, Point, Polygon};
use algorithm::euclidean_distance::EuclideanDistance;
fn point_line_distance<T>(point: &Point<T>, start: &Point<T>, end: &Point<T>) -> T
where
T: Float,
{
if start == end {
point.euclidean_distance(start)
} else {
let numerator = ((end.x() - start.x()) * (start.y() - point.y())
- (start.x() - point.x()) * (end.y() - start.y()))
.abs();
let denominator = start.euclidean_distance(end);
numerator / denominator
}
}
fn rdp<T>(points: &[Point<T>], epsilon: &T) -> Vec<Point<T>>
where
T: Float,
{
if points.is_empty() {
return points.to_vec();
}
let mut dmax = T::zero();
let mut index: usize = 0;
let mut distance: T;
for (i, _) in points.iter().enumerate().take(points.len() - 1).skip(1) {
distance = point_line_distance(&points[i], &points[0], &*points.last().unwrap());
if distance > dmax {
index = i;
dmax = distance;
}
}
if dmax > *epsilon {
let mut intermediate = rdp(&points[..index + 1], &*epsilon);
intermediate.pop();
intermediate.extend_from_slice(&rdp(&points[index..], &*epsilon));
intermediate
} else {
vec![*points.first().unwrap(), *points.last().unwrap()]
}
}
pub trait Simplify<T, Epsilon = T> {
fn simplify(&self, epsilon: &T) -> Self
where
T: Float;
}
impl<T> Simplify<T> for LineString<T>
where
T: Float,
{
fn simplify(&self, epsilon: &T) -> LineString<T> {
LineString(rdp(&self.0, epsilon))
}
}
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 ::{LineString, MultiLineString, MultiPolygon, Point, Polygon};
use super::{point_line_distance, rdp, Simplify};
#[test]
fn perpdistance_test() {
let start = Point::new(1.0, 2.0);
let end = Point::new(3.0, 4.0);
let p = Point::new(1.0, 1.0);
let dist = point_line_distance(&p, &start, &end);
assert_relative_eq!(dist, 0.7071067811865475);
}
#[test]
fn rdp_test() {
let mut vec = Vec::new();
vec.push(Point::new(0.0, 0.0));
vec.push(Point::new(5.0, 4.0));
vec.push(Point::new(11.0, 5.5));
vec.push(Point::new(17.3, 3.2));
vec.push(Point::new(27.8, 0.1));
let mut compare = Vec::new();
compare.push(Point::new(0.0, 0.0));
compare.push(Point::new(5.0, 4.0));
compare.push(Point::new(11.0, 5.5));
compare.push(Point::new(27.8, 0.1));
let simplified = rdp(&vec, &1.0);
assert_eq!(simplified, compare);
}
#[test]
fn rdp_test_empty_linestring() {
let vec = Vec::new();
let compare = Vec::new();
let simplified = rdp(&vec, &1.0);
assert_eq!(simplified, compare);
}
#[test]
fn rdp_test_two_point_linestring() {
let mut vec = Vec::new();
vec.push(Point::new(0.0, 0.0));
vec.push(Point::new(27.8, 0.1));
let mut compare = Vec::new();
compare.push(Point::new(0.0, 0.0));
compare.push(Point::new(27.8, 0.1));
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![],
),
])
);
}
}