use geometry_model::{
Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Polygon, Ring, Segment,
};
use geometry_trait::{Point as PointTrait, PointMut};
use crate::make::make_point;
#[must_use]
pub fn convert<Src, Dst>(src: &Src) -> Dst
where
Src: Convert<Dst>,
{
src.convert()
}
#[doc(hidden)]
pub trait Convert<Dst> {
fn convert(&self) -> Dst;
}
impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Box<P>
where
P: PointMut + Default,
{
fn convert(&self) -> Polygon<P, CW, CL> {
let min_x = self.min().get::<0>();
let min_y = self.min().get::<1>();
let max_x = self.max().get::<0>();
let max_y = self.max().get::<1>();
let ring = Ring::<P, CW, CL>::from_vec(alloc::vec![
make_point(&[min_x, min_y]),
make_point(&[min_x, max_y]),
make_point(&[max_x, max_y]),
make_point(&[max_x, min_y]),
make_point(&[min_x, min_y]),
]);
Polygon::new(ring)
}
}
impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Ring<P, CW, CL>
where
P: PointTrait + Copy,
{
fn convert(&self) -> Polygon<P, CW, CL> {
Polygon::new(Ring::from_vec(self.0.clone()))
}
}
impl<P, const CW: bool, const CL: bool> Convert<Linestring<P>> for Ring<P, CW, CL>
where
P: PointTrait + Copy,
{
fn convert(&self) -> Linestring<P> {
Linestring(self.0.clone())
}
}
impl<P> Convert<Linestring<P>> for Segment<P>
where
P: PointTrait + Copy,
{
fn convert(&self) -> Linestring<P> {
Linestring(alloc::vec![*self.start(), *self.end()])
}
}
impl<P> Convert<MultiPoint<P>> for P
where
P: PointTrait + Copy,
{
fn convert(&self) -> MultiPoint<P> {
MultiPoint(alloc::vec![*self])
}
}
impl<P> Convert<MultiLinestring<Linestring<P>>> for Linestring<P>
where
P: PointTrait + Copy,
{
fn convert(&self) -> MultiLinestring<Linestring<P>> {
MultiLinestring(alloc::vec![self.clone()])
}
}
impl<P, const CW: bool, const CL: bool> Convert<MultiPolygon<Polygon<P, CW, CL>>>
for Polygon<P, CW, CL>
where
P: PointTrait + Copy,
{
fn convert(&self) -> MultiPolygon<Polygon<P, CW, CL>> {
MultiPolygon(alloc::vec![self.clone()])
}
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
reason = "Converted corner coordinates are exact literals."
)]
mod tests {
use super::convert;
use geometry_cs::Cartesian;
use geometry_model::{Box, Linestring, MultiPoint, Point2D, Polygon, Segment};
use geometry_trait::{Point as _, Polygon as _, Ring as _};
type Pt = Point2D<f64, Cartesian>;
#[test]
fn box_to_polygon_five_points_and_corners() {
let b: Box<Pt> = Box::from_corners(Pt::new(0., 0.), Pt::new(4., 3.));
let pg: Polygon<Pt> = convert(&b);
let pts: alloc::vec::Vec<(f64, f64)> = pg
.exterior()
.points()
.map(|p| (p.get::<0>(), p.get::<1>()))
.collect();
assert_eq!(pts.len(), 5);
assert_eq!(pts[0], (0., 0.));
assert_eq!(pts[4], (0., 0.));
assert!(pts.contains(&(0., 3.)));
assert!(pts.contains(&(4., 3.)));
assert!(pts.contains(&(4., 0.)));
}
#[test]
fn segment_to_linestring_endpoints() {
let s = Segment::new(Pt::new(0., 0.), Pt::new(3., 4.));
let ls: Linestring<Pt> = convert(&s);
assert_eq!(ls.0.len(), 2);
assert_eq!(ls.0[0].get::<0>(), 0.);
assert_eq!(ls.0[1].get::<0>(), 3.);
}
#[test]
fn point_to_multi_point_single_member() {
let mp: MultiPoint<Pt> = convert(&Pt::new(1., 2.));
assert_eq!(mp.0.len(), 1);
assert_eq!(mp.0[0].get::<0>(), 1.);
}
#[test]
fn ring_to_polygon_becomes_hole_free_exterior() {
use geometry_model::Ring;
let ring: Ring<Pt> = Ring::from_vec(vec![
Pt::new(0., 0.),
Pt::new(1., 0.),
Pt::new(1., 1.),
Pt::new(0., 0.),
]);
let pg: Polygon<Pt> = convert(&ring);
assert_eq!(pg.exterior().points().count(), 4);
assert_eq!(pg.interiors().count(), 0);
let first = pg.exterior().points().next().unwrap();
assert_eq!((first.get::<0>(), first.get::<1>()), (0., 0.));
}
#[test]
fn ring_to_linestring_copies_points() {
use geometry_model::Ring;
let ring: Ring<Pt> =
Ring::from_vec(vec![Pt::new(2., 3.), Pt::new(4., 5.), Pt::new(2., 3.)]);
let ls: Linestring<Pt> = convert(&ring);
assert_eq!(ls.0.len(), 3);
assert_eq!((ls.0[1].get::<0>(), ls.0[1].get::<1>()), (4., 5.));
}
#[test]
fn linestring_to_multi_linestring_single_member() {
use geometry_model::MultiLinestring;
let ls: Linestring<Pt> = Linestring(vec![Pt::new(0., 0.), Pt::new(1., 1.)]);
let mls: MultiLinestring<Linestring<Pt>> = convert(&ls);
assert_eq!(mls.0.len(), 1);
assert_eq!(mls.0[0].0.len(), 2);
}
#[test]
fn polygon_to_multi_polygon_single_member() {
use geometry_model::{MultiPolygon, Ring};
let pg: Polygon<Pt> = Polygon::new(Ring::from_vec(vec![
Pt::new(0., 0.),
Pt::new(1., 0.),
Pt::new(1., 1.),
Pt::new(0., 0.),
]));
let mpg: MultiPolygon<Polygon<Pt>> = convert(&pg);
assert_eq!(mpg.0.len(), 1);
assert_eq!(mpg.0[0].exterior().points().count(), 4);
}
}