1use geometry_model::{
4 Box as ModelBox, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point as ModelPoint,
5 Polygon, Ring, Segment,
6};
7use geometry_trait::{Geometry, Point as PointTrait};
8
9pub fn map_coords<G, Q, F>(geometry: &G, mut map: F) -> G::Output
14where
15 G: MapCoords<Q>,
16 Q: PointTrait,
17 F: FnMut(&G::Point) -> Q,
18{
19 geometry.map_coords(&mut map)
20}
21
22#[doc(hidden)]
24pub trait MapCoords<Q: PointTrait>: Geometry {
25 type Output;
27
28 fn map_coords<F>(&self, map: &mut F) -> Self::Output
30 where
31 F: FnMut(&Self::Point) -> Q;
32}
33
34impl<A, const D: usize, Cs, Q> MapCoords<Q> for ModelPoint<A, D, Cs>
35where
36 A: geometry_coords::CoordinateScalar,
37 Cs: geometry_cs::CoordinateSystem,
38 Q: PointTrait,
39{
40 type Output = Q;
41
42 fn map_coords<F>(&self, map: &mut F) -> Self::Output
43 where
44 F: FnMut(&Self::Point) -> Q,
45 {
46 map(self)
47 }
48}
49
50impl<P, Q> MapCoords<Q> for Linestring<P>
51where
52 P: PointTrait,
53 Q: PointTrait,
54{
55 type Output = Linestring<Q>;
56
57 fn map_coords<F>(&self, map: &mut F) -> Self::Output
58 where
59 F: FnMut(&Self::Point) -> Q,
60 {
61 Linestring::from_vec(self.0.iter().map(map).collect())
62 }
63}
64
65impl<P, Q, const CW: bool, const CL: bool> MapCoords<Q> for Ring<P, CW, CL>
66where
67 P: PointTrait,
68 Q: PointTrait,
69{
70 type Output = Ring<Q, CW, CL>;
71
72 fn map_coords<F>(&self, map: &mut F) -> Self::Output
73 where
74 F: FnMut(&Self::Point) -> Q,
75 {
76 Ring::from_vec(self.0.iter().map(map).collect())
77 }
78}
79
80impl<P, Q, const CW: bool, const CL: bool> MapCoords<Q> for Polygon<P, CW, CL>
81where
82 P: PointTrait,
83 Q: PointTrait,
84{
85 type Output = Polygon<Q, CW, CL>;
86
87 fn map_coords<F>(&self, map: &mut F) -> Self::Output
88 where
89 F: FnMut(&Self::Point) -> Q,
90 {
91 Polygon::with_inners(
92 Ring::from_vec(self.outer.0.iter().map(&mut *map).collect()),
93 self.inners
94 .iter()
95 .map(|ring| Ring::from_vec(ring.0.iter().map(&mut *map).collect()))
96 .collect(),
97 )
98 }
99}
100
101impl<P, Q> MapCoords<Q> for MultiPoint<P>
102where
103 P: PointTrait,
104 Q: PointTrait,
105{
106 type Output = MultiPoint<Q>;
107
108 fn map_coords<F>(&self, map: &mut F) -> Self::Output
109 where
110 F: FnMut(&Self::Point) -> Q,
111 {
112 MultiPoint::from_vec(self.0.iter().map(map).collect())
113 }
114}
115
116impl<P, Q> MapCoords<Q> for MultiLinestring<Linestring<P>>
117where
118 P: PointTrait,
119 Q: PointTrait,
120{
121 type Output = MultiLinestring<Linestring<Q>>;
122
123 fn map_coords<F>(&self, map: &mut F) -> Self::Output
124 where
125 F: FnMut(&Self::Point) -> Q,
126 {
127 MultiLinestring::from_vec(
128 self.0
129 .iter()
130 .map(|line| Linestring::from_vec(line.0.iter().map(&mut *map).collect()))
131 .collect(),
132 )
133 }
134}
135
136impl<P, Q, const CW: bool, const CL: bool> MapCoords<Q> for MultiPolygon<Polygon<P, CW, CL>>
137where
138 P: PointTrait,
139 Q: PointTrait,
140{
141 type Output = MultiPolygon<Polygon<Q, CW, CL>>;
142
143 fn map_coords<F>(&self, map: &mut F) -> Self::Output
144 where
145 F: FnMut(&Self::Point) -> Q,
146 {
147 MultiPolygon::from_vec(
148 self.0
149 .iter()
150 .map(|polygon| polygon.map_coords(&mut *map))
151 .collect(),
152 )
153 }
154}
155
156impl<P, Q> MapCoords<Q> for ModelBox<P>
157where
158 P: PointTrait,
159 Q: PointTrait,
160{
161 type Output = ModelBox<Q>;
162
163 fn map_coords<F>(&self, map: &mut F) -> Self::Output
164 where
165 F: FnMut(&Self::Point) -> Q,
166 {
167 ModelBox::from_corners(map(self.min()), map(self.max()))
168 }
169}
170
171impl<P, Q> MapCoords<Q> for Segment<P>
172where
173 P: PointTrait,
174 Q: PointTrait,
175{
176 type Output = Segment<Q>;
177
178 fn map_coords<F>(&self, map: &mut F) -> Self::Output
179 where
180 F: FnMut(&Self::Point) -> Q,
181 {
182 Segment::new(map(self.start()), map(self.end()))
183 }
184}
185
186pub fn map_coords_in_place<G, F>(geometry: &mut G, mut map: F)
188where
189 G: MapCoordsInPlace,
190 F: FnMut(&mut G::Point),
191{
192 geometry.map_coords_in_place(&mut map);
193}
194
195#[doc(hidden)]
197pub trait MapCoordsInPlace: Geometry {
198 fn map_coords_in_place<F>(&mut self, map: &mut F)
200 where
201 F: FnMut(&mut Self::Point);
202}
203
204impl<A, const D: usize, Cs> MapCoordsInPlace for ModelPoint<A, D, Cs>
205where
206 A: geometry_coords::CoordinateScalar,
207 Cs: geometry_cs::CoordinateSystem,
208{
209 fn map_coords_in_place<F>(&mut self, map: &mut F)
210 where
211 F: FnMut(&mut Self::Point),
212 {
213 map(self);
214 }
215}
216
217impl<P: PointTrait> MapCoordsInPlace for Linestring<P> {
218 fn map_coords_in_place<F>(&mut self, map: &mut F)
219 where
220 F: FnMut(&mut Self::Point),
221 {
222 self.0.iter_mut().for_each(map);
223 }
224}
225
226impl<P: PointTrait, const CW: bool, const CL: bool> MapCoordsInPlace for Ring<P, CW, CL> {
227 fn map_coords_in_place<F>(&mut self, map: &mut F)
228 where
229 F: FnMut(&mut Self::Point),
230 {
231 self.0.iter_mut().for_each(map);
232 }
233}
234
235impl<P: PointTrait, const CW: bool, const CL: bool> MapCoordsInPlace for Polygon<P, CW, CL> {
236 fn map_coords_in_place<F>(&mut self, map: &mut F)
237 where
238 F: FnMut(&mut Self::Point),
239 {
240 self.outer.0.iter_mut().for_each(&mut *map);
241 for ring in &mut self.inners {
242 ring.0.iter_mut().for_each(&mut *map);
243 }
244 }
245}
246
247impl<P: PointTrait> MapCoordsInPlace for MultiPoint<P> {
248 fn map_coords_in_place<F>(&mut self, map: &mut F)
249 where
250 F: FnMut(&mut Self::Point),
251 {
252 self.0.iter_mut().for_each(map);
253 }
254}
255
256impl<P: PointTrait> MapCoordsInPlace for MultiLinestring<Linestring<P>> {
257 fn map_coords_in_place<F>(&mut self, map: &mut F)
258 where
259 F: FnMut(&mut Self::Point),
260 {
261 for line in &mut self.0 {
262 line.0.iter_mut().for_each(&mut *map);
263 }
264 }
265}
266
267impl<P: PointTrait, const CW: bool, const CL: bool> MapCoordsInPlace
268 for MultiPolygon<Polygon<P, CW, CL>>
269{
270 fn map_coords_in_place<F>(&mut self, map: &mut F)
271 where
272 F: FnMut(&mut Self::Point),
273 {
274 for polygon in &mut self.0 {
275 polygon.map_coords_in_place(&mut *map);
276 }
277 }
278}
279
280impl<P> MapCoordsInPlace for ModelBox<P>
281where
282 P: PointTrait + Clone,
283{
284 fn map_coords_in_place<F>(&mut self, map: &mut F)
285 where
286 F: FnMut(&mut Self::Point),
287 {
288 let mut min = self.min().clone();
289 let mut max = self.max().clone();
290 map(&mut min);
291 map(&mut max);
292 *self = ModelBox::from_corners(min, max);
293 }
294}
295
296impl<P> MapCoordsInPlace for Segment<P>
297where
298 P: PointTrait + Clone,
299{
300 fn map_coords_in_place<F>(&mut self, map: &mut F)
301 where
302 F: FnMut(&mut Self::Point),
303 {
304 let mut start = self.start().clone();
305 let mut end = self.end().clone();
306 map(&mut start);
307 map(&mut end);
308 *self = Segment::new(start, end);
309 }
310}
311
312#[cfg(test)]
313mod tests {
314 use geometry_cs::Cartesian;
315 use geometry_model::{Linestring, Point2D};
316 use geometry_trait::{Point as _, PointMut as _};
317
318 use super::{map_coords, map_coords_in_place};
319
320 #[test]
321 #[allow(
322 clippy::cast_possible_truncation,
323 reason = "small exact fixtures intentionally exercise scalar rebinding"
324 )]
325 fn linestring_rebinds_and_mutates() {
326 let line = Linestring::from_vec(alloc::vec![
327 Point2D::<f64, Cartesian>::new(1.0, 2.0),
328 Point2D::new(3.0, 4.0),
329 ]);
330 let mapped: Linestring<Point2D<f32, Cartesian>> = map_coords(&line, |point| {
331 Point2D::new(point.get::<0>() as f32, point.get::<1>() as f32)
332 });
333 assert_eq!(mapped.0[0], Point2D::new(1.0, 2.0));
334
335 let mut shifted = line;
336 map_coords_in_place(&mut shifted, |point| {
337 point.set::<0>(point.get::<0>() + 1.0);
338 });
339 assert_eq!(shifted.0[0], Point2D::new(2.0, 2.0));
340 }
341}