Skip to content

Commit

Permalink
Merge branch 'master' into frewsxcv-bounding-rect
Browse files Browse the repository at this point in the history
  • Loading branch information
frewsxcv authored Jul 4, 2018
2 parents b9e692b + 88012e2 commit cd6446f
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 1 deletion.
129 changes: 128 additions & 1 deletion geo-types/src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,56 @@
// wouldn't have this duplication

use num_traits::{Float, ToPrimitive};
use {Coordinate, CoordinateType, Line, Point, Rect};
use {Coordinate, CoordinateType, Line, LineString, Point, Rect};

pub static COORD_PRECISION: f32 = 1e-1; // 0.1m

pub trait Contains<Rhs = Self> {
fn contains(&self, rhs: &Rhs) -> bool;
}

impl<T> Contains<Point<T>> for Point<T>
where
T: Float + ToPrimitive,
{
fn contains(&self, p: &Point<T>) -> bool {
self.euclidean_distance(p).to_f32().unwrap() < COORD_PRECISION
}
}

impl<T> Contains<Point<T>> for LineString<T>
where
T: Float,
{
fn contains(&self, p: &Point<T>) -> bool {
// LineString without points
if self.0.is_empty() {
return false;
}
// LineString with one point equal p
if self.0.len() == 1 {
return Point(self.0[0]).contains(p);
}
// check if point is a vertex
if self.0.contains(&p.0) {
return true;
}
for line in self.lines() {
if ((line.start.y == line.end.y)
&& (line.start.y == p.y())
&& (p.x() > line.start.x.min(line.end.x))
&& (p.x() < line.start.x.max(line.end.x)))
|| ((line.start.x == line.end.x)
&& (line.start.x == p.x())
&& (p.y() > line.start.y.min(line.end.y))
&& (p.y() < line.start.y.max(line.end.y)))
{
return true;
}
}
false
}
}

pub trait EuclideanDistance<T, Rhs = Self> {
fn euclidean_distance(&self, rhs: &Rhs) -> T;
Expand Down Expand Up @@ -39,6 +88,33 @@ where
}
}

impl<T> EuclideanDistance<T, LineString<T>> for Point<T>
where
T: Float,
{
/// Minimum distance from a Point to a LineString
fn euclidean_distance(&self, linestring: &LineString<T>) -> T {
// No need to continue if the point is on the LineString, or it's empty
if linestring.contains(self) || linestring.0.is_empty() {
return T::zero();
}
linestring
.lines()
.map(|line| line_segment_distance(*self, line.start_point(), line.end_point()))
.fold(T::max_value(), |accum, val| accum.min(val))
}
}

impl<T> EuclideanDistance<T, Point<T>> for LineString<T>
where
T: Float,
{
/// Minimum distance from a LineString to a Point
fn euclidean_distance(&self, point: &Point<T>) -> T {
point.euclidean_distance(self)
}
}

impl<T> EuclideanDistance<T, Point<T>> for Line<T>
where
T: Float,
Expand All @@ -55,6 +131,43 @@ pub trait BoundingRect<T: CoordinateType> {
fn bounding_rect(&self) -> Self::Output;
}

fn get_min_max<T>(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<I, T>(collection: I) -> Option<Bbox<T>>
where
T: CoordinateType,
I: IntoIterator<Item = Coordinate<T>>,
{
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<T> BoundingRect<T> for Line<T>
where
T: CoordinateType,
Expand All @@ -72,3 +185,17 @@ where
}
}
}

impl<T> BoundingRect<T> for LineString<T>
where
T: CoordinateType,
{
type Output = Option<Rect<T>>;

///
/// Return the BoundingBox for a LineString
///
fn bbox(&self) -> Self::Output {
get_bbox(self.0.iter().cloned())
}
}
38 changes: 38 additions & 0 deletions geo-types/src/line_string.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use std::iter::FromIterator;
use {Coordinate, CoordinateType, Line, Point, Triangle};

#[cfg(feature = "spade")]
use algorithms::{BoundingBox, EuclideanDistance};

/// An ordered collection of two or more [`Coordinate`s](struct.Coordinate.html), representing a
/// path between locations.
///
Expand Down Expand Up @@ -162,3 +165,38 @@ impl<T: CoordinateType> IntoIterator for LineString<T> {
self.0.into_iter()
}
}

#[cfg(feature = "spade")]
impl<T> ::spade::SpatialObject for LineString<T>
where
T: ::num_traits::Float + ::spade::SpadeNum + ::std::fmt::Debug,
{
type Point = Point<T>;

fn mbr(&self) -> ::spade::BoundingRect<Self::Point> {
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),
)
},
}
}

fn distance2(&self, point: &Self::Point) -> <Self::Point as ::spade::PointN>::Scalar {
let d = self.euclidean_distance(point);
if d == T::zero() {
d
} else {
d.powi(2)
}
}
}
1 change: 1 addition & 0 deletions geo-types/src/triangle.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use {Coordinate, CoordinateType};

#[derive(Copy, Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Triangle<T: CoordinateType>(pub Coordinate<T>, pub Coordinate<T>, pub Coordinate<T>);

impl<T: CoordinateType> Triangle<T> {
Expand Down

0 comments on commit cd6446f

Please sign in to comment.