use geo_types::{Coord, Geometry};
use crate::algorithm::centroid::get_centroid;
use crate::geometry_adapter::{distance, is_geometry_empty};
pub(crate) struct InteriorPointLine {
centroid: Option<Coord<f64>>,
min_distance: f64,
interior_point: Option<Coord<f64>>,
}
impl InteriorPointLine {
pub(crate) fn new(g: &Geometry<f64>) -> Self {
let mut int_pt = Self {
centroid: get_centroid(g),
min_distance: f64::MAX,
interior_point: None,
};
int_pt.add_interior_geometry(g);
if int_pt.interior_point.is_none() {
int_pt.add_endpoints_geometry(g);
}
int_pt
}
pub(crate) fn get_interior_point(&self) -> Option<Coord<f64>> {
self.interior_point
}
fn add_interior_geometry(&mut self, geom: &Geometry<f64>) {
if is_geometry_empty(geom) {
return;
}
match geom {
Geometry::LineString(ls) => self.add_interior_coordinates(&ls.0),
Geometry::MultiLineString(mls) => {
for ls in &mls.0 {
if ls.0.is_empty() {
continue;
}
self.add_interior_coordinates(&ls.0);
}
}
Geometry::GeometryCollection(gc) => {
for g in &gc.0 {
self.add_interior_geometry(g);
}
}
_ => {}
}
}
fn add_interior_coordinates(&mut self, pts: &[Coord<f64>]) {
for &pt in pts.iter().take(pts.len().saturating_sub(1)).skip(1) {
self.add(pt);
}
}
fn add_endpoints_geometry(&mut self, geom: &Geometry<f64>) {
if is_geometry_empty(geom) {
return;
}
match geom {
Geometry::LineString(ls) => self.add_endpoints_coordinates(&ls.0),
Geometry::MultiLineString(mls) => {
for ls in &mls.0 {
if ls.0.is_empty() {
continue;
}
self.add_endpoints_coordinates(&ls.0);
}
}
Geometry::GeometryCollection(gc) => {
for g in &gc.0 {
self.add_endpoints_geometry(g);
}
}
_ => {}
}
}
fn add_endpoints_coordinates(&mut self, pts: &[Coord<f64>]) {
self.add(pts[0]);
self.add(pts[pts.len() - 1]);
}
fn add(&mut self, point: Coord<f64>) {
let dist = distance(
point,
self.centroid
.expect("centroid is set for a non-empty input"),
);
if dist < self.min_distance {
self.interior_point = Some(point);
self.min_distance = dist;
}
}
}
pub(crate) fn interior_point_line(geom: &Geometry<f64>) -> Option<Coord<f64>> {
let int_pt = InteriorPointLine::new(geom);
int_pt.get_interior_point()
}