geo/algorithm/
translate.rs

1use crate::{AffineOps, AffineTransform, CoordNum};
2
3pub trait Translate<T: CoordNum> {
4    /// Translate a Geometry along its axes by the given offsets
5    ///
6    /// ## Performance
7    ///
8    /// If you will be performing multiple transformations, like [`Scale`](crate::Scale),
9    /// [`Skew`](crate::Skew), [`Translate`](crate::Translate), or [`Rotate`](crate::Rotate), it is more
10    /// efficient to compose the transformations and apply them as a single operation using the
11    /// [`AffineOps`](crate::AffineOps) trait.
12    ///
13    /// # Examples
14    ///
15    /// ```
16    /// use geo::Translate;
17    /// use geo::line_string;
18    ///
19    /// let ls = line_string![
20    ///     (x: 0.0, y: 0.0),
21    ///     (x: 5.0, y: 5.0),
22    ///     (x: 10.0, y: 10.0),
23    /// ];
24    ///
25    /// let translated = ls.translate(1.5, 3.5);
26    ///
27    /// assert_eq!(translated, line_string![
28    ///     (x: 1.5, y: 3.5),
29    ///     (x: 6.5, y: 8.5),
30    ///     (x: 11.5, y: 13.5),
31    /// ]);
32    /// ```
33    #[must_use]
34    fn translate(&self, x_offset: T, y_offset: T) -> Self;
35
36    /// Translate a Geometry along its axes, but in place.
37    fn translate_mut(&mut self, x_offset: T, y_offset: T);
38
39    #[doc(hidden)]
40    /// Translate a Geometry along its axes, but in place.
41    #[deprecated(since = "0.23.0", note = "renamed to `translate_mut`")]
42    fn translate_in_place(&mut self, x_offset: T, y_offset: T);
43
44    #[doc(hidden)]
45    #[deprecated(since = "0.21.0", note = "renamed to `translate_mut`")]
46    fn translate_inplace(&mut self, x_offset: T, y_offset: T);
47}
48
49impl<T, G> Translate<T> for G
50where
51    T: CoordNum,
52    G: AffineOps<T>,
53{
54    fn translate(&self, x_offset: T, y_offset: T) -> Self {
55        let transform = AffineTransform::translate(x_offset, y_offset);
56        self.affine_transform(&transform)
57    }
58
59    fn translate_mut(&mut self, x_offset: T, y_offset: T) {
60        let transform = AffineTransform::translate(x_offset, y_offset);
61        self.affine_transform_mut(&transform)
62    }
63
64    fn translate_in_place(&mut self, x_offset: T, y_offset: T) {
65        self.translate_mut(x_offset, y_offset)
66    }
67
68    fn translate_inplace(&mut self, x_offset: T, y_offset: T) {
69        self.translate_mut(x_offset, y_offset)
70    }
71}
72
73#[cfg(test)]
74mod test {
75    use super::*;
76    use crate::{line_string, point, polygon, Coord, LineString, Polygon};
77
78    #[test]
79    fn test_translate_point() {
80        let p = point!(x: 1.0, y: 5.0);
81        let translated = p.translate(30.0, 20.0);
82        assert_eq!(translated, point!(x: 31.0, y: 25.0));
83    }
84    #[test]
85    fn test_translate_point_in_place() {
86        let mut p = point!(x: 1.0, y: 5.0);
87        p.translate_mut(30.0, 20.0);
88        assert_eq!(p, point!(x: 31.0, y: 25.0));
89    }
90    #[test]
91    fn test_translate_linestring() {
92        let linestring = line_string![
93            (x: 0.0, y: 0.0),
94            (x: 5.0, y: 1.0),
95            (x: 10.0, y: 0.0),
96        ];
97        let translated = linestring.translate(17.0, 18.0);
98        assert_eq!(
99            translated,
100            line_string![
101                (x: 17.0, y: 18.0),
102                (x: 22.0, y: 19.0),
103                (x: 27., y: 18.),
104            ]
105        );
106    }
107    #[test]
108    fn test_translate_polygon() {
109        let poly1 = polygon![
110            (x: 5., y: 1.),
111            (x: 4., y: 2.),
112            (x: 4., y: 3.),
113            (x: 5., y: 4.),
114            (x: 6., y: 4.),
115            (x: 7., y: 3.),
116            (x: 7., y: 2.),
117            (x: 6., y: 1.),
118            (x: 5., y: 1.),
119        ];
120        let translated = poly1.translate(17.0, 18.0);
121        let correct = polygon![
122            (x: 22.0, y: 19.0),
123            (x: 21.0, y: 20.0),
124            (x: 21.0, y: 21.0),
125            (x: 22.0, y: 22.0),
126            (x: 23.0, y: 22.0),
127            (x: 24.0, y: 21.0),
128            (x: 24.0, y: 20.0),
129            (x: 23.0, y: 19.0),
130            (x: 22.0, y: 19.0),
131        ];
132        // results agree with Shapely / GEOS
133        assert_eq!(translated, correct);
134    }
135    #[test]
136    fn test_rotate_polygon_holes() {
137        let ls1 = LineString::from(vec![
138            (5.0, 1.0),
139            (4.0, 2.0),
140            (4.0, 3.0),
141            (5.0, 4.0),
142            (6.0, 4.0),
143            (7.0, 3.0),
144            (7.0, 2.0),
145            (6.0, 1.0),
146            (5.0, 1.0),
147        ]);
148
149        let ls2 = LineString::from(vec![(5.0, 1.3), (5.5, 2.0), (6.0, 1.3), (5.0, 1.3)]);
150
151        let poly1 = Polygon::new(ls1, vec![ls2]);
152        let rotated = poly1.translate(17.0, 18.0);
153        let correct_outside = vec![
154            Coord::from((22.0, 19.0)),
155            Coord::from((21.0, 20.0)),
156            Coord::from((21.0, 21.0)),
157            Coord::from((22.0, 22.0)),
158            Coord::from((23.0, 22.0)),
159            Coord::from((24.0, 21.0)),
160            Coord::from((24.0, 20.0)),
161            Coord::from((23.0, 19.0)),
162            Coord::from((22.0, 19.0)),
163        ];
164        let correct_inside = vec![
165            Coord::from((22.0, 19.3)),
166            Coord::from((22.5, 20.0)),
167            Coord::from((23.0, 19.3)),
168            Coord::from((22.0, 19.3)),
169        ];
170        assert_eq!(rotated.exterior().0, correct_outside);
171        assert_eq!(rotated.interiors()[0].0, correct_inside);
172    }
173}