use crate::geometry::Geometry;
use crate::linestring::Linestring;
use crate::point::Point;
use crate::polygon::Polygon;
use geometry_tag::{MultiLinestringTag, MultiPointTag, MultiPolygonTag};
pub trait MultiPoint: Geometry<Kind = MultiPointTag> {
type ItemPoint: Point;
fn points(&self) -> impl ExactSizeIterator<Item = &Self::ItemPoint>;
}
pub trait MultiLinestring: Geometry<Kind = MultiLinestringTag> {
type ItemLinestring: Linestring<Point = Self::Point>;
fn linestrings(&self) -> impl ExactSizeIterator<Item = &Self::ItemLinestring>;
}
pub trait MultiPolygon: Geometry<Kind = MultiPolygonTag> {
type ItemPolygon: Polygon<Point = Self::Point>;
fn polygons(&self) -> impl ExactSizeIterator<Item = &Self::ItemPolygon>;
}
#[cfg(test)]
mod tests {
extern crate alloc;
use super::*;
use crate::point::PointMut;
use crate::ring::Ring;
use alloc::vec;
use alloc::vec::Vec;
use geometry_cs::Cartesian;
use geometry_tag::{LinestringTag, PointTag, PolygonTag, RingTag};
fn accepts_mp<M: MultiPoint>() {}
fn accepts_mls<M: MultiLinestring>() {}
fn accepts_mpg<M: MultiPolygon>() {}
#[derive(Clone)]
struct Xy(f64, f64);
impl Geometry for Xy {
type Kind = PointTag;
type Point = Self;
}
impl Point for Xy {
type Scalar = f64;
type Cs = Cartesian;
const DIM: usize = 2;
fn get<const D: usize>(&self) -> f64 {
if D == 0 { self.0 } else { self.1 }
}
}
impl PointMut for Xy {
fn set<const D: usize>(&mut self, v: f64) {
if D == 0 {
self.0 = v;
} else {
self.1 = v;
}
}
}
struct VMp(Vec<Xy>);
impl Geometry for VMp {
type Kind = MultiPointTag;
type Point = Xy;
}
impl MultiPoint for VMp {
type ItemPoint = Xy;
fn points(&self) -> impl ExactSizeIterator<Item = &Xy> {
self.0.iter()
}
}
#[test]
fn vec_backed_multipoint_satisfies_trait() {
let mp = VMp(vec![Xy(0.0, 0.0), Xy(1.0, 2.0), Xy(3.0, 4.0)]);
accepts_mp::<VMp>();
assert_eq!(mp.points().len(), 3);
let xs: Vec<f64> = mp.points().map(Xy::get::<0>).collect();
assert_eq!(xs, vec![0.0, 1.0, 3.0]);
}
struct VLs(Vec<Xy>);
impl Geometry for VLs {
type Kind = LinestringTag;
type Point = Xy;
}
impl Linestring for VLs {
fn points(&self) -> impl ExactSizeIterator<Item = &Xy> + Clone {
self.0.iter()
}
}
struct VMls(Vec<VLs>);
impl Geometry for VMls {
type Kind = MultiLinestringTag;
type Point = Xy;
}
impl MultiLinestring for VMls {
type ItemLinestring = VLs;
fn linestrings(&self) -> impl ExactSizeIterator<Item = &VLs> {
self.0.iter()
}
}
#[test]
fn vec_backed_multilinestring_satisfies_trait() {
let mls = VMls(vec![
VLs(vec![Xy(0.0, 0.0), Xy(1.0, 1.0)]),
VLs(vec![Xy(2.0, 2.0), Xy(3.0, 3.0), Xy(4.0, 4.0)]),
]);
accepts_mls::<VMls>();
assert_eq!(mls.linestrings().len(), 2);
let counts: Vec<usize> = mls.linestrings().map(|ls| ls.points().count()).collect();
assert_eq!(counts, vec![2, 3]);
}
struct VRing(Vec<Xy>);
impl Geometry for VRing {
type Kind = RingTag;
type Point = Xy;
}
impl Ring for VRing {
fn points(&self) -> impl ExactSizeIterator<Item = &Xy> + Clone {
self.0.iter()
}
}
struct VPoly {
outer: VRing,
inners: Vec<VRing>,
}
impl Geometry for VPoly {
type Kind = PolygonTag;
type Point = Xy;
}
impl Polygon for VPoly {
type Ring = VRing;
fn exterior(&self) -> &VRing {
&self.outer
}
fn interiors(&self) -> impl ExactSizeIterator<Item = &VRing> {
self.inners.iter()
}
}
struct VMpg(Vec<VPoly>);
impl Geometry for VMpg {
type Kind = MultiPolygonTag;
type Point = Xy;
}
impl MultiPolygon for VMpg {
type ItemPolygon = VPoly;
fn polygons(&self) -> impl ExactSizeIterator<Item = &VPoly> {
self.0.iter()
}
}
#[test]
fn vec_backed_multipolygon_satisfies_trait() {
let mpg = VMpg(vec![
VPoly {
outer: VRing(vec![
Xy(0.0, 0.0),
Xy(1.0, 0.0),
Xy(1.0, 1.0),
Xy(0.0, 1.0),
Xy(0.0, 0.0),
]),
inners: vec![],
},
VPoly {
outer: VRing(vec![
Xy(2.0, 2.0),
Xy(3.0, 2.0),
Xy(3.0, 3.0),
Xy(2.0, 3.0),
Xy(2.0, 2.0),
]),
inners: vec![VRing(vec![
Xy(2.25, 2.25),
Xy(2.75, 2.25),
Xy(2.75, 2.75),
Xy(2.25, 2.75),
Xy(2.25, 2.25),
])],
},
]);
accepts_mpg::<VMpg>();
assert_eq!(mpg.polygons().len(), 2);
let inner_counts: Vec<usize> = mpg.polygons().map(|p| p.interiors().count()).collect();
assert_eq!(inner_counts, vec![0, 1]);
let first = mpg.polygons().next().unwrap();
let ext: Vec<(f64, f64)> = first
.exterior()
.points()
.map(|p| (p.get::<0>(), p.get::<1>()))
.collect();
assert_eq!(ext.len(), 5);
assert_eq!(ext[2], (1.0, 1.0));
let hole_len = mpg
.polygons()
.nth(1)
.unwrap()
.interiors()
.next()
.unwrap()
.points()
.count();
assert_eq!(hole_len, 5);
}
#[test]
#[allow(
clippy::float_cmp,
reason = "compared values are exact integer-valued literals"
)]
fn xy_get_set_round_trips_both_ordinates() {
let mut p = Xy(0.0, 0.0);
p.set::<0>(3.0);
p.set::<1>(4.0);
assert_eq!(p.get::<0>(), 3.0);
assert_eq!(p.get::<1>(), 4.0);
}
}