geo/algorithm/
map_coords.rs

1//! # Advanced Example: Fallible Geometry coordinate conversion using `PROJ`
2//!
3#![cfg_attr(feature = "use-proj", doc = "```")]
4#![cfg_attr(not(feature = "use-proj"), doc = "```ignore")]
5//! // activate the [use-proj] feature in cargo.toml in order to access proj functions
6//! use approx::assert_relative_eq;
7//! use geo::{Coord, Point};
8//! use geo::MapCoords;
9//! use proj::{Coord as ProjCoord, Proj, ProjError};
10//! // GeoJSON uses the WGS 84 coordinate system
11//! let from = "EPSG:4326";
12//! // The NAD83 / California zone 6 (ftUS) coordinate system
13//! let to = "EPSG:2230";
14//! let to_feet = Proj::new_known_crs(&from, &to, None).unwrap();
15//! let transform = |c: Coord<f64>| -> Result<_, ProjError> {
16//!     // proj can accept Point, Coord, Tuple, and array values, returning a Result
17//!     let shifted = to_feet.convert(c)?;
18//!     Ok(shifted)
19//! };
20//! // 👽
21//! let usa_m = Point::new(-115.797615, 37.2647978);
22//! let usa_ft = usa_m.try_map_coords(|coord| transform(coord)).unwrap();
23//! assert_relative_eq!(6693625.67217475, usa_ft.x(), epsilon = 1e-6);
24//! assert_relative_eq!(3497301.5918027186, usa_ft.y(), epsilon = 1e-6);
25//! ```
26
27pub use modern::*;
28mod modern {
29    pub(crate) use crate::geometry::*;
30    pub(crate) use crate::CoordNum;
31
32    /// Map a function over all the coordinates in an object, returning a new one
33    pub trait MapCoords<T, NT> {
34        type Output;
35
36        /// Apply a function to all the coordinates in a geometric object, returning a new object.
37        ///
38        /// # Examples
39        ///
40        /// ```
41        /// use geo::MapCoords;
42        /// use geo::{Coord, Point};
43        /// use approx::assert_relative_eq;
44        ///
45        /// let p1 = Point::new(10., 20.);
46        /// let p2 = p1.map_coords(|Coord { x, y }| Coord { x: x + 1000., y: y * 2. });
47        ///
48        /// assert_relative_eq!(p2, Point::new(1010., 40.), epsilon = 1e-6);
49        /// ```
50        ///
51        /// Note that the input and output numeric types need not match.
52        ///
53        /// For example, consider OpenStreetMap's coordinate encoding scheme, which, to save space,
54        /// encodes latitude/longitude as 32bit signed integers from the floating point values
55        /// to six decimal places (eg. lat/lon * 1000000).
56        ///
57        /// ```
58        /// # use geo::{Coord, Point};
59        /// # use geo::MapCoords;
60        /// # use approx::assert_relative_eq;
61        ///
62        /// let SCALE_FACTOR: f64 = 1000000.0;
63        /// let floating_point_geom: Point<f64> = Point::new(10.15f64, 20.05f64);
64        /// let fixed_point_geom: Point<i32> = floating_point_geom.map_coords(|Coord { x, y }| {
65        ///     Coord { x: (x * SCALE_FACTOR) as i32, y: (y * SCALE_FACTOR) as i32 }
66        /// });
67        ///
68        /// assert_eq!(fixed_point_geom.x(), 10150000);
69        /// ```
70        ///
71        /// If you want *only* to convert between numeric types (i32 -> f64) without further
72        /// transformation, consider using [`Convert`](crate::Convert).
73        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output
74        where
75            T: CoordNum,
76            NT: CoordNum;
77
78        /// Map a fallible function over all the coordinates in a geometry, returning a Result
79        ///
80        /// # Examples
81        ///
82        /// ```
83        /// use approx::assert_relative_eq;
84        /// use geo::MapCoords;
85        /// use geo::{Coord, Point};
86        ///
87        /// let p1 = Point::new(10., 20.);
88        /// let p2 = p1
89        ///     .try_map_coords(|Coord { x, y }| -> Result<_, std::convert::Infallible> {
90        ///         Ok(Coord { x: x + 1000., y: y * 2. })
91        ///     }).unwrap();
92        ///
93        /// assert_relative_eq!(p2, Point::new(1010., 40.), epsilon = 1e-6);
94        /// ```
95        ///
96        /// ## Advanced Example: Geometry coordinate conversion using `PROJ`
97        ///
98        #[cfg_attr(feature = "use-proj", doc = "```")]
99        #[cfg_attr(not(feature = "use-proj"), doc = "```ignore")]
100        /// use approx::assert_relative_eq;
101        /// // activate the [use-proj] feature in cargo.toml in order to access proj functions
102        /// use geo::{Coord, Point};
103        /// use geo::map_coords::MapCoords;
104        /// use proj::{Coord as ProjCoord, Proj, ProjError};
105        /// // GeoJSON uses the WGS 84 coordinate system
106        /// let from = "EPSG:4326";
107        /// // The NAD83 / California zone 6 (ftUS) coordinate system
108        /// let to = "EPSG:2230";
109        /// let to_feet = Proj::new_known_crs(&from, &to, None).unwrap();
110        /// let transform = |c: Coord<f64>| -> Result<_, ProjError> {
111        ///     // proj can accept Point, Coord, Tuple, and array values, returning a Result
112        ///     let shifted = to_feet.convert(c)?;
113        ///     Ok(shifted)
114        /// };
115        /// // 👽
116        /// let usa_m = Point::new(-115.797615, 37.2647978);
117        /// let usa_ft = usa_m.try_map_coords(|coord| transform(coord)).unwrap();
118        /// assert_relative_eq!(6693625.67217475, usa_ft.x(), epsilon = 1e-6);
119        /// assert_relative_eq!(3497301.5918027186, usa_ft.y(), epsilon = 1e-6);
120        /// ```
121        fn try_map_coords<E>(
122            &self,
123            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
124        ) -> Result<Self::Output, E>
125        where
126            T: CoordNum,
127            NT: CoordNum;
128    }
129
130    pub trait MapCoordsInPlace<T> {
131        /// Apply a function to all the coordinates in a geometric object, in place
132        ///
133        /// # Examples
134        ///
135        /// ```
136        /// use geo::MapCoordsInPlace;
137        /// use geo::{Coord, Point};
138        /// use approx::assert_relative_eq;
139        ///
140        /// let mut p = Point::new(10., 20.);
141        /// p.map_coords_in_place(|Coord { x, y }| Coord { x: x + 1000., y: y * 2. });
142        ///
143        /// assert_relative_eq!(p, Point::new(1010., 40.), epsilon = 1e-6);
144        /// ```
145        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T> + Copy)
146        where
147            T: CoordNum;
148
149        /// Map a fallible function over all the coordinates in a geometry, in place, returning a `Result`.
150        ///
151        /// Upon encountering an `Err` from the function, `try_map_coords_in_place` immediately returns
152        /// and the geometry is potentially left in a partially mapped state.
153        ///
154        /// # Examples
155        ///
156        /// ```
157        /// use geo::MapCoordsInPlace;
158        /// use geo::Coord;
159        ///
160        /// let mut p1 = geo::point!{x: 10u32, y: 20u32};
161        ///
162        /// p1.try_map_coords_in_place(|Coord { x, y }| -> Result<_, &str> {
163        ///     Ok(Coord {
164        ///         x: x.checked_add(1000).ok_or("Overflow")?,
165        ///         y: y.checked_mul(2).ok_or("Overflow")?,
166        ///     })
167        /// })?;
168        ///
169        /// assert_eq!(
170        ///     p1,
171        ///     geo::point!{x: 1010u32, y: 40u32},
172        /// );
173        /// # Ok::<(), &str>(())
174        /// ```
175        fn try_map_coords_in_place<E>(
176            &mut self,
177            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
178        ) -> Result<(), E>
179        where
180            T: CoordNum;
181    }
182
183    //-----------------------//
184    // Point implementations //
185    //-----------------------//
186
187    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for Point<T> {
188        type Output = Point<NT>;
189
190        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
191            Point(func(self.0))
192        }
193
194        fn try_map_coords<E>(
195            &self,
196            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E>,
197        ) -> Result<Self::Output, E> {
198            Ok(Point(func(self.0)?))
199        }
200    }
201
202    impl<T: CoordNum> MapCoordsInPlace<T> for Point<T> {
203        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T>) {
204            self.0 = func(self.0);
205        }
206
207        fn try_map_coords_in_place<E>(
208            &mut self,
209            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
210        ) -> Result<(), E> {
211            self.0 = func(self.0)?;
212            Ok(())
213        }
214    }
215
216    //----------------------//
217    // Line implementations //
218    //----------------------//
219
220    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for Line<T> {
221        type Output = Line<NT>;
222
223        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
224            Line::new(
225                self.start_point().map_coords(func).0,
226                self.end_point().map_coords(func).0,
227            )
228        }
229
230        fn try_map_coords<E>(
231            &self,
232            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
233        ) -> Result<Self::Output, E> {
234            Ok(Line::new(
235                self.start_point().try_map_coords(func)?.0,
236                self.end_point().try_map_coords(func)?.0,
237            ))
238        }
239    }
240
241    impl<T: CoordNum> MapCoordsInPlace<T> for Line<T> {
242        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T>) {
243            self.start = func(self.start);
244            self.end = func(self.end);
245        }
246
247        fn try_map_coords_in_place<E>(
248            &mut self,
249            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
250        ) -> Result<(), E> {
251            self.start = func(self.start)?;
252            self.end = func(self.end)?;
253
254            Ok(())
255        }
256    }
257
258    //----------------------------//
259    // LineString implementations //
260    //----------------------------//
261
262    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for LineString<T> {
263        type Output = LineString<NT>;
264
265        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
266            LineString::from(
267                self.points()
268                    .map(|p| p.map_coords(func))
269                    .collect::<Vec<_>>(),
270            )
271        }
272
273        fn try_map_coords<E>(
274            &self,
275            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
276        ) -> Result<Self::Output, E> {
277            Ok(LineString::from(
278                self.points()
279                    .map(|p| p.try_map_coords(func))
280                    .collect::<Result<Vec<_>, E>>()?,
281            ))
282        }
283    }
284
285    impl<T: CoordNum> MapCoordsInPlace<T> for LineString<T> {
286        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T>) {
287            for p in &mut self.0 {
288                *p = func(*p);
289            }
290        }
291
292        fn try_map_coords_in_place<E>(
293            &mut self,
294            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
295        ) -> Result<(), E> {
296            for p in &mut self.0 {
297                *p = func(*p)?;
298            }
299            Ok(())
300        }
301    }
302
303    //-------------------------//
304    // Polygon implementations //
305    //-------------------------//
306
307    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for Polygon<T> {
308        type Output = Polygon<NT>;
309
310        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
311            Polygon::new(
312                self.exterior().map_coords(func),
313                self.interiors()
314                    .iter()
315                    .map(|l| l.map_coords(func))
316                    .collect(),
317            )
318        }
319
320        fn try_map_coords<E>(
321            &self,
322            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
323        ) -> Result<Self::Output, E> {
324            Ok(Polygon::new(
325                self.exterior().try_map_coords(func)?,
326                self.interiors()
327                    .iter()
328                    .map(|l| l.try_map_coords(func))
329                    .collect::<Result<Vec<_>, E>>()?,
330            ))
331        }
332    }
333
334    impl<T: CoordNum> MapCoordsInPlace<T> for Polygon<T> {
335        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T> + Copy) {
336            self.exterior_mut(|line_string| {
337                line_string.map_coords_in_place(func);
338            });
339
340            self.interiors_mut(|line_strings| {
341                for line_string in line_strings {
342                    line_string.map_coords_in_place(func);
343                }
344            });
345        }
346
347        fn try_map_coords_in_place<E>(
348            &mut self,
349            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
350        ) -> Result<(), E> {
351            let mut result = Ok(());
352
353            self.exterior_mut(|line_string| {
354                if let Err(e) = line_string.try_map_coords_in_place(&func) {
355                    result = Err(e);
356                }
357            });
358
359            if result.is_ok() {
360                self.interiors_mut(|line_strings| {
361                    for line_string in line_strings {
362                        if let Err(e) = line_string.try_map_coords_in_place(&func) {
363                            result = Err(e);
364                            break;
365                        }
366                    }
367                });
368            }
369
370            result
371        }
372    }
373
374    //----------------------------//
375    // MultiPoint implementations //
376    //----------------------------//
377
378    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for MultiPoint<T> {
379        type Output = MultiPoint<NT>;
380
381        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
382            MultiPoint::new(self.iter().map(|p| p.map_coords(func)).collect())
383        }
384
385        fn try_map_coords<E>(
386            &self,
387            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
388        ) -> Result<Self::Output, E> {
389            Ok(MultiPoint::new(
390                self.0
391                    .iter()
392                    .map(|p| p.try_map_coords(func))
393                    .collect::<Result<Vec<_>, E>>()?,
394            ))
395        }
396    }
397
398    impl<T: CoordNum> MapCoordsInPlace<T> for MultiPoint<T> {
399        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T> + Copy) {
400            for p in &mut self.0 {
401                p.map_coords_in_place(func);
402            }
403        }
404
405        fn try_map_coords_in_place<E>(
406            &mut self,
407            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
408        ) -> Result<(), E> {
409            for p in &mut self.0 {
410                p.try_map_coords_in_place(&func)?;
411            }
412            Ok(())
413        }
414    }
415
416    //---------------------------------//
417    // MultiLineString implementations //
418    //---------------------------------//
419
420    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for MultiLineString<T> {
421        type Output = MultiLineString<NT>;
422
423        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
424            MultiLineString::new(self.iter().map(|l| l.map_coords(func)).collect())
425        }
426
427        fn try_map_coords<E>(
428            &self,
429            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
430        ) -> Result<Self::Output, E> {
431            Ok(MultiLineString::new(
432                self.0
433                    .iter()
434                    .map(|l| l.try_map_coords(func))
435                    .collect::<Result<Vec<_>, E>>()?,
436            ))
437        }
438    }
439
440    impl<T: CoordNum> MapCoordsInPlace<T> for MultiLineString<T> {
441        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T> + Copy) {
442            for p in &mut self.0 {
443                p.map_coords_in_place(func);
444            }
445        }
446
447        fn try_map_coords_in_place<E>(
448            &mut self,
449            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
450        ) -> Result<(), E> {
451            for p in &mut self.0 {
452                p.try_map_coords_in_place(&func)?;
453            }
454            Ok(())
455        }
456    }
457
458    //------------------------------//
459    // MultiPolygon implementations //
460    //------------------------------//
461
462    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for MultiPolygon<T> {
463        type Output = MultiPolygon<NT>;
464
465        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
466            MultiPolygon::new(self.iter().map(|p| p.map_coords(func)).collect())
467        }
468
469        fn try_map_coords<E>(
470            &self,
471            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
472        ) -> Result<Self::Output, E> {
473            Ok(MultiPolygon::new(
474                self.0
475                    .iter()
476                    .map(|p| p.try_map_coords(func))
477                    .collect::<Result<Vec<_>, E>>()?,
478            ))
479        }
480    }
481
482    impl<T: CoordNum> MapCoordsInPlace<T> for MultiPolygon<T> {
483        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T> + Copy) {
484            for p in &mut self.0 {
485                p.map_coords_in_place(func);
486            }
487        }
488
489        fn try_map_coords_in_place<E>(
490            &mut self,
491            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
492        ) -> Result<(), E> {
493            for p in &mut self.0 {
494                p.try_map_coords_in_place(&func)?;
495            }
496            Ok(())
497        }
498    }
499
500    //--------------------------//
501    // Geometry implementations //
502    //--------------------------//
503
504    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for Geometry<T> {
505        type Output = Geometry<NT>;
506
507        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
508            match *self {
509                Geometry::Point(ref x) => Geometry::Point(x.map_coords(func)),
510                Geometry::Line(ref x) => Geometry::Line(x.map_coords(func)),
511                Geometry::LineString(ref x) => Geometry::LineString(x.map_coords(func)),
512                Geometry::Polygon(ref x) => Geometry::Polygon(x.map_coords(func)),
513                Geometry::MultiPoint(ref x) => Geometry::MultiPoint(x.map_coords(func)),
514                Geometry::MultiLineString(ref x) => Geometry::MultiLineString(x.map_coords(func)),
515                Geometry::MultiPolygon(ref x) => Geometry::MultiPolygon(x.map_coords(func)),
516                Geometry::GeometryCollection(ref x) => {
517                    Geometry::GeometryCollection(x.map_coords(func))
518                }
519                Geometry::Rect(ref x) => Geometry::Rect(x.map_coords(func)),
520                Geometry::Triangle(ref x) => Geometry::Triangle(x.map_coords(func)),
521            }
522        }
523
524        fn try_map_coords<E>(
525            &self,
526            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
527        ) -> Result<Self::Output, E> {
528            match *self {
529                Geometry::Point(ref x) => Ok(Geometry::Point(x.try_map_coords(func)?)),
530                Geometry::Line(ref x) => Ok(Geometry::Line(x.try_map_coords(func)?)),
531                Geometry::LineString(ref x) => Ok(Geometry::LineString(x.try_map_coords(func)?)),
532                Geometry::Polygon(ref x) => Ok(Geometry::Polygon(x.try_map_coords(func)?)),
533                Geometry::MultiPoint(ref x) => Ok(Geometry::MultiPoint(x.try_map_coords(func)?)),
534                Geometry::MultiLineString(ref x) => {
535                    Ok(Geometry::MultiLineString(x.try_map_coords(func)?))
536                }
537                Geometry::MultiPolygon(ref x) => {
538                    Ok(Geometry::MultiPolygon(x.try_map_coords(func)?))
539                }
540                Geometry::GeometryCollection(ref x) => {
541                    Ok(Geometry::GeometryCollection(x.try_map_coords(func)?))
542                }
543                Geometry::Rect(ref x) => Ok(Geometry::Rect(x.try_map_coords(func)?)),
544                Geometry::Triangle(ref x) => Ok(Geometry::Triangle(x.try_map_coords(func)?)),
545            }
546        }
547    }
548
549    impl<T: CoordNum> MapCoordsInPlace<T> for Geometry<T> {
550        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T> + Copy) {
551            match *self {
552                Geometry::Point(ref mut x) => x.map_coords_in_place(func),
553                Geometry::Line(ref mut x) => x.map_coords_in_place(func),
554                Geometry::LineString(ref mut x) => x.map_coords_in_place(func),
555                Geometry::Polygon(ref mut x) => x.map_coords_in_place(func),
556                Geometry::MultiPoint(ref mut x) => x.map_coords_in_place(func),
557                Geometry::MultiLineString(ref mut x) => x.map_coords_in_place(func),
558                Geometry::MultiPolygon(ref mut x) => x.map_coords_in_place(func),
559                Geometry::GeometryCollection(ref mut x) => x.map_coords_in_place(func),
560                Geometry::Rect(ref mut x) => x.map_coords_in_place(func),
561                Geometry::Triangle(ref mut x) => x.map_coords_in_place(func),
562            }
563        }
564
565        fn try_map_coords_in_place<E>(
566            &mut self,
567            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
568        ) -> Result<(), E> {
569            match *self {
570                Geometry::Point(ref mut x) => x.try_map_coords_in_place(func),
571                Geometry::Line(ref mut x) => x.try_map_coords_in_place(func),
572                Geometry::LineString(ref mut x) => x.try_map_coords_in_place(func),
573                Geometry::Polygon(ref mut x) => x.try_map_coords_in_place(func),
574                Geometry::MultiPoint(ref mut x) => x.try_map_coords_in_place(func),
575                Geometry::MultiLineString(ref mut x) => x.try_map_coords_in_place(func),
576                Geometry::MultiPolygon(ref mut x) => x.try_map_coords_in_place(func),
577                Geometry::GeometryCollection(ref mut x) => x.try_map_coords_in_place(func),
578                Geometry::Rect(ref mut x) => x.try_map_coords_in_place(func),
579                Geometry::Triangle(ref mut x) => x.try_map_coords_in_place(func),
580            }
581        }
582    }
583
584    //------------------------------------//
585    // GeometryCollection implementations //
586    //------------------------------------//
587
588    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for GeometryCollection<T> {
589        type Output = GeometryCollection<NT>;
590
591        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
592            GeometryCollection::new_from(self.iter().map(|g| g.map_coords(func)).collect())
593        }
594
595        fn try_map_coords<E>(
596            &self,
597            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E> + Copy,
598        ) -> Result<Self::Output, E> {
599            Ok(GeometryCollection::new_from(
600                self.0
601                    .iter()
602                    .map(|g| g.try_map_coords(func))
603                    .collect::<Result<Vec<_>, E>>()?,
604            ))
605        }
606    }
607
608    impl<T: CoordNum> MapCoordsInPlace<T> for GeometryCollection<T> {
609        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T> + Copy) {
610            for p in &mut self.0 {
611                p.map_coords_in_place(func);
612            }
613        }
614
615        fn try_map_coords_in_place<E>(
616            &mut self,
617            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
618        ) -> Result<(), E> {
619            for p in &mut self.0 {
620                p.try_map_coords_in_place(&func)?;
621            }
622            Ok(())
623        }
624    }
625
626    //----------------------//
627    // Rect implementations //
628    //----------------------//
629
630    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for Rect<T> {
631        type Output = Rect<NT>;
632
633        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
634            Rect::new(func(self.min()), func(self.max()))
635        }
636
637        fn try_map_coords<E>(
638            &self,
639            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E>,
640        ) -> Result<Self::Output, E> {
641            Ok(Rect::new(func(self.min())?, func(self.max())?))
642        }
643    }
644
645    impl<T: CoordNum> MapCoordsInPlace<T> for Rect<T> {
646        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T>) {
647            let mut new_rect = Rect::new(func(self.min()), func(self.max()));
648            ::std::mem::swap(self, &mut new_rect);
649        }
650
651        fn try_map_coords_in_place<E>(
652            &mut self,
653            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
654        ) -> Result<(), E> {
655            let mut new_rect = Rect::new(func(self.min())?, func(self.max())?);
656            ::std::mem::swap(self, &mut new_rect);
657            Ok(())
658        }
659    }
660
661    //--------------------------//
662    // Triangle implementations //
663    //--------------------------//
664
665    impl<T: CoordNum, NT: CoordNum> MapCoords<T, NT> for Triangle<T> {
666        type Output = Triangle<NT>;
667
668        fn map_coords(&self, func: impl Fn(Coord<T>) -> Coord<NT> + Copy) -> Self::Output {
669            Triangle::new(func(self.0), func(self.1), func(self.2))
670        }
671
672        fn try_map_coords<E>(
673            &self,
674            func: impl Fn(Coord<T>) -> Result<Coord<NT>, E>,
675        ) -> Result<Self::Output, E> {
676            Ok(Triangle::new(func(self.0)?, func(self.1)?, func(self.2)?))
677        }
678    }
679
680    impl<T: CoordNum> MapCoordsInPlace<T> for Triangle<T> {
681        fn map_coords_in_place(&mut self, func: impl Fn(Coord<T>) -> Coord<T>) {
682            let mut new_triangle = Triangle::new(func(self.0), func(self.1), func(self.2));
683
684            ::std::mem::swap(self, &mut new_triangle);
685        }
686
687        fn try_map_coords_in_place<E>(
688            &mut self,
689            func: impl Fn(Coord<T>) -> Result<Coord<T>, E>,
690        ) -> Result<(), E> {
691            let mut new_triangle = Triangle::new(func(self.0)?, func(self.1)?, func(self.2)?);
692
693            ::std::mem::swap(self, &mut new_triangle);
694
695            Ok(())
696        }
697    }
698}
699pub use deprecated::*;
700pub(crate) mod deprecated {
701    use super::*;
702
703    /// Map a fallible function over all the coordinates in a geometry, returning a Result
704    #[deprecated(
705        since = "0.21.0",
706        note = "use `MapCoords::try_map_coords` which takes a `Coord` instead of an (x,y) tuple"
707    )]
708    pub trait TryMapCoords<T, NT, E> {
709        type Output;
710
711        /// Map a fallible function over all the coordinates in a geometry, returning a Result
712        ///
713        /// # Examples
714        ///
715        /// ```
716        /// use approx::assert_relative_eq;
717        /// #[allow(deprecated)]
718        /// use geo::TryMapCoords;
719        /// use geo::Point;
720        ///
721        /// let p1 = Point::new(10., 20.);
722        /// #[allow(deprecated)]
723        /// let p2 = p1
724        ///     .try_map_coords(|(x, y)| -> Result<_, std::convert::Infallible> {
725        ///         Ok((x + 1000., y * 2.))
726        ///     }).unwrap();
727        ///
728        /// assert_relative_eq!(p2, Point::new(1010., 40.), epsilon = 1e-6);
729        /// ```
730        ///
731        /// ## Advanced Example: Geometry coordinate conversion using `PROJ`
732        ///
733        #[cfg_attr(feature = "use-proj", doc = "```")]
734        #[cfg_attr(not(feature = "use-proj"), doc = "```ignore")]
735        /// use approx::assert_relative_eq;
736        /// // activate the [use-proj] feature in cargo.toml in order to access proj functions
737        /// use geo::{Coord, Point};
738        /// #[allow(deprecated)]
739        /// use geo::TryMapCoords;
740        /// use proj::{Coord as ProjCoord, Proj, ProjError};
741        /// // GeoJSON uses the WGS 84 coordinate system
742        /// let from = "EPSG:4326";
743        /// // The NAD83 / California zone 6 (ftUS) coordinate system
744        /// let to = "EPSG:2230";
745        /// let to_feet = Proj::new_known_crs(&from, &to, None).unwrap();
746        /// let f = |x: f64, y: f64| -> Result<_, ProjError> {
747        ///     // proj can accept Point, Coord, Tuple, and array values, returning a Result
748        ///     let shifted = to_feet.convert((x, y))?;
749        ///     Ok((shifted.x(), shifted.y()))
750        /// };
751        ///
752        /// // 👽
753        /// let usa_m = Point::new(-115.797615, 37.2647978);
754        /// #[allow(deprecated)]
755        /// let usa_ft = usa_m.try_map_coords(|(x, y)| f(x, y)).unwrap();
756        /// assert_relative_eq!(6693625.67217475, usa_ft.x(), epsilon = 1e-6);
757        /// assert_relative_eq!(3497301.5918027186, usa_ft.y(), epsilon = 1e-6);
758        /// ```
759        fn try_map_coords(
760            &self,
761            func: impl Fn((T, T)) -> Result<(NT, NT), E> + Copy,
762        ) -> Result<Self::Output, E>
763        where
764            T: CoordNum,
765            NT: CoordNum;
766    }
767
768    #[deprecated(
769        since = "0.21.0",
770        note = "use `MapCoordsInPlace::try_map_coords_in_place` which takes a `Coord` instead of an (x,y) tuple"
771    )]
772    pub trait TryMapCoordsInplace<T, E> {
773        /// Map a fallible function over all the coordinates in a geometry, in place, returning a `Result`.
774        ///
775        /// Upon encountering an `Err` from the function, `try_map_coords_in_place` immediately returns
776        /// and the geometry is potentially left in a partially mapped state.
777        ///
778        /// # Examples
779        ///
780        /// ```
781        /// #[allow(deprecated)]
782        /// use geo::TryMapCoordsInplace;
783        ///
784        /// let mut p1 = geo::point!{x: 10u32, y: 20u32};
785        ///
786        /// #[allow(deprecated)]
787        /// p1.try_map_coords_inplace(|(x, y)| -> Result<_, &str> {
788        ///     Ok((
789        ///         x.checked_add(1000).ok_or("Overflow")?,
790        ///         y.checked_mul(2).ok_or("Overflow")?,
791        ///     ))
792        /// })?;
793        ///
794        /// assert_eq!(
795        ///     p1,
796        ///     geo::point!{x: 1010u32, y: 40u32},
797        /// );
798        /// # Ok::<(), &str>(())
799        /// ```
800        fn try_map_coords_inplace(
801            &mut self,
802            func: impl Fn((T, T)) -> Result<(T, T), E>,
803        ) -> Result<(), E>
804        where
805            T: CoordNum;
806    }
807
808    /// Map a function over all the coordinates in an object in place
809    #[deprecated(
810        since = "0.21.0",
811        note = "use `MapCoordsInPlace::map_coords_in_place` instead which takes a `Coord` instead of an (x,y) tuple"
812    )]
813    pub trait MapCoordsInplace<T>: MapCoordsInPlace<T> {
814        /// Apply a function to all the coordinates in a geometric object, in place
815        ///
816        /// # Examples
817        ///
818        /// ```
819        /// #[allow(deprecated)]
820        /// use geo::MapCoordsInplace;
821        /// use geo::Point;
822        /// use approx::assert_relative_eq;
823        ///
824        /// let mut p = Point::new(10., 20.);
825        /// #[allow(deprecated)]
826        /// p.map_coords_inplace(|(x, y)| (x + 1000., y * 2.));
827        ///
828        /// assert_relative_eq!(p, Point::new(1010., 40.), epsilon = 1e-6);
829        /// ```
830        fn map_coords_inplace(&mut self, func: impl Fn((T, T)) -> (T, T) + Copy)
831        where
832            T: CoordNum;
833    }
834
835    macro_rules! impl_deprecated_map_coords {
836        ($geom:ident) => {
837            #[allow(deprecated)]
838            impl<T: CoordNum, NT: CoordNum, E> TryMapCoords<T, NT, E> for $geom<T> {
839                type Output = $geom<NT>;
840
841                fn try_map_coords(
842                    &self,
843                    func: impl Fn((T, T)) -> Result<(NT, NT), E> + Copy,
844                ) -> Result<Self::Output, E> {
845                    MapCoords::try_map_coords(self, |c| Ok(Coord::from(func(c.x_y())?)))
846                }
847            }
848
849            #[allow(deprecated)]
850            impl<T: CoordNum, E> TryMapCoordsInplace<T, E> for $geom<T> {
851                fn try_map_coords_inplace(
852                    &mut self,
853                    func: impl Fn((T, T)) -> Result<(T, T), E>,
854                ) -> Result<(), E> {
855                    MapCoordsInPlace::try_map_coords_in_place(self, |c| Ok(func(c.x_y())?.into()))
856                }
857            }
858
859            #[allow(deprecated)]
860            impl<T: CoordNum> MapCoordsInplace<T> for $geom<T> {
861                /// Apply a function to all the coordinates in a geometric object, in place
862                ///
863                /// # Examples
864                ///
865                /// ```
866                /// #[allow(deprecated)]
867                /// use geo::MapCoordsInplace;
868                /// use geo::Point;
869                /// use approx::assert_relative_eq;
870                ///
871                /// let mut p = Point::new(10., 20.);
872                /// #[allow(deprecated)]
873                /// p.map_coords_inplace(|(x, y)| (x + 1000., y * 2.));
874                ///
875                /// assert_relative_eq!(p, Point::new(1010., 40.), epsilon = 1e-6);
876                /// ```
877                fn map_coords_inplace(&mut self, func: impl Fn((T, T)) -> (T, T) + Copy)
878                where
879                    T: CoordNum,
880                {
881                    MapCoordsInPlace::map_coords_in_place(self, |c| func(c.x_y()).into())
882                }
883            }
884        };
885    }
886
887    impl_deprecated_map_coords!(Point);
888    impl_deprecated_map_coords!(Line);
889    impl_deprecated_map_coords!(LineString);
890    impl_deprecated_map_coords!(Polygon);
891    impl_deprecated_map_coords!(MultiPoint);
892    impl_deprecated_map_coords!(MultiLineString);
893    impl_deprecated_map_coords!(MultiPolygon);
894    impl_deprecated_map_coords!(Geometry);
895    impl_deprecated_map_coords!(GeometryCollection);
896    impl_deprecated_map_coords!(Triangle);
897    impl_deprecated_map_coords!(Rect);
898}
899
900#[cfg(test)]
901mod test {
902    use super::{MapCoords, MapCoordsInPlace};
903    use crate::{
904        coord, polygon, Coord, Geometry, GeometryCollection, Line, LineString, MultiLineString,
905        MultiPoint, MultiPolygon, Point, Polygon, Rect,
906    };
907
908    #[test]
909    fn point() {
910        let p = Point::new(10., 10.);
911        let new_p = p.map_coords(|Coord { x, y }| (x + 10., y + 100.).into());
912        assert_relative_eq!(new_p.x(), 20.);
913        assert_relative_eq!(new_p.y(), 110.);
914    }
915
916    #[test]
917    fn point_inplace() {
918        let mut p2 = Point::new(10f32, 10f32);
919        p2.map_coords_in_place(|Coord { x, y }| (x + 10., y + 100.).into());
920        assert_relative_eq!(p2.x(), 20.);
921        assert_relative_eq!(p2.y(), 110.);
922    }
923
924    #[test]
925    fn rect_inplace() {
926        let mut rect = Rect::new((10, 10), (20, 20));
927        rect.map_coords_in_place(|Coord { x, y }| (x + 10, y + 20).into());
928        assert_eq!(rect.min(), coord! { x: 20, y: 30 });
929        assert_eq!(rect.max(), coord! { x: 30, y: 40 });
930    }
931
932    #[test]
933    fn rect_inplace_normalized() {
934        let mut rect = Rect::new((2, 2), (3, 3));
935        // Rect's enforce that rect.min is up and left of p2.  Here we test that the points are
936        // normalized into a valid rect, regardless of the order they are mapped.
937        rect.map_coords_in_place(|pt| {
938            match pt.x_y() {
939                // old min point maps to new max point
940                (2, 2) => (4, 4).into(),
941                // old max point maps to new min point
942                (3, 3) => (1, 1).into(),
943                _ => panic!("unexpected point"),
944            }
945        });
946
947        assert_eq!(rect.min(), coord! { x: 1, y: 1 });
948        assert_eq!(rect.max(), coord! { x: 4, y: 4 });
949    }
950
951    #[test]
952    fn rect_map_coords() {
953        let rect = Rect::new((10, 10), (20, 20));
954        let another_rect = rect.map_coords(|Coord { x, y }| (x + 10, y + 20).into());
955        assert_eq!(another_rect.min(), coord! { x: 20, y: 30 });
956        assert_eq!(another_rect.max(), coord! { x: 30, y: 40 });
957    }
958
959    #[test]
960    fn rect_try_map_coords() {
961        let rect = Rect::new((10i32, 10), (20, 20));
962        let result = rect.try_map_coords(|Coord { x, y }| -> Result<_, &'static str> {
963            Ok((
964                x.checked_add(10).ok_or("overflow")?,
965                y.checked_add(20).ok_or("overflow")?,
966            )
967                .into())
968        });
969        assert!(result.is_ok());
970    }
971
972    #[test]
973    fn rect_try_map_coords_normalized() {
974        let rect = Rect::new((2, 2), (3, 3));
975        // Rect's enforce that rect.min is up and left of p2.  Here we test that the points are
976        // normalized into a valid rect, regardless of the order they are mapped.
977        let result: Result<_, std::convert::Infallible> = rect.try_map_coords(|pt| {
978            match pt.x_y() {
979                // old min point maps to new max point
980                (2, 2) => Ok((4, 4).into()),
981                // old max point maps to new min point
982                (3, 3) => Ok((1, 1).into()),
983                _ => panic!("unexpected point"),
984            }
985        });
986        let new_rect = result.unwrap();
987        assert_eq!(new_rect.min(), coord! { x: 1, y: 1 });
988        assert_eq!(new_rect.max(), coord! { x: 4, y: 4 });
989    }
990
991    #[test]
992    fn line() {
993        let line = Line::from([(0., 0.), (1., 2.)]);
994        assert_relative_eq!(
995            line.map_coords(|Coord { x, y }| (x * 2., y).into()),
996            Line::from([(0., 0.), (2., 2.)]),
997            epsilon = 1e-6
998        );
999    }
1000
1001    #[test]
1002    fn linestring() {
1003        let line1: LineString<f32> = LineString::from(vec![(0., 0.), (1., 2.)]);
1004        let line2 = line1.map_coords(|Coord { x, y }| (x + 10., y - 100.).into());
1005        assert_relative_eq!(line2.0[0], Coord::from((10., -100.)), epsilon = 1e-6);
1006        assert_relative_eq!(line2.0[1], Coord::from((11., -98.)), epsilon = 1e-6);
1007    }
1008
1009    #[test]
1010    fn polygon() {
1011        let exterior = LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.)]);
1012        let interiors = vec![LineString::from(vec![
1013            (0.1, 0.1),
1014            (0.9, 0.9),
1015            (0.9, 0.1),
1016            (0.1, 0.1),
1017        ])];
1018        let p = Polygon::new(exterior, interiors);
1019
1020        let p2 = p.map_coords(|Coord { x, y }| (x + 10., y - 100.).into());
1021
1022        let exterior2 =
1023            LineString::from(vec![(10., -100.), (11., -99.), (11., -100.), (10., -100.)]);
1024        let interiors2 = vec![LineString::from(vec![
1025            (10.1, -99.9),
1026            (10.9, -99.1),
1027            (10.9, -99.9),
1028            (10.1, -99.9),
1029        ])];
1030        let expected_p2 = Polygon::new(exterior2, interiors2);
1031
1032        assert_relative_eq!(p2, expected_p2, epsilon = 1e-6);
1033    }
1034
1035    #[test]
1036    fn multipoint() {
1037        let p1 = Point::new(10., 10.);
1038        let p2 = Point::new(0., -100.);
1039        let mp = MultiPoint::new(vec![p1, p2]);
1040
1041        assert_eq!(
1042            mp.map_coords(|Coord { x, y }| (x + 10., y + 100.).into()),
1043            MultiPoint::new(vec![Point::new(20., 110.), Point::new(10., 0.)])
1044        );
1045    }
1046
1047    #[test]
1048    fn multilinestring() {
1049        let line1: LineString<f32> = LineString::from(vec![(0., 0.), (1., 2.)]);
1050        let line2: LineString<f32> = LineString::from(vec![(-1., 0.), (0., 0.), (1., 2.)]);
1051        let mline = MultiLineString::new(vec![line1, line2]);
1052        let mline2 = mline.map_coords(|Coord { x, y }| (x + 10., y - 100.).into());
1053        assert_relative_eq!(
1054            mline2,
1055            MultiLineString::new(vec![
1056                LineString::from(vec![(10., -100.), (11., -98.)]),
1057                LineString::from(vec![(9., -100.), (10., -100.), (11., -98.)]),
1058            ]),
1059            epsilon = 1e-6
1060        );
1061    }
1062
1063    #[test]
1064    fn multipolygon() {
1065        let poly1 = polygon![
1066            (x: 0., y: 0.),
1067            (x: 10., y: 0.),
1068            (x: 10., y: 10.),
1069            (x: 0., y: 10.),
1070            (x: 0., y: 0.),
1071        ];
1072        let poly2 = polygon![
1073            exterior: [
1074                (x: 11., y: 11.),
1075                (x: 20., y: 11.),
1076                (x: 20., y: 20.),
1077                (x: 11., y: 20.),
1078                (x: 11., y: 11.),
1079            ],
1080            interiors: [
1081                [
1082                    (x: 13., y: 13.),
1083                    (x: 13., y: 17.),
1084                    (x: 17., y: 17.),
1085                    (x: 17., y: 13.),
1086                    (x: 13., y: 13.),
1087                ]
1088            ],
1089        ];
1090
1091        let mp = MultiPolygon::new(vec![poly1, poly2]);
1092        let mp2 = mp.map_coords(|Coord { x, y }| (x * 2., y + 100.).into());
1093        assert_eq!(mp2.0.len(), 2);
1094        assert_relative_eq!(
1095            mp2.0[0],
1096            polygon![
1097                (x: 0., y: 100.),
1098                (x: 20., y: 100.),
1099                (x: 20., y: 110.),
1100                (x: 0., y: 110.),
1101                (x: 0., y: 100.),
1102            ],
1103        );
1104        assert_relative_eq!(
1105            mp2.0[1],
1106            polygon![
1107                exterior: [
1108                    (x: 22., y: 111.),
1109                    (x: 40., y: 111.),
1110                    (x: 40., y: 120.),
1111                    (x: 22., y: 120.),
1112                    (x: 22., y: 111.),
1113                ],
1114                interiors: [
1115                    [
1116                        (x: 26., y: 113.),
1117                        (x: 26., y: 117.),
1118                        (x: 34., y: 117.),
1119                        (x: 34., y: 113.),
1120                        (x: 26., y: 113.),
1121                    ],
1122                ],
1123            ],
1124        );
1125    }
1126
1127    #[test]
1128    fn geometrycollection() {
1129        let p1 = Geometry::Point(Point::new(10., 10.));
1130        let line1 = Geometry::LineString(LineString::from(vec![(0., 0.), (1., 2.)]));
1131
1132        let gc = GeometryCollection::new_from(vec![p1, line1]);
1133
1134        assert_eq!(
1135            gc.map_coords(|Coord { x, y }| (x + 10., y + 100.).into()),
1136            GeometryCollection::new_from(vec![
1137                Geometry::Point(Point::new(20., 110.)),
1138                Geometry::LineString(LineString::from(vec![(10., 100.), (11., 102.)])),
1139            ])
1140        );
1141    }
1142
1143    #[test]
1144    fn convert_type() {
1145        let p1: Point<f64> = Point::new(1., 2.);
1146        let p2: Point<f32> = p1.map_coords(|Coord { x, y }| (x as f32, y as f32).into());
1147        assert_relative_eq!(p2.x(), 1f32);
1148        assert_relative_eq!(p2.y(), 2f32);
1149    }
1150
1151    #[cfg(feature = "use-proj")]
1152    #[test]
1153    fn test_fallible_proj() {
1154        use proj::{Proj, ProjError};
1155        let from = "EPSG:4326";
1156        let to = "EPSG:2230";
1157        let to_feet = Proj::new_known_crs(from, to, None).unwrap();
1158
1159        let f = |c| -> Result<_, ProjError> {
1160            let shifted = to_feet.convert(c)?;
1161            Ok(shifted)
1162        };
1163        // 👽
1164        let usa_m = Point::new(-115.797615, 37.2647978);
1165        let usa_ft = usa_m.try_map_coords(f).unwrap();
1166        assert_relative_eq!(6693625.67217475, usa_ft.x(), epsilon = 1e-6);
1167        assert_relative_eq!(3497301.5918027186, usa_ft.y(), epsilon = 1e-6);
1168    }
1169
1170    #[test]
1171    fn test_fallible() {
1172        let f = |Coord { x, y }| -> Result<_, &'static str> {
1173            if relative_ne!(x, 2.0) {
1174                Ok((x * 2., y + 100.).into())
1175            } else {
1176                Err("Ugh")
1177            }
1178        };
1179        // this should produce an error
1180        let bad_ls: LineString<_> = vec![
1181            Point::new(1.0, 1.0),
1182            Point::new(2.0, 2.0),
1183            Point::new(3.0, 3.0),
1184        ]
1185        .into();
1186        // this should be fine
1187        let good_ls: LineString<_> = vec![
1188            Point::new(1.0, 1.0),
1189            Point::new(2.1, 2.0),
1190            Point::new(3.0, 3.0),
1191        ]
1192        .into();
1193        let bad = bad_ls.try_map_coords(f);
1194        assert!(bad.is_err());
1195        let good = good_ls.try_map_coords(f);
1196        assert!(good.is_ok());
1197        assert_relative_eq!(
1198            good.unwrap(),
1199            vec![
1200                Point::new(2., 101.),
1201                Point::new(4.2, 102.),
1202                Point::new(6.0, 103.),
1203            ]
1204            .into()
1205        );
1206    }
1207
1208    #[test]
1209    fn rect_map_invert_coords() {
1210        let rect = Rect::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 1. });
1211
1212        // This call should not panic even though Rect::new
1213        // constructor panics if min coords > max coords
1214        rect.map_coords(|Coord { x, y }| (-x, -y).into());
1215    }
1216}