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
21pub 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
48impl 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
104impl 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
237impl 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
244impl 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
257impl 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}
273impl 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
281impl 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
294impl 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
325impl ToSFCGAL for geo_types::Polygon<f64> {
327 fn to_sfcgal(&self) -> Result<SFCGeometry> {
328 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
335impl 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 (exterior, interiors) = (polygon.exterior(), polygon.interiors());
345 geo_polygon_to_sfcgal(&exterior.0, interiors)
346 })
347 .collect::<Result<Vec<_>>>()?
348 )
349 }
350}
351
352impl 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
369impl 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
377impl 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}