use std::fmt;
use geo_types::{Coord, Geometry};
use crate::algorithm::interior_point::dimension_non_empty;
use crate::algorithm::locate::simple_point_in_area_locator::locate;
use crate::geom::location::{BOUNDARY, INTERIOR};
use crate::geometry_adapter::coordinates_at_dimension;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Verification {
Interior,
OnGeometry,
OffGeometry,
Unverifiable,
}
impl fmt::Display for Verification {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::Interior => "interior",
Self::OnGeometry => "on-geometry",
Self::OffGeometry => "off-geometry",
Self::Unverifiable => "unverifiable",
})
}
}
pub fn verify_interior_point(
point: Option<Coord<f64>>,
geometry: Option<&Geometry<f64>>,
) -> Verification {
let (Some(point), Some(geometry)) = (point, geometry) else {
return Verification::Unverifiable;
};
let dim = dimension_non_empty(geometry);
if dim < 0 {
return Verification::Unverifiable;
}
if dim == 2 {
let location = locate(point, geometry);
if location == INTERIOR {
return Verification::Interior;
}
if location == BOUNDARY {
return Verification::OnGeometry;
}
return Verification::OffGeometry;
}
if coordinates_at_dimension(geometry, dim)
.iter()
.any(|c| c.x == point.x && c.y == point.y)
{
Verification::OnGeometry
} else {
Verification::OffGeometry
}
}
#[cfg(test)]
mod tests {
use super::{Verification, verify_interior_point};
use crate::interior_point;
use geo_types::{
Coord, Geometry, GeometryCollection, LineString, MultiLineString, MultiPoint, Point,
Polygon,
};
fn polygon(ring: &[(f64, f64)]) -> Geometry<f64> {
Geometry::Polygon(Polygon::new(LineString::from(ring.to_vec()), vec![]))
}
fn verify_computed(geometry: &Geometry<f64>) -> Verification {
verify_interior_point(interior_point(geometry), Some(geometry))
}
#[test]
fn reports_interior_for_a_point_inside_an_areal_geometry() {
let square = polygon(&[
(0.0, 0.0),
(10.0, 0.0),
(10.0, 10.0),
(0.0, 10.0),
(0.0, 0.0),
]);
assert_eq!(interior_point(&square), Some(Coord { x: 5.0, y: 5.0 }));
assert_eq!(verify_computed(&square), Verification::Interior);
}
#[test]
fn reports_on_geometry_for_a_zero_area_polygon() {
let collapsed = polygon(&[(10.0, 10.0), (10.0, 10.0), (10.0, 10.0), (10.0, 10.0)]);
assert_eq!(verify_computed(&collapsed), Verification::OnGeometry);
}
#[test]
fn reports_on_geometry_for_a_polygon_collapsed_to_a_segment() {
let collapsed = polygon(&[(0.0, 0.0), (10.0, 0.0), (0.0, 0.0)]);
assert_eq!(verify_computed(&collapsed), Verification::OnGeometry);
}
#[test]
fn reports_on_geometry_for_a_point() {
let point = Geometry::Point(Point::new(5.0, 5.0));
assert_eq!(verify_computed(&point), Verification::OnGeometry);
}
#[test]
fn reports_on_geometry_for_a_line_string() {
let line = Geometry::LineString(LineString::from(vec![(0.0, 0.0), (10.0, 10.0)]));
assert_eq!(interior_point(&line), Some(Coord { x: 0.0, y: 0.0 }));
assert_eq!(verify_computed(&line), Verification::OnGeometry);
}
#[test]
fn reports_on_geometry_for_a_multi_point() {
let points = Geometry::MultiPoint(MultiPoint(vec![
Point::new(0.0, 0.0),
Point::new(10.0, 10.0),
]));
assert_eq!(verify_computed(&points), Verification::OnGeometry);
}
#[test]
fn reports_on_geometry_for_a_collection_of_a_point_and_a_line() {
let collection = Geometry::GeometryCollection(GeometryCollection(vec![
Geometry::Point(Point::new(5.0, 5.0)),
Geometry::LineString(LineString::from(vec![(0.0, 0.0), (10.0, 10.0)])),
]));
assert_eq!(verify_computed(&collection), Verification::OnGeometry);
}
#[test]
fn follows_the_non_empty_dimension_for_a_collection_holding_an_empty_line() {
let collection = Geometry::GeometryCollection(GeometryCollection(vec![
Geometry::Point(Point::new(5.0, 5.0)),
Geometry::LineString(LineString(vec![])),
]));
assert_eq!(interior_point(&collection), Some(Coord { x: 5.0, y: 5.0 }));
assert_eq!(verify_computed(&collection), Verification::OnGeometry);
}
#[test]
fn reports_off_geometry_for_a_fabricated_point_outside_an_areal_geometry() {
let square = polygon(&[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0)]);
assert_eq!(
verify_interior_point(Some(Coord { x: 100.0, y: 100.0 }), Some(&square)),
Verification::OffGeometry
);
}
#[test]
fn reports_off_geometry_for_a_fabricated_point_off_a_line_string() {
let line = Geometry::LineString(LineString::from(vec![(0.0, 0.0), (10.0, 10.0)]));
assert_eq!(
verify_interior_point(Some(Coord { x: 100.0, y: 100.0 }), Some(&line)),
Verification::OffGeometry
);
}
#[test]
fn reports_unverifiable_for_an_empty_geometry() {
let empty = Geometry::Polygon(Polygon::new(LineString(vec![]), vec![]));
assert_eq!(interior_point(&empty), None);
assert_eq!(verify_computed(&empty), Verification::Unverifiable);
}
#[test]
fn reports_unverifiable_for_a_multi_line_string_of_one_empty_line() {
let empty = Geometry::MultiLineString(MultiLineString::new(vec![LineString(vec![])]));
assert_eq!(verify_computed(&empty), Verification::Unverifiable);
}
#[test]
fn reports_unverifiable_when_either_argument_is_absent() {
let square = polygon(&[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0), (0.0, 0.0)]);
assert_eq!(
verify_interior_point(None, Some(&square)),
Verification::Unverifiable
);
assert_eq!(
verify_interior_point(Some(Coord { x: 0.0, y: 0.0 }), None),
Verification::Unverifiable
);
assert_eq!(
verify_interior_point(None, None),
Verification::Unverifiable
);
}
#[test]
fn prints_the_four_outcome_words() {
assert_eq!(Verification::Interior.to_string(), "interior");
assert_eq!(Verification::OnGeometry.to_string(), "on-geometry");
assert_eq!(Verification::OffGeometry.to_string(), "off-geometry");
assert_eq!(Verification::Unverifiable.to_string(), "unverifiable");
}
}