geos/
from_geojson.rs

1use crate::error::{Error, GResult};
2use crate::{CoordDimensions, CoordSeq, Geometry as GGeometry};
3use geojson::{Geometry, Value};
4
5use std::convert::{TryFrom, TryInto};
6use std::iter;
7
8#[allow(clippy::needless_lifetimes)]
9fn create_coord_seq_from_vec<'a, 'b>(coords: &'a [Vec<f64>]) -> Result<CoordSeq, Error> {
10    create_coord_seq(coords.iter(), coords.len())
11}
12
13#[allow(clippy::needless_lifetimes)]
14fn create_coord_seq<'a, 'b, It>(points: It, len: usize) -> Result<CoordSeq, Error>
15where
16    It: Iterator<Item = &'a Vec<f64>>,
17{
18    let mut coord_seq =
19        CoordSeq::new(len as u32, CoordDimensions::TwoD).expect("failed to create CoordSeq");
20
21    for (i, p) in points.enumerate() {
22        coord_seq.set_x(i, p[0])?;
23        coord_seq.set_y(i, p[1])?;
24    }
25    Ok(coord_seq)
26}
27
28// We need to ensure that rings of polygons are closed
29// to create valid GEOS LinearRings (geojson crate doesn't enforce this for now)
30#[allow(clippy::needless_lifetimes)]
31fn create_closed_coord_seq_from_vec<'a, 'b>(points: &'a [Vec<f64>]) -> Result<CoordSeq, Error> {
32    let nb_points = points.len();
33    // if the geom is not closed we close it
34    let is_closed = nb_points > 0 && points.first() == points.last();
35    // Note: we also need to close a 2 points closed linearring,
36    // as in `from_geo` module
37    let need_closing = nb_points > 0 && (!is_closed || nb_points == 3);
38    if need_closing {
39        create_coord_seq(points.iter().chain(iter::once(&points[0])), nb_points + 1)
40    } else {
41        create_coord_seq(points.iter(), nb_points)
42    }
43}
44
45impl<'a, 'b> TryFrom<&'a Geometry> for GGeometry {
46    type Error = Error;
47
48    fn try_from(other: &'a Geometry) -> Result<GGeometry, Self::Error> {
49        match other.value {
50            Value::Point(ref c) => GGeometry::create_point(create_coord_seq(iter::once(c), 1)?),
51            Value::MultiPoint(ref pts) => {
52                let ggpts = pts
53                    .iter()
54                    .map(|pt| GGeometry::create_point(create_coord_seq(iter::once(pt), 1)?))
55                    .collect::<GResult<Vec<GGeometry>>>()?;
56                GGeometry::create_multipoint(ggpts)
57            }
58            Value::LineString(ref line) => {
59                let coord_seq = create_coord_seq_from_vec(line.as_slice())?;
60                GGeometry::create_line_string(coord_seq)
61            }
62            Value::MultiLineString(ref lines) => {
63                let gglines = lines
64                    .iter()
65                    .map(|line| {
66                        let coord_seq = create_coord_seq_from_vec(line.as_slice())?;
67                        GGeometry::create_line_string(coord_seq)
68                    })
69                    .collect::<GResult<Vec<GGeometry>>>()?;
70                GGeometry::create_multiline_string(gglines)
71            }
72            Value::Polygon(ref rings) => {
73                let exterior_ring = GGeometry::create_linear_ring(
74                    create_closed_coord_seq_from_vec(rings[0].as_slice())?,
75                )?;
76                let interiors = rings
77                    .iter()
78                    .skip(1)
79                    .map(|r| {
80                        GGeometry::create_linear_ring(create_closed_coord_seq_from_vec(
81                            r.as_slice(),
82                        )?)
83                    })
84                    .collect::<GResult<Vec<GGeometry>>>()?;
85                GGeometry::create_polygon(exterior_ring, interiors)
86            }
87            Value::MultiPolygon(ref polygons) => {
88                let ggpolys = polygons
89                    .iter()
90                    .map(|rings| {
91                        let exterior_ring = GGeometry::create_linear_ring(
92                            create_closed_coord_seq_from_vec(rings[0].as_slice())?,
93                        )?;
94                        let interiors = rings
95                            .iter()
96                            .skip(1)
97                            .map(|r| {
98                                GGeometry::create_linear_ring(create_closed_coord_seq_from_vec(
99                                    r.as_slice(),
100                                )?)
101                            })
102                            .collect::<GResult<Vec<GGeometry>>>()?;
103                        GGeometry::create_polygon(exterior_ring, interiors)
104                    })
105                    .collect::<GResult<Vec<GGeometry>>>()?;
106                GGeometry::create_multipolygon(ggpolys)
107            }
108            Value::GeometryCollection(ref geoms) => {
109                let _geoms = geoms
110                    .iter()
111                    .map(|geom| geom.try_into())
112                    .collect::<GResult<Vec<GGeometry>>>()?;
113                GGeometry::create_geometry_collection(_geoms)
114            }
115        }
116    }
117}
118
119impl TryFrom<Geometry> for GGeometry {
120    type Error = Error;
121
122    fn try_from(other: Geometry) -> Result<GGeometry, Self::Error> {
123        GGeometry::try_from(&other)
124    }
125}
126
127#[cfg(test)]
128mod test {
129    use crate::{Geom, Geometry as GGeometry};
130    use geojson::{Geometry, Value};
131
132    use std::convert::TryInto;
133
134    #[test]
135    fn geom_from_geojson_point() {
136        let geojson_pt = Geometry::new(Value::Point(vec![1., 1.]));
137        let gpoint: GGeometry = (&geojson_pt).try_into().unwrap();
138
139        assert_eq!(gpoint.to_wkt_precision(0), Ok("POINT (1 1)".to_string()));
140        // This check ensures that `TryFrom` is implemented for both reference and value.
141        let tmp: GGeometry = geojson_pt.try_into().unwrap();
142        assert_eq!(tmp.to_wkt_precision(0), Ok("POINT (1 1)".to_string()),);
143    }
144
145    #[test]
146    fn geom_from_geojson_multipoint() {
147        let geojson_pts = Geometry::new(Value::MultiPoint(vec![vec![1., 1.], vec![2., 2.]]));
148        let gpts: GGeometry = (&geojson_pts).try_into().unwrap();
149        assert_eq!(
150            gpts.to_wkt_precision(0),
151            Ok("MULTIPOINT (1 1, 2 2)".to_string()),
152        );
153        // This check ensures that `TryFrom` is implemented for both reference and value.
154        let tmp: GGeometry = geojson_pts.try_into().unwrap();
155        assert_eq!(
156            tmp.to_wkt_precision(0),
157            Ok("MULTIPOINT (1 1, 2 2)".to_string()),
158        );
159    }
160
161    #[test]
162    fn geom_from_geojson_line() {
163        let geojson_line = Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]]));
164        let gline: GGeometry = (&geojson_line).try_into().unwrap();
165        assert_eq!(
166            gline.to_wkt_precision(0),
167            Ok("LINESTRING (1 1, 2 2)".to_string()),
168        );
169        // This check ensures that `TryFrom` is implemented for both reference and value.
170        let tmp: GGeometry = geojson_line.try_into().unwrap();
171        assert_eq!(
172            tmp.to_wkt_precision(0),
173            Ok("LINESTRING (1 1, 2 2)".to_string()),
174        );
175    }
176
177    #[test]
178    fn geom_from_geojson_multiline() {
179        let geojson_lines = Geometry::new(Value::MultiLineString(vec![
180            vec![vec![1., 1.], vec![2., 2.]],
181            vec![vec![3., 3.], vec![4., 4.]],
182        ]));
183        let glines: GGeometry = (&geojson_lines).try_into().unwrap();
184        assert_eq!(
185            glines.to_wkt_precision(0),
186            Ok("MULTILINESTRING ((1 1, 2 2), (3 3, 4 4))".to_string()),
187        );
188        // This check ensures that `TryFrom` is implemented for both reference and value.
189        let tmp: GGeometry = geojson_lines.try_into().unwrap();
190        assert_eq!(
191            tmp.to_wkt_precision(0),
192            Ok("MULTILINESTRING ((1 1, 2 2), (3 3, 4 4))".to_string()),
193        );
194    }
195
196    #[test]
197    fn geom_from_geojson_polygon() {
198        let geojson_polygon = Geometry::new(Value::Polygon(vec![
199            vec![
200                vec![0., 0.],
201                vec![0., 3.],
202                vec![3., 3.],
203                vec![3., 0.],
204                vec![0., 0.],
205            ],
206            vec![
207                vec![0.2, 0.2],
208                vec![0.2, 2.],
209                vec![2., 2.],
210                vec![2., 0.2],
211                vec![0.2, 0.2],
212            ],
213        ]));
214        let gpolygon: GGeometry = (&geojson_polygon).try_into().unwrap();
215        assert_eq!(
216            gpolygon.to_wkt_precision(1),
217            Ok("POLYGON ((0.0 0.0, 0.0 3.0, 3.0 3.0, 3.0 0.0, 0.0 0.0), (0.2 0.2, 0.2 2.0, 2.0 2.0, 2.0 0.2, 0.2 0.2))"
218                .to_string()),
219        );
220        // This check ensures that `TryFrom` is implemented for both reference and value.
221        let tmp: GGeometry = geojson_polygon.try_into().unwrap();
222        assert_eq!(
223            tmp.to_wkt_precision(1),
224            Ok("POLYGON ((0.0 0.0, 0.0 3.0, 3.0 3.0, 3.0 0.0, 0.0 0.0), (0.2 0.2, 0.2 2.0, 2.0 2.0, 2.0 0.2, 0.2 0.2))"
225                .to_string()),
226        );
227    }
228
229    #[test]
230    fn geom_from_geojson_polygon_with_unclosed_interior_ring() {
231        let geojson_polygon = Geometry::new(Value::Polygon(vec![
232            vec![
233                vec![0., 0.],
234                vec![0., 3.],
235                vec![3., 3.],
236                vec![3., 0.],
237                vec![0., 0.],
238            ],
239            vec![vec![0.2, 0.2], vec![0.2, 2.], vec![2., 2.], vec![2., 0.2]],
240        ]));
241        let gpolygon: GGeometry = (&geojson_polygon).try_into().unwrap();
242        assert_eq!(
243            gpolygon.to_wkt_precision(1),
244            Ok("POLYGON ((0.0 0.0, 0.0 3.0, 3.0 3.0, 3.0 0.0, 0.0 0.0), (0.2 0.2, 0.2 2.0, 2.0 2.0, 2.0 0.2, 0.2 0.2))"
245                .to_string()),
246        );
247        // This check ensures that `TryFrom` is implemented for both reference and value.
248        let tmp: GGeometry = geojson_polygon.try_into().unwrap();
249        assert_eq!(
250            tmp.to_wkt_precision(1),
251            Ok("POLYGON ((0.0 0.0, 0.0 3.0, 3.0 3.0, 3.0 0.0, 0.0 0.0), (0.2 0.2, 0.2 2.0, 2.0 2.0, 2.0 0.2, 0.2 0.2))"
252                .to_string()),
253        );
254    }
255
256    #[test]
257    fn geom_from_geojson_multipolygon() {
258        let geojson_multipolygon = Geometry::new(Value::MultiPolygon(vec![vec![vec![
259            vec![0., 0.],
260            vec![0., 1.],
261            vec![1., 1.],
262            vec![1., 0.],
263            vec![0., 0.],
264        ]]]));
265        let gmultipolygon: GGeometry = (&geojson_multipolygon).try_into().unwrap();
266        assert_eq!(
267            gmultipolygon.to_wkt_precision(0),
268            Ok("MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)))".to_string()),
269        );
270        // This check ensures that `TryFrom` is implemented for both reference and value.
271        let tmp: GGeometry = geojson_multipolygon.try_into().unwrap();
272        assert_eq!(
273            tmp.to_wkt_precision(0),
274            Ok("MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)))".to_string()),
275        );
276    }
277
278    #[test]
279    fn geom_from_geojson_geometry_collection() {
280        let geojson_gc = Geometry::new(Value::GeometryCollection(vec![
281            Geometry::new(Value::Point(vec![1., 1.])),
282            Geometry::new(Value::LineString(vec![vec![1., 1.], vec![2., 2.]])),
283        ]));
284        let gc: GGeometry = (&geojson_gc).try_into().unwrap();
285        assert_eq!(
286            gc.to_wkt_precision(0),
287            Ok("GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))".to_string()),
288        );
289        // This check ensures that `TryFrom` is implemented for both reference and value.
290        let tmp: GGeometry = geojson_gc.try_into().unwrap();
291        assert_eq!(
292            tmp.to_wkt_precision(0),
293            Ok("GEOMETRYCOLLECTION (POINT (1 1), LINESTRING (1 1, 2 2))".to_string()),
294        );
295    }
296}