use super::{Geometry, Point};
use geo::{
Area as GeoArea, BoundingRect, Centroid as GeoCentroid, Contains as GeoContains,
Intersects as GeoIntersects,
};
pub fn distance(g1: &Geometry, g2: &Geometry) -> f64 {
use geo::{Distance, Euclidean, Haversine};
match (g1, g2) {
(Geometry::Point(p1), Geometry::Point(p2)) => {
let geo_p1 = p1.to_geo();
let geo_p2 = p2.to_geo();
Haversine.distance(geo_p1, geo_p2)
}
(Geometry::Point(p), other) | (other, Geometry::Point(p)) => other.distance_to(p),
(g1, g2) => {
let geo_g1 = g1.to_geo();
let geo_g2 = g2.to_geo();
Euclidean.distance(&geo_g1, &geo_g2)
}
}
}
pub fn contains(g1: &Geometry, g2: &Geometry) -> bool {
if let Geometry::Point(p) = g2 {
return g1.contains(p);
}
let geo_g1 = g1.to_geo();
let geo_g2 = g2.to_geo();
geo_g1.contains(&geo_g2)
}
pub fn intersects(g1: &Geometry, g2: &Geometry) -> bool {
let geo_g1 = g1.to_geo();
let geo_g2 = g2.to_geo();
geo_g1.intersects(&geo_g2)
}
pub fn within(g1: &Geometry, g2: &Geometry) -> bool {
contains(g2, g1)
}
pub fn touches(g1: &Geometry, g2: &Geometry) -> bool {
intersects(g1, g2) && !contains(g1, g2) && !contains(g2, g1)
}
pub fn disjoint(g1: &Geometry, g2: &Geometry) -> bool {
!intersects(g1, g2)
}
pub fn area(geometry: &Geometry) -> f64 {
match geometry {
Geometry::Point(_) => 0.0,
Geometry::LineString(_) => 0.0,
Geometry::Polygon(poly) => {
let geo_poly = poly.to_geo();
geo_poly.unsigned_area()
}
}
}
pub fn centroid(geometry: &Geometry) -> Option<Point> {
let geo_geom = geometry.to_geo();
geo_geom.centroid().map(|c| Point::new(c.y(), c.x()))
}
pub fn envelope(geometry: &Geometry) -> Option<super::BoundingBox> {
let geo_geom = geometry.to_geo();
geo_geom
.bounding_rect()
.map(|rect| super::BoundingBox::new(rect.min().y, rect.min().x, rect.max().y, rect.max().x))
}
pub fn is_simple(geometry: &Geometry) -> bool {
match geometry {
Geometry::Point(_) => true,
Geometry::LineString(_) => {
true
}
Geometry::Polygon(_) => {
true
}
}
}
pub fn is_valid(geometry: &Geometry) -> bool {
match geometry {
Geometry::Point(_) => true,
Geometry::LineString(ls) => ls.points.len() >= 2,
Geometry::Polygon(poly) => poly.exterior.len() >= 3,
}
}
pub fn equals(g1: &Geometry, g2: &Geometry) -> bool {
match (g1, g2) {
(Geometry::Point(p1), Geometry::Point(p2)) => p1.equals(p2),
_ => {
contains(g1, g2) && contains(g2, g1)
}
}
}
pub fn buffer(geometry: &Geometry, distance_meters: f64) -> super::BoundingBox {
let bbox = geometry.bounding_box();
let degree_delta = distance_meters / 111_000.0;
super::BoundingBox::new(
bbox.min_lat - degree_delta,
bbox.min_lon - degree_delta,
bbox.max_lat + degree_delta,
bbox.max_lon + degree_delta,
)
}
pub fn overlaps(g1: &Geometry, g2: &Geometry) -> bool {
intersects(g1, g2) && !contains(g1, g2) && !contains(g2, g1)
}
pub fn crosses(g1: &Geometry, g2: &Geometry) -> bool {
intersects(g1, g2) && !contains(g1, g2) && !contains(g2, g1)
}
#[cfg(test)]
mod tests {
use super::super::Polygon;
use super::*;
#[test]
fn test_distance_points() {
let p1 = Point::new(40.7128, -74.0060); let p2 = Point::new(40.7589, -73.9851);
let g1 = Geometry::Point(p1);
let g2 = Geometry::Point(p2);
let d = distance(&g1, &g2);
assert!(d > 5000.0 && d < 7000.0);
}
#[test]
fn test_contains_point_in_polygon() {
let square = vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(1.0, 1.0),
Point::new(0.0, 1.0),
Point::new(0.0, 0.0),
];
let polygon = Polygon::new(square).unwrap();
let g1 = Geometry::Polygon(polygon);
let inside_point = Point::new(0.5, 0.5);
let g2 = Geometry::Point(inside_point);
assert!(contains(&g1, &g2));
}
#[test]
fn test_contains_point_outside_polygon() {
let square = vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(1.0, 1.0),
Point::new(0.0, 1.0),
Point::new(0.0, 0.0),
];
let polygon = Polygon::new(square).unwrap();
let g1 = Geometry::Polygon(polygon);
let outside_point = Point::new(2.0, 2.0);
let g2 = Geometry::Point(outside_point);
assert!(!contains(&g1, &g2));
}
#[test]
fn test_intersects_same_point() {
let p = Point::new(40.7128, -74.0060);
let g1 = Geometry::Point(p);
let g2 = Geometry::Point(p);
assert!(intersects(&g1, &g2));
}
#[test]
fn test_within() {
let square = vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(1.0, 1.0),
Point::new(0.0, 1.0),
Point::new(0.0, 0.0),
];
let polygon = Polygon::new(square).unwrap();
let g1 = Geometry::Polygon(polygon);
let inside_point = Point::new(0.5, 0.5);
let g2 = Geometry::Point(inside_point);
assert!(within(&g2, &g1));
}
#[test]
fn test_disjoint() {
let p1 = Point::new(0.0, 0.0);
let p2 = Point::new(10.0, 10.0);
let g1 = Geometry::Point(p1);
let g2 = Geometry::Point(p2);
assert!(disjoint(&g1, &g2));
}
#[test]
fn test_area_point() {
let p = Point::new(40.7128, -74.0060);
let g = Geometry::Point(p);
assert_eq!(area(&g), 0.0);
}
#[test]
fn test_area_polygon() {
let square = vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(1.0, 1.0),
Point::new(0.0, 1.0),
Point::new(0.0, 0.0),
];
let polygon = Polygon::new(square).unwrap();
let g = Geometry::Polygon(polygon);
let a = area(&g);
assert!(a > 0.0);
}
#[test]
fn test_centroid_point() {
let p = Point::new(40.7128, -74.0060);
let g = Geometry::Point(p);
let c = centroid(&g).unwrap();
assert!(c.equals(&p));
}
#[test]
fn test_is_valid_point() {
let p = Point::new(40.7128, -74.0060);
let g = Geometry::Point(p);
assert!(is_valid(&g));
}
#[test]
fn test_is_valid_polygon() {
let square = vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(1.0, 1.0),
Point::new(0.0, 1.0),
Point::new(0.0, 0.0),
];
let polygon = Polygon::new(square).unwrap();
let g = Geometry::Polygon(polygon);
assert!(is_valid(&g));
}
#[test]
fn test_equals_same_point() {
let p = Point::new(40.7128, -74.0060);
let g1 = Geometry::Point(p);
let g2 = Geometry::Point(p);
assert!(equals(&g1, &g2));
}
#[test]
fn test_buffer() {
let p = Point::new(40.7128, -74.0060);
let g = Geometry::Point(p);
let buffered = buffer(&g, 1000.0);
assert!(buffered.max_lat > p.lat);
assert!(buffered.min_lat < p.lat);
}
#[test]
fn test_overlaps() {
let poly1 = vec![
Point::new(0.0, 0.0),
Point::new(1.0, 0.0),
Point::new(1.0, 1.0),
Point::new(0.0, 1.0),
Point::new(0.0, 0.0),
];
let poly2 = vec![
Point::new(0.5, 0.5),
Point::new(1.5, 0.5),
Point::new(1.5, 1.5),
Point::new(0.5, 1.5),
Point::new(0.5, 0.5),
];
let g1 = Geometry::Polygon(Polygon::new(poly1).unwrap());
let g2 = Geometry::Polygon(Polygon::new(poly2).unwrap());
assert!(overlaps(&g1, &g2));
}
}