1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use crate::algorithm::broadcasting::BroadcastableVec;
use crate::array::GeometryArray;
use crate::error::Result;
use geo::{AffineTransform, MapCoords};

/// Used to express the origin for a given transform. Can be specified either be with reference to
/// the geometry being transformed (Centroid, Center) or some arbitrary point.
///
/// - Centroid: Use the centriod of each geometry in the series as the transform origin.
/// - Center: Use the center of each geometry in the series as the transform origin. The center is
///   defined as the center of the bounding box of the geometry
/// - Point: Define a single point to transform each geometry in the series about.
pub enum TransformOrigin {
    Centroid,
    Center,
    Point(geo::Point),
}

/// Apply an affine transformation on an array of geometries
pub fn affine_transform(
    array: &GeometryArray,
    transform: BroadcastableVec<AffineTransform>,
) -> Result<GeometryArray> {
    match array {
        GeometryArray::WKB(arr) => {
            let output_geoms: Vec<Option<geo::Geometry>> = arr
                .iter_geo()
                .zip(transform.into_iter())
                .map(|(maybe_g, transform)| {
                    maybe_g.map(|geom| geom.map_coords(|coord| transform.apply(coord)))
                })
                .collect();

            Ok(GeometryArray::WKB(output_geoms.into()))
        }
        GeometryArray::Point(arr) => {
            let output_geoms: Vec<Option<geo::Point>> = arr
                .iter_geo()
                .zip(transform.into_iter())
                .map(|(maybe_g, transform)| {
                    maybe_g.map(|geom| geom.map_coords(|coord| transform.apply(coord)))
                })
                .collect();

            Ok(GeometryArray::Point(output_geoms.into()))
        }

        GeometryArray::MultiPoint(arr) => {
            let output_geoms: Vec<Option<geo::MultiPoint>> = arr
                .iter_geo()
                .zip(transform.into_iter())
                .map(|(maybe_g, transform)| {
                    maybe_g.map(|geom| geom.map_coords(|coord| transform.apply(coord)))
                })
                .collect();

            Ok(GeometryArray::MultiPoint(output_geoms.into()))
        }
        GeometryArray::LineString(arr) => {
            let output_geoms: Vec<Option<geo::LineString>> = arr
                .iter_geo()
                .zip(transform.into_iter())
                .map(|(maybe_g, transform)| {
                    maybe_g.map(|geom| geom.map_coords(|coord| transform.apply(coord)))
                })
                .collect();

            Ok(GeometryArray::LineString(output_geoms.into()))
        }
        GeometryArray::MultiLineString(arr) => {
            let output_geoms: Vec<Option<geo::MultiLineString>> = arr
                .iter_geo()
                .zip(transform.into_iter())
                .map(|(maybe_g, transform)| {
                    maybe_g.map(|geom| geom.map_coords(|coord| transform.apply(coord)))
                })
                .collect();

            Ok(GeometryArray::MultiLineString(output_geoms.into()))
        }
        GeometryArray::Polygon(arr) => {
            let output_geoms: Vec<Option<geo::Polygon>> = arr
                .iter_geo()
                .zip(transform.into_iter())
                .map(|(maybe_g, transform)| {
                    maybe_g.map(|geom| geom.map_coords(|coord| transform.apply(coord)))
                })
                .collect();

            Ok(GeometryArray::Polygon(output_geoms.into()))
        }
        GeometryArray::MultiPolygon(arr) => {
            let output_geoms: Vec<Option<geo::MultiPolygon>> = arr
                .iter_geo()
                .zip(transform.into_iter())
                .map(|(maybe_g, transform)| {
                    maybe_g.map(|geom| geom.map_coords(|coord| transform.apply(coord)))
                })
                .collect();

            Ok(GeometryArray::MultiPolygon(output_geoms.into()))
        }
    }
}