sfcgal/conversion/
geotypes.rs

1use crate::{
2    conversion::coords::{CoordSeq, CoordType, ToSFCGALGeom},
3    utils::check_null_geom,
4    GeomType, Point2d, Result, SFCGeometry, ToCoordinates, ToSFCGAL,
5};
6use anyhow::Error;
7use sfcgal_sys::{
8    sfcgal_geometry_collection_add_geometry, sfcgal_geometry_collection_create,
9    sfcgal_geometry_collection_geometry_n, sfcgal_geometry_collection_num_geometries,
10    sfcgal_geometry_t, sfcgal_linestring_add_point, sfcgal_linestring_create,
11    sfcgal_linestring_num_points, sfcgal_linestring_point_n, sfcgal_multi_linestring_create,
12    sfcgal_multi_point_create, sfcgal_multi_polygon_create, sfcgal_point_create_from_xy,
13    sfcgal_point_x, sfcgal_point_y, sfcgal_polygon_add_interior_ring,
14    sfcgal_polygon_create_from_exterior_ring, sfcgal_polygon_exterior_ring,
15    sfcgal_polygon_interior_ring_n, sfcgal_polygon_num_interior_rings, sfcgal_triangle_create,
16    sfcgal_triangle_set_vertex_from_xy,
17};
18use std::convert::{Into, TryFrom};
19use std::iter::FromIterator;
20
21/// Conversion from [`SFCGeometry`] (implemented on [geo-types](https://docs.rs/geo-types/) geometries)
22///
23/// [`SFCGeometry`]: struct.SFCGeometry.html
24pub trait TryInto<T> {
25    type Err;
26    fn try_into(self) -> Result<T>;
27}
28
29impl CoordType for geo_types::Point<f64> {}
30impl CoordType for geo_types::Coord<f64> {}
31
32impl ToSFCGALGeom for geo_types::Point<f64> {
33    fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> {
34        let g = unsafe { sfcgal_point_create_from_xy(self.x(), self.y()) };
35        check_null_geom(g)?;
36        Ok(g)
37    }
38}
39
40impl ToSFCGALGeom for geo_types::Coord<f64> {
41    fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> {
42        let g = unsafe { sfcgal_point_create_from_xy(self.x, self.y) };
43        check_null_geom(g)?;
44        Ok(g)
45    }
46}
47
48/// Implements conversion from CoordSeq to geo_types::Geometry
49/// (better use TryInto<geo_types::Geometry> for SFCGeometry if the intend
50/// is to convert SFCGAL Geometries to geo_types ones)
51impl TryInto<geo_types::Geometry<f64>> for CoordSeq<Point2d> {
52    type Err = Error;
53
54    fn try_into(self) -> Result<geo_types::Geometry<f64>> {
55        match self {
56                CoordSeq::Point(pt) => Ok(geo_types::Point(pt.into()).into()),
57                CoordSeq::Multipoint(pts) => Ok(geo_types::MultiPoint::from_iter(pts).into()),
58                CoordSeq::Linestring(pts) => Ok(geo_types::LineString::from_iter(pts).into()),
59                CoordSeq::Multilinestring(lines) => {
60                    Ok(geo_types::MultiLineString(
61                        lines.into_iter()
62                            .map(geo_types::LineString::from)
63                            .collect()
64                    ).into())
65                },
66                CoordSeq::Polygon(rings) => {
67                    let mut it = rings.into_iter();
68                    let exterior = geo_types::LineString::from(it.next().unwrap());
69                    let interiors = it.map(geo_types::LineString::from).collect::<Vec<geo_types::LineString<f64>>>();
70                    Ok(geo_types::Polygon::new(exterior, interiors).into())
71                },
72                CoordSeq::Multipolygon(polygons) => {
73                    let polys = polygons.into_iter().map(|p| {
74                        let a: geo_types::Geometry<f64> = TryInto::try_into(CoordSeq::Polygon(p))?;
75                        if let Ok(poly) = geo_types::Polygon::try_from(a) {
76                            Ok(poly)
77                        } else {
78                            Err(format_err!("Error while building geo_types::MultiPolygon"))
79                        }
80                    }).collect::<Result<Vec<geo_types::Polygon<f64>>>>()?;
81                    Ok(geo_types::MultiPolygon(polys).into())
82                },
83                CoordSeq::Geometrycollection(collection) => {
84                    Ok(geo_types::Geometry::GeometryCollection(
85                        geo_types::GeometryCollection(collection
86                            .into_iter()
87                            .map(TryInto::try_into)
88                            .collect::<Result<Vec<geo_types::Geometry<f64>>>>()?
89                        )
90                    ))
91                },
92                CoordSeq::Triangle(pts) => {
93                    Ok(geo_types::Geometry::Triangle(
94                        geo_types::Triangle(pts[0].into(), pts[1].into(), pts[2].into())
95                    ))
96                },
97                _ => Err(
98                    format_err!(
99                        "Conversion from CoordSeq variants `Solid`, `Multisolid`, `Triangulatedsurface` and `Polyhedralsurface` are not yet implemented!"))
100            }
101    }
102}
103
104/// Implements faillible conversion from SFCGeometry to geo_types::Geometry.
105///
106/// This is notably faillible because some types of [`SFCGeometry`] like GeoTypes::Polyhedralsurface
107/// don't have equivalents in geo_types::Geometry.
108/// Please note that geo_types Coordinate and Point primitives are 2d only, so
109/// every information about z coordinate (if any) won't be taken into account.
110impl TryInto<geo_types::Geometry<f64>> for SFCGeometry {
111    type Err = Error;
112
113    fn try_into(self) -> Result<geo_types::Geometry<f64>> {
114        match self._type()? {
115            GeomType::Point => {
116                let c = self.to_coordinates::<Point2d>()?;
117                let p: geo_types::Point<f64> = match c {
118                    CoordSeq::Point(pt) => pt.into(),
119                    _ => unimplemented!(),
120                };
121                Ok(geo_types::Geometry::Point(p))
122            }
123            GeomType::Multipoint => {
124                let c = self.to_coordinates::<Point2d>()?;
125                let p: geo_types::MultiPoint<f64> = match c {
126                    CoordSeq::Multipoint(pts) => pts.into(),
127                    _ => unimplemented!(),
128                };
129                Ok(geo_types::Geometry::MultiPoint(p))
130            }
131            GeomType::Linestring => Ok(geo_types::Geometry::LineString(geo_line_from_sfcgal(
132                unsafe { self.c_geom.as_ref() },
133            )?)),
134            GeomType::Multilinestring => {
135                let ngeoms =
136                    unsafe { sfcgal_geometry_collection_num_geometries(self.c_geom.as_ref()) };
137                let mut lines = Vec::with_capacity(ngeoms);
138                for i in 0..ngeoms {
139                    let geom =
140                        unsafe { sfcgal_geometry_collection_geometry_n(self.c_geom.as_ref(), i) };
141                    lines.push(geo_line_from_sfcgal(geom)?);
142                }
143                Ok(geo_types::Geometry::MultiLineString(
144                    geo_types::MultiLineString(lines),
145                ))
146            }
147            GeomType::Polygon => {
148                let nrings = unsafe { sfcgal_polygon_num_interior_rings(self.c_geom.as_ref()) };
149                let exterior_sfcgal = unsafe { sfcgal_polygon_exterior_ring(self.c_geom.as_ref()) };
150                let exterior_geo = geo_line_from_sfcgal(exterior_sfcgal)?;
151                let mut interiors_geo = Vec::with_capacity(nrings);
152                for i in 0..nrings {
153                    let line_sfcgal =
154                        unsafe { sfcgal_polygon_interior_ring_n(self.c_geom.as_ref(), i) };
155                    interiors_geo.push(geo_line_from_sfcgal(line_sfcgal)?);
156                }
157
158                Ok(geo_types::Geometry::Polygon(geo_types::Polygon::new(
159                    exterior_geo,
160                    interiors_geo,
161                )))
162            }
163            GeomType::Multipolygon => {
164                let ngeoms =
165                    unsafe { sfcgal_geometry_collection_num_geometries(self.c_geom.as_ref()) };
166                let mut vec_polygons = Vec::with_capacity(ngeoms);
167                for i in 0..ngeoms {
168                    let _polyg =
169                        unsafe { sfcgal_geometry_collection_geometry_n(self.c_geom.as_ref(), i) };
170                    let nrings = unsafe { sfcgal_polygon_num_interior_rings(_polyg) };
171                    let exterior_sfcgal = unsafe { sfcgal_polygon_exterior_ring(_polyg) };
172                    let exterior_geo = geo_line_from_sfcgal(exterior_sfcgal)?;
173                    let mut interiors_geo = Vec::with_capacity(nrings);
174                    for j in 0..nrings {
175                        let line_sfcgal = unsafe { sfcgal_polygon_interior_ring_n(_polyg, j) };
176                        interiors_geo.push(geo_line_from_sfcgal(line_sfcgal)?);
177                    }
178                    vec_polygons.push(geo_types::Polygon::new(exterior_geo, interiors_geo));
179                }
180
181                Ok(geo_types::MultiPolygon(vec_polygons).into())
182            }
183            GeomType::Geometrycollection => {
184                let c = self.to_coordinates::<Point2d>()?;
185                let p = match c {
186                    CoordSeq::Geometrycollection(g) => {
187                        g.into_iter()
188                            .map(TryInto::try_into)
189                            .collect::<Result<Vec<geo_types::Geometry<f64>>>>()?
190                    }
191                    _ => unimplemented!(),
192                };
193                Ok(geo_types::Geometry::GeometryCollection(
194                    geo_types::GeometryCollection(p),
195                ))
196            }
197            GeomType::Triangle => {
198                let coords = match self.to_coordinates::<Point2d>()? {
199                    CoordSeq::Triangle(t) => t,
200                    _ => unimplemented!(),
201                };
202                Ok(geo_types::Geometry::Triangle(geo_types::Triangle(
203                    coords[0].into(),
204                    coords[1].into(),
205                    coords[2].into(),
206                )))
207            }
208            _ => Err(format_err!(
209                "Conversion from SFCGeometry of type `Solid`, `Multisolid`, \
210                 `Triangulatedsurface` and `Polyhedralsurface` \
211                 to geo_types::Geometry are not yet implemented!"
212            )),
213        }
214    }
215}
216
217fn geo_line_from_sfcgal(
218    sfcgal_geom: *const sfcgal_geometry_t,
219) -> Result<geo_types::LineString<f64>> {
220    let n_points = unsafe { sfcgal_linestring_num_points(sfcgal_geom) };
221    let mut v_points = Vec::with_capacity(n_points);
222    for i in 0..n_points {
223        let pt_sfcgal = unsafe { sfcgal_linestring_point_n(sfcgal_geom, i) };
224        check_null_geom(pt_sfcgal)?;
225        let pt_geom = geo_point_from_sfcgal(pt_sfcgal);
226        v_points.push(pt_geom);
227    }
228    Ok(geo_types::LineString::from(v_points))
229}
230
231fn geo_point_from_sfcgal(geom: *const sfcgal_geometry_t) -> geo_types::Point<f64> {
232    let x = unsafe { sfcgal_point_x(geom) };
233    let y = unsafe { sfcgal_point_y(geom) };
234    geo_types::Point::new(x, y)
235}
236
237/// Create a `SFCGeometry` from a geo-types Point
238impl ToSFCGAL for geo_types::Point<f64> {
239    fn to_sfcgal(&self) -> Result<SFCGeometry> {
240        unsafe { SFCGeometry::new_from_raw(sfcgal_point_create_from_xy(self.x(), self.y()), true) }
241    }
242}
243
244/// Create a `SFCGeometry` from a geo-types MultiPoint
245impl ToSFCGAL for geo_types::MultiPoint<f64> {
246    fn to_sfcgal(&self) -> Result<SFCGeometry> {
247        make_sfcgal_multi_geom!(
248            sfcgal_multi_point_create(),
249            self.0
250                .iter()
251                .map(|pt| pt.to_sfcgeometry())
252                .collect::<Result<Vec<_>>>()?
253        )
254    }
255}
256
257/// Create a `SFCGeometry` from a geo-types Line
258impl ToSFCGAL for geo_types::Line<f64> {
259    fn to_sfcgal(&self) -> Result<SFCGeometry> {
260        let out_linestring = unsafe { sfcgal_linestring_create() };
261        check_null_geom(out_linestring)?;
262        let start = unsafe { sfcgal_point_create_from_xy(self.start.x, self.start.y) };
263        let end = unsafe { sfcgal_point_create_from_xy(self.end.x, self.end.y) };
264        check_null_geom(start)?;
265        check_null_geom(end)?;
266        unsafe {
267            sfcgal_linestring_add_point(out_linestring, start);
268            sfcgal_linestring_add_point(out_linestring, end);
269            SFCGeometry::new_from_raw(out_linestring, true)
270        }
271    }
272}
273/// Create a `SFCGeometry` from a geo-types LineString
274impl ToSFCGAL for geo_types::LineString<f64> {
275    fn to_sfcgal(&self) -> Result<SFCGeometry> {
276        let line = self.0.to_sfcgeometry()?;
277        unsafe { SFCGeometry::new_from_raw(line, true) }
278    }
279}
280
281/// Create a `SFCGeometry` from a geo-types MultiLineString
282impl ToSFCGAL for geo_types::MultiLineString<f64> {
283    fn to_sfcgal(&self) -> Result<SFCGeometry> {
284        make_sfcgal_multi_geom!(
285            sfcgal_multi_linestring_create(),
286            self.0
287                .iter()
288                .map(|line| line.0.to_sfcgeometry())
289                .collect::<Result<Vec<_>>>()?
290        )
291    }
292}
293
294/// Create a `SFCGeometry` from a geo-types Triangle
295impl ToSFCGAL for geo_types::Triangle<f64> {
296    fn to_sfcgal(&self) -> Result<SFCGeometry> {
297        let out_triangle = unsafe { sfcgal_triangle_create() };
298        check_null_geom(out_triangle)?;
299        let geo_types::Triangle(c0, c1, c2) = self;
300        unsafe {
301            sfcgal_triangle_set_vertex_from_xy(out_triangle, 0, c0.x, c0.y);
302            sfcgal_triangle_set_vertex_from_xy(out_triangle, 1, c1.x, c1.y);
303            sfcgal_triangle_set_vertex_from_xy(out_triangle, 2, c2.x, c2.y);
304            SFCGeometry::new_from_raw(out_triangle, true)
305        }
306    }
307}
308
309fn geo_polygon_to_sfcgal<T>(
310    exterior: &Vec<T>,
311    interiors: &[geo_types::LineString<f64>],
312) -> Result<*mut sfcgal_geometry_t>
313where
314    T: ToSFCGALGeom + CoordType,
315{
316    let out_polygon =
317        unsafe { sfcgal_polygon_create_from_exterior_ring(exterior.to_sfcgeometry()?) };
318    check_null_geom(out_polygon)?;
319    for ring in interiors.iter() {
320        unsafe { sfcgal_polygon_add_interior_ring(out_polygon, ring.0.to_sfcgeometry()?) };
321    }
322    Ok(out_polygon)
323}
324
325/// Create a `SFCGeometry` from a geo-types Polygon
326impl ToSFCGAL for geo_types::Polygon<f64> {
327    fn to_sfcgal(&self) -> Result<SFCGeometry> {
328        // let geo_types::Polygon{exterior, interiors} = self;
329        let (exterior, interiors) = (self.exterior(), self.interiors());
330        let out_polygon = geo_polygon_to_sfcgal(&exterior.0, interiors)?;
331        unsafe { SFCGeometry::new_from_raw(out_polygon, true) }
332    }
333}
334
335/// Create a `SFCGeometry` from a geo-types MultiPolygon
336impl ToSFCGAL for geo_types::MultiPolygon<f64> {
337    fn to_sfcgal(&self) -> Result<SFCGeometry> {
338        make_sfcgal_multi_geom!(
339            sfcgal_multi_polygon_create(),
340            self.0
341                .iter()
342                .map(|polygon| {
343                    // let geo_types::Polygon{ref exterior, ref interiors} = polygon;
344                    let (exterior, interiors) = (polygon.exterior(), polygon.interiors());
345                    geo_polygon_to_sfcgal(&exterior.0, interiors)
346                })
347                .collect::<Result<Vec<_>>>()?
348        )
349    }
350}
351
352/// Create a `SFCGeometry` from a geo-types GeometryCollection
353impl ToSFCGAL for geo_types::GeometryCollection<f64> {
354    fn to_sfcgal(&self) -> Result<SFCGeometry> {
355        make_sfcgal_multi_geom!(
356            sfcgal_geometry_collection_create(),
357            self.0
358                .iter()
359                .map(|geom| {
360                    let mut _g = geom.to_sfcgal()?;
361                    _g.owned = false;
362                    Ok(_g.c_geom.as_ptr())
363                })
364                .collect::<Result<Vec<_>>>()?
365        )
366    }
367}
368
369/// Create a `SFCGeometry` from a geo-types Rect
370impl ToSFCGAL for geo_types::Rect<f64> {
371    fn to_sfcgal(&self) -> Result<SFCGeometry> {
372        let poly = self.to_polygon();
373        poly.to_sfcgal()
374    }
375}
376
377/// Create a `SFCGeometry` from any geo-type Geometry
378impl ToSFCGAL for geo_types::Geometry<f64> {
379    fn to_sfcgal(&self) -> Result<SFCGeometry> {
380        match *self {
381            geo_types::Geometry::Point(ref c) => c.to_sfcgal(),
382            geo_types::Geometry::Line(ref c) => c.to_sfcgal(),
383            geo_types::Geometry::LineString(ref c) => c.to_sfcgal(),
384            geo_types::Geometry::Polygon(ref c) => c.to_sfcgal(),
385            geo_types::Geometry::MultiPoint(ref c) => c.to_sfcgal(),
386            geo_types::Geometry::MultiLineString(ref c) => c.to_sfcgal(),
387            geo_types::Geometry::MultiPolygon(ref c) => c.to_sfcgal(),
388            geo_types::Geometry::GeometryCollection(ref c) => c.to_sfcgal(),
389            geo_types::Geometry::Rect(ref c) => c.to_sfcgal(),
390            geo_types::Geometry::Triangle(ref c) => c.to_sfcgal(),
391        }
392    }
393}
394
395#[cfg(test)]
396mod tests {
397    use super::TryInto;
398    use crate::{GeomType, SFCGeometry, ToSFCGAL};
399    use geo_types::{
400        Coord, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Triangle,
401    };
402    use std::convert::TryFrom;
403
404    #[test]
405    fn point_geo_to_sfcgal_to_geo() {
406        let pt = Point::new(0.1, 0.9);
407        let pt_sfcgal = pt.to_sfcgal().unwrap();
408        assert!(pt_sfcgal.is_valid().unwrap());
409        let pt: Point<f64> =
410            geo_types::Point::try_from(TryInto::try_into(pt_sfcgal).unwrap()).unwrap();
411        assert_eq!(pt.x(), 0.1);
412        assert_eq!(pt.y(), 0.9);
413    }
414
415    #[test]
416    fn point_sfcgal_try_into_geo() {
417        let pt_sfcgal = SFCGeometry::new("POINT (0.1 0.9)").unwrap();
418        let pt: Point<f64> =
419            geo_types::Point::try_from(TryInto::try_into(pt_sfcgal).unwrap()).unwrap();
420        assert_ulps_eq!(pt.x(), 0.1);
421        assert_ulps_eq!(pt.y(), 0.9);
422    }
423
424    #[test]
425    fn multipoint_geo_to_sfcgal_to_geo() {
426        let multipt = MultiPoint::from(vec![Point::new(0., 0.), Point::new(1., 1.)]);
427        let mpt_sfcgal = multipt.to_sfcgal().unwrap();
428        assert!(mpt_sfcgal.is_valid().unwrap());
429        let mpt: MultiPoint<f64> =
430            geo_types::MultiPoint::try_from(TryInto::try_into(mpt_sfcgal).unwrap()).unwrap();
431
432        assert_eq!(mpt.0[0].x(), 0.);
433        assert_eq!(mpt.0[0].y(), 0.);
434        assert_eq!(mpt.0[1].x(), 1.);
435        assert_eq!(mpt.0[1].y(), 1.);
436    }
437
438    #[test]
439    fn linestring_geo_to_sfcgal_to_geo() {
440        let linestring = LineString::from(vec![Point::new(0., 0.), Point::new(1., 1.)]);
441        let line_sfcgal = linestring.to_sfcgal().unwrap();
442        assert!(line_sfcgal.is_valid().unwrap());
443        let linestring_geo: LineString<f64> =
444            geo_types::LineString::try_from(TryInto::try_into(line_sfcgal).unwrap()).unwrap();
445        assert_eq!(linestring_geo.0[0].x, 0.);
446        assert_eq!(linestring_geo.0[0].y, 0.);
447        assert_eq!(linestring_geo.0[1].x, 1.);
448        assert_eq!(linestring_geo.0[1].y, 1.);
449    }
450
451    #[test]
452    fn multilinestring_geo_to_sfcgal_to_geo() {
453        let multilinestring = MultiLineString::from(LineString::from(vec![
454            Point::new(0., 0.),
455            Point::new(1., 1.),
456        ]));
457        let mls_sfcgal = multilinestring.to_sfcgal().unwrap();
458        assert!(mls_sfcgal.is_valid().unwrap());
459        let mls: MultiLineString<f64> =
460            geo_types::MultiLineString::try_from(TryInto::try_into(mls_sfcgal).unwrap()).unwrap();
461        assert_eq!(mls.0[0].0[0].x, 0.);
462        assert_eq!(mls.0[0].0[0].y, 0.);
463        assert_eq!(mls.0[0].0[1].x, 1.);
464        assert_eq!(mls.0[0].0[1].y, 1.);
465    }
466
467    #[test]
468    fn triangle_geo_to_sfcgal_to_geo() {
469        let tri = Triangle(
470            Coord::from((0., 0.)),
471            Coord::from((1., 0.)),
472            Coord::from((0.5, 1.)),
473        );
474        let tri_sfcgal = tri.to_sfcgal().unwrap();
475        assert!(tri_sfcgal.is_valid().unwrap());
476        assert_eq!(tri_sfcgal._type().unwrap(), GeomType::Triangle);
477        let tri_geo: geo_types::Geometry<f64> = TryInto::try_into(tri_sfcgal).unwrap();
478        match tri_geo {
479            geo_types::Geometry::Triangle(t) => {
480                assert_eq!(t.0.x, 0.);
481                assert_eq!(t.0.y, 0.);
482                assert_eq!(t.1.x, 1.);
483                assert_eq!(t.1.y, 0.);
484                assert_eq!(t.2.x, 0.5);
485                assert_eq!(t.2.y, 1.);
486            }
487            _ => panic!("Bad conversion when converting Triangle"),
488        }
489    }
490
491    #[test]
492    fn polygon_geo_to_sfcgal_to_geo() {
493        let polygon = Polygon::new(
494            LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)]),
495            vec![LineString::from(vec![
496                (0.1, 0.1),
497                (0.1, 0.9),
498                (0.9, 0.9),
499                (0.9, 0.1),
500                (0.1, 0.1),
501            ])],
502        );
503        let poly_sfcgal = polygon.to_sfcgal().unwrap();
504        let polyg: Polygon<f64> =
505            geo_types::Polygon::try_from(TryInto::try_into(poly_sfcgal).unwrap()).unwrap();
506        let interiors = polyg.interiors();
507        assert_eq!(
508            polyg.exterior(),
509            &LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.,), (0., 0.)])
510        );
511        assert_eq!(interiors[0].0[0].x, 0.1);
512        assert_eq!(interiors[0].0[0].y, 0.1);
513        assert_eq!(interiors[0].0[2].x, 0.9);
514        assert_eq!(interiors[0].0[2].y, 0.9);
515        assert_eq!(interiors[0].0[3].x, 0.9);
516        assert_eq!(interiors[0].0[3].y, 0.1);
517    }
518
519    #[test]
520    fn multipolygon_geo_to_sfcgal_to_geo() {
521        let multipolygon = MultiPolygon(vec![Polygon::new(
522            LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)]),
523            vec![LineString::from(vec![
524                (0.1, 0.1),
525                (0.1, 0.9),
526                (0.9, 0.9),
527                (0.9, 0.1),
528                (0.1, 0.1),
529            ])],
530        )]);
531        let mutlipolygon_sfcgal = multipolygon.to_sfcgal().unwrap();
532        let mpg: MultiPolygon<f64> =
533            geo_types::MultiPolygon::try_from(TryInto::try_into(mutlipolygon_sfcgal).unwrap())
534                .unwrap();
535
536        assert_eq!(
537            mpg.0[0].exterior(),
538            &LineString::from(vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.,), (0., 0.)])
539        );
540        assert_eq!(
541            mpg.0[0].interiors()[0],
542            LineString::from(vec![
543                (0.1, 0.1),
544                (0.1, 0.9,),
545                (0.9, 0.9),
546                (0.9, 0.1),
547                (0.1, 0.1)
548            ])
549        );
550    }
551
552    #[test]
553    fn geometrycollection_sfcgal_to_geo_to_sfcgal() {
554        let input_wkt = "GEOMETRYCOLLECTION (POINT (4.0 6.0),LINESTRING (4.0 6.0,7.0 10.0))";
555        let gc_sfcgal = SFCGeometry::new(input_wkt).unwrap();
556        let gc: geo_types::Geometry<f64> = TryInto::try_into(gc_sfcgal).unwrap();
557        if let geo_types::Geometry::GeometryCollection(_gc) = &gc {
558            assert_eq!(
559                Point::new(4., 6.),
560                geo_types::Point::try_from(_gc.0[0].clone()).unwrap()
561            );
562            assert_eq!(
563                LineString::from(vec![(4., 6.), (7., 10.)]),
564                geo_types::LineString::try_from(_gc.0[1].clone()).unwrap(),
565            );
566            let gc_sfcgal = _gc.to_sfcgal().unwrap();
567            assert_eq!(input_wkt, gc_sfcgal.to_wkt_decim(1).unwrap());
568        } else {
569            panic!("Error while deconstructing geometrycollection");
570        }
571    }
572}