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`], or [`Rotate`](crate::Rotate), it is more
10    /// efficient to compose the transformations and apply them as a single operation using the
11    /// [`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
40impl<T, G> Translate<T> for G
41where
42    T: CoordNum,
43    G: AffineOps<T>,
44{
45    fn translate(&self, x_offset: T, y_offset: T) -> Self {
46        let transform = AffineTransform::translate(x_offset, y_offset);
47        self.affine_transform(&transform)
48    }
49
50    fn translate_mut(&mut self, x_offset: T, y_offset: T) {
51        let transform = AffineTransform::translate(x_offset, y_offset);
52        self.affine_transform_mut(&transform)
53    }
54}
55
56#[cfg(test)]
57mod test {
58    use super::*;
59    use crate::{line_string, point, polygon, Coord, LineString, Polygon};
60
61    #[test]
62    fn test_translate_point() {
63        let p = point!(x: 1.0, y: 5.0);
64        let translated = p.translate(30.0, 20.0);
65        assert_eq!(translated, point!(x: 31.0, y: 25.0));
66    }
67    #[test]
68    fn test_translate_point_in_place() {
69        let mut p = point!(x: 1.0, y: 5.0);
70        p.translate_mut(30.0, 20.0);
71        assert_eq!(p, point!(x: 31.0, y: 25.0));
72    }
73    #[test]
74    fn test_translate_linestring() {
75        let linestring = line_string![
76            (x: 0.0, y: 0.0),
77            (x: 5.0, y: 1.0),
78            (x: 10.0, y: 0.0),
79        ];
80        let translated = linestring.translate(17.0, 18.0);
81        assert_eq!(
82            translated,
83            line_string![
84                (x: 17.0, y: 18.0),
85                (x: 22.0, y: 19.0),
86                (x: 27., y: 18.),
87            ]
88        );
89    }
90    #[test]
91    fn test_translate_polygon() {
92        let poly1 = polygon![
93            (x: 5., y: 1.),
94            (x: 4., y: 2.),
95            (x: 4., y: 3.),
96            (x: 5., y: 4.),
97            (x: 6., y: 4.),
98            (x: 7., y: 3.),
99            (x: 7., y: 2.),
100            (x: 6., y: 1.),
101            (x: 5., y: 1.),
102        ];
103        let translated = poly1.translate(17.0, 18.0);
104        let correct = polygon![
105            (x: 22.0, y: 19.0),
106            (x: 21.0, y: 20.0),
107            (x: 21.0, y: 21.0),
108            (x: 22.0, y: 22.0),
109            (x: 23.0, y: 22.0),
110            (x: 24.0, y: 21.0),
111            (x: 24.0, y: 20.0),
112            (x: 23.0, y: 19.0),
113            (x: 22.0, y: 19.0),
114        ];
115        // results agree with Shapely / GEOS
116        assert_eq!(translated, correct);
117    }
118    #[test]
119    fn test_rotate_polygon_holes() {
120        let ls1 = LineString::from(vec![
121            (5.0, 1.0),
122            (4.0, 2.0),
123            (4.0, 3.0),
124            (5.0, 4.0),
125            (6.0, 4.0),
126            (7.0, 3.0),
127            (7.0, 2.0),
128            (6.0, 1.0),
129            (5.0, 1.0),
130        ]);
131
132        let ls2 = LineString::from(vec![(5.0, 1.3), (5.5, 2.0), (6.0, 1.3), (5.0, 1.3)]);
133
134        let poly1 = Polygon::new(ls1, vec![ls2]);
135        let rotated = poly1.translate(17.0, 18.0);
136        let correct_outside = vec![
137            Coord::from((22.0, 19.0)),
138            Coord::from((21.0, 20.0)),
139            Coord::from((21.0, 21.0)),
140            Coord::from((22.0, 22.0)),
141            Coord::from((23.0, 22.0)),
142            Coord::from((24.0, 21.0)),
143            Coord::from((24.0, 20.0)),
144            Coord::from((23.0, 19.0)),
145            Coord::from((22.0, 19.0)),
146        ];
147        let correct_inside = vec![
148            Coord::from((22.0, 19.3)),
149            Coord::from((22.5, 20.0)),
150            Coord::from((23.0, 19.3)),
151            Coord::from((22.0, 19.3)),
152        ];
153        assert_eq!(rotated.exterior().0, correct_outside);
154        assert_eq!(rotated.interiors()[0].0, correct_inside);
155    }
156}