use geometry_model::{
Box as ModelBox, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point as ModelPoint,
Polygon, Ring, Segment,
};
use geometry_trait::{Geometry, Point as PointTrait};
pub fn map_coords<G, Q, F>(geometry: &G, mut map: F) -> G::Output
where
G: MapCoords<Q>,
Q: PointTrait,
F: FnMut(&G::Point) -> Q,
{
geometry.map_coords(&mut map)
}
#[doc(hidden)]
pub trait MapCoords<Q: PointTrait>: Geometry {
type Output;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q;
}
impl<A, const D: usize, Cs, Q> MapCoords<Q> for ModelPoint<A, D, Cs>
where
A: geometry_coords::CoordinateScalar,
Cs: geometry_cs::CoordinateSystem,
Q: PointTrait,
{
type Output = Q;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
map(self)
}
}
impl<P, Q> MapCoords<Q> for Linestring<P>
where
P: PointTrait,
Q: PointTrait,
{
type Output = Linestring<Q>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
Linestring::from_vec(self.0.iter().map(map).collect())
}
}
impl<P, Q, const CW: bool, const CL: bool> MapCoords<Q> for Ring<P, CW, CL>
where
P: PointTrait,
Q: PointTrait,
{
type Output = Ring<Q, CW, CL>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
Ring::from_vec(self.0.iter().map(map).collect())
}
}
impl<P, Q, const CW: bool, const CL: bool> MapCoords<Q> for Polygon<P, CW, CL>
where
P: PointTrait,
Q: PointTrait,
{
type Output = Polygon<Q, CW, CL>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
Polygon::with_inners(
Ring::from_vec(self.outer.0.iter().map(&mut *map).collect()),
self.inners
.iter()
.map(|ring| Ring::from_vec(ring.0.iter().map(&mut *map).collect()))
.collect(),
)
}
}
impl<P, Q> MapCoords<Q> for MultiPoint<P>
where
P: PointTrait,
Q: PointTrait,
{
type Output = MultiPoint<Q>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
MultiPoint::from_vec(self.0.iter().map(map).collect())
}
}
impl<P, Q> MapCoords<Q> for MultiLinestring<Linestring<P>>
where
P: PointTrait,
Q: PointTrait,
{
type Output = MultiLinestring<Linestring<Q>>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
MultiLinestring::from_vec(
self.0
.iter()
.map(|line| Linestring::from_vec(line.0.iter().map(&mut *map).collect()))
.collect(),
)
}
}
impl<P, Q, const CW: bool, const CL: bool> MapCoords<Q> for MultiPolygon<Polygon<P, CW, CL>>
where
P: PointTrait,
Q: PointTrait,
{
type Output = MultiPolygon<Polygon<Q, CW, CL>>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
MultiPolygon::from_vec(
self.0
.iter()
.map(|polygon| polygon.map_coords(&mut *map))
.collect(),
)
}
}
impl<P, Q> MapCoords<Q> for ModelBox<P>
where
P: PointTrait,
Q: PointTrait,
{
type Output = ModelBox<Q>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
ModelBox::from_corners(map(self.min()), map(self.max()))
}
}
impl<P, Q> MapCoords<Q> for Segment<P>
where
P: PointTrait,
Q: PointTrait,
{
type Output = Segment<Q>;
fn map_coords<F>(&self, map: &mut F) -> Self::Output
where
F: FnMut(&Self::Point) -> Q,
{
Segment::new(map(self.start()), map(self.end()))
}
}
pub fn map_coords_in_place<G, F>(geometry: &mut G, mut map: F)
where
G: MapCoordsInPlace,
F: FnMut(&mut G::Point),
{
geometry.map_coords_in_place(&mut map);
}
#[doc(hidden)]
pub trait MapCoordsInPlace: Geometry {
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point);
}
impl<A, const D: usize, Cs> MapCoordsInPlace for ModelPoint<A, D, Cs>
where
A: geometry_coords::CoordinateScalar,
Cs: geometry_cs::CoordinateSystem,
{
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
map(self);
}
}
impl<P: PointTrait> MapCoordsInPlace for Linestring<P> {
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
self.0.iter_mut().for_each(map);
}
}
impl<P: PointTrait, const CW: bool, const CL: bool> MapCoordsInPlace for Ring<P, CW, CL> {
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
self.0.iter_mut().for_each(map);
}
}
impl<P: PointTrait, const CW: bool, const CL: bool> MapCoordsInPlace for Polygon<P, CW, CL> {
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
self.outer.0.iter_mut().for_each(&mut *map);
for ring in &mut self.inners {
ring.0.iter_mut().for_each(&mut *map);
}
}
}
impl<P: PointTrait> MapCoordsInPlace for MultiPoint<P> {
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
self.0.iter_mut().for_each(map);
}
}
impl<P: PointTrait> MapCoordsInPlace for MultiLinestring<Linestring<P>> {
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
for line in &mut self.0 {
line.0.iter_mut().for_each(&mut *map);
}
}
}
impl<P: PointTrait, const CW: bool, const CL: bool> MapCoordsInPlace
for MultiPolygon<Polygon<P, CW, CL>>
{
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
for polygon in &mut self.0 {
polygon.map_coords_in_place(&mut *map);
}
}
}
impl<P> MapCoordsInPlace for ModelBox<P>
where
P: PointTrait + Clone,
{
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
let mut min = self.min().clone();
let mut max = self.max().clone();
map(&mut min);
map(&mut max);
*self = ModelBox::from_corners(min, max);
}
}
impl<P> MapCoordsInPlace for Segment<P>
where
P: PointTrait + Clone,
{
fn map_coords_in_place<F>(&mut self, map: &mut F)
where
F: FnMut(&mut Self::Point),
{
let mut start = self.start().clone();
let mut end = self.end().clone();
map(&mut start);
map(&mut end);
*self = Segment::new(start, end);
}
}
#[cfg(test)]
mod tests {
use geometry_cs::Cartesian;
use geometry_model::{Linestring, Point2D};
use geometry_trait::{Point as _, PointMut as _};
use super::{map_coords, map_coords_in_place};
#[test]
#[allow(
clippy::cast_possible_truncation,
reason = "small exact fixtures intentionally exercise scalar rebinding"
)]
fn linestring_rebinds_and_mutates() {
let line = Linestring::from_vec(alloc::vec![
Point2D::<f64, Cartesian>::new(1.0, 2.0),
Point2D::new(3.0, 4.0),
]);
let mapped: Linestring<Point2D<f32, Cartesian>> = map_coords(&line, |point| {
Point2D::new(point.get::<0>() as f32, point.get::<1>() as f32)
});
assert_eq!(mapped.0[0], Point2D::new(1.0, 2.0));
let mut shifted = line;
map_coords_in_place(&mut shifted, |point| {
point.set::<0>(point.get::<0>() + 1.0);
});
assert_eq!(shifted.0[0], Point2D::new(2.0, 2.0));
}
}