1use crate::{
2 utils::check_null_geom, GeomType, Point2d, Point3d, Result, SFCGeometry, ToCoordinates,
3 ToSFCGAL,
4};
5use sfcgal_sys::{
6 sfcgal_geometry_collection_add_geometry, sfcgal_geometry_collection_create,
7 sfcgal_geometry_collection_geometry_n, sfcgal_geometry_collection_num_geometries,
8 sfcgal_geometry_delete, sfcgal_geometry_t, sfcgal_linestring_add_point,
9 sfcgal_linestring_create, sfcgal_linestring_num_points, sfcgal_linestring_point_n,
10 sfcgal_multi_linestring_create, sfcgal_multi_point_create, sfcgal_multi_polygon_create,
11 sfcgal_point_create_from_xy, sfcgal_point_create_from_xyz, sfcgal_point_x, sfcgal_point_y,
12 sfcgal_point_z, sfcgal_polygon_add_interior_ring, sfcgal_polygon_create_from_exterior_ring,
13 sfcgal_polygon_exterior_ring, sfcgal_polygon_interior_ring_n,
14 sfcgal_polygon_num_interior_rings, sfcgal_polyhedral_surface_add_polygon,
15 sfcgal_polyhedral_surface_create, sfcgal_polyhedral_surface_num_polygons,
16 sfcgal_polyhedral_surface_polygon_n, sfcgal_solid_add_interior_shell, sfcgal_solid_create,
17 sfcgal_solid_create_from_exterior_shell, sfcgal_solid_num_shells, sfcgal_solid_shell_n,
18 sfcgal_triangle_create, sfcgal_triangle_set_vertex, sfcgal_triangle_vertex,
19 sfcgal_triangulated_surface_add_triangle, sfcgal_triangulated_surface_create,
20 sfcgal_triangulated_surface_num_triangles, sfcgal_triangulated_surface_triangle_n,
21};
22
23pub trait CoordType {}
24impl CoordType for Point2d {}
25impl CoordType for Point3d {}
26
27pub trait FromSFCGALGeom {
29 fn from_sfcgeometry(geom: *const sfcgal_geometry_t) -> Self;
30}
31
32pub trait ToSFCGALGeom {
34 fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t>;
35}
36
37impl FromSFCGALGeom for Point2d {
38 fn from_sfcgeometry(c_geom: *const sfcgal_geometry_t) -> Point2d {
39 let x = unsafe { sfcgal_point_x(c_geom) };
40 let y = unsafe { sfcgal_point_y(c_geom) };
41 (x, y)
42 }
43}
44
45impl FromSFCGALGeom for Point3d {
46 fn from_sfcgeometry(c_geom: *const sfcgal_geometry_t) -> Point3d {
47 let x = unsafe { sfcgal_point_x(c_geom) };
48 let y = unsafe { sfcgal_point_y(c_geom) };
49 let z = unsafe { sfcgal_point_z(c_geom) };
50 (x, y, z)
51 }
52}
53
54impl ToSFCGALGeom for Point2d {
55 fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> {
56 let g = unsafe { sfcgal_point_create_from_xy(self.0, self.1) };
57 check_null_geom(g)?;
58 Ok(g)
59 }
60}
61
62impl ToSFCGALGeom for Point3d {
63 fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> {
64 let g = unsafe { sfcgal_point_create_from_xyz(self.0, self.1, self.2) };
65 check_null_geom(g)?;
66 Ok(g)
67 }
68}
69
70impl<T> ToSFCGALGeom for Vec<T>
71where
72 T: ToSFCGALGeom + CoordType,
73{
74 fn to_sfcgeometry(&self) -> Result<*mut sfcgal_geometry_t> {
75 let out_linestring = unsafe { sfcgal_linestring_create() };
76 check_null_geom(out_linestring)?;
77 for point in self {
78 let sf_pt_geom: *mut sfcgal_geometry_t = point.to_sfcgeometry()?;
79 unsafe { sfcgal_linestring_add_point(out_linestring, sf_pt_geom) };
80 }
81 Ok(out_linestring)
82 }
83}
84
85#[derive(Debug, Clone, Hash, PartialEq)]
103pub enum CoordSeq<T> {
104 Point(T),
106 Linestring(Vec<T>),
108 Polygon(Vec<Vec<T>>),
111 Multipoint(Vec<T>),
113 Multilinestring(Vec<Vec<T>>),
115 Multipolygon(Vec<Vec<Vec<T>>>),
117 Geometrycollection(Vec<CoordSeq<T>>),
119 Polyhedralsurface(Vec<Vec<Vec<T>>>),
121 Triangulatedsurface(Vec<Vec<T>>),
123 Triangle(Vec<T>),
125 Solid(Vec<Vec<Vec<Vec<T>>>>),
128 Multisolid(Vec<Vec<Vec<Vec<Vec<T>>>>>),
130}
131
132fn coords_polygon_to_sfcgal<T>(rings: &[Vec<T>]) -> Result<*mut sfcgal_geometry_t>
133where
134 T: ToSFCGALGeom + CoordType,
135{
136 let out_polygon =
137 unsafe { sfcgal_polygon_create_from_exterior_ring(rings[0].to_sfcgeometry()?) };
138 check_null_geom(out_polygon)?;
139 for ring in rings.iter().skip(1) {
140 unsafe { sfcgal_polygon_add_interior_ring(out_polygon, ring.to_sfcgeometry()?) };
141 }
142 Ok(out_polygon)
143}
144
145fn coords_polyhedralsurface_to_sfcgal<T>(
146 polyhedres: &[Vec<Vec<T>>],
147) -> Result<*mut sfcgal_geometry_t>
148where
149 T: ToSFCGALGeom + CoordType,
150{
151 let out_surf = unsafe { sfcgal_polyhedral_surface_create() };
152 check_null_geom(out_surf)?;
153 for poly in polyhedres {
154 unsafe { sfcgal_polyhedral_surface_add_polygon(out_surf, coords_polygon_to_sfcgal(poly)?) };
155 }
156 Ok(out_surf)
157}
158
159unsafe fn pts_to_triangle_sfcgal<T>(pts: &[T]) -> Result<*mut sfcgal_geometry_t>
160where
161 T: ToSFCGALGeom,
162{
163 let out_triangle = sfcgal_triangle_create();
164 check_null_geom(out_triangle)?;
165 let p0: *mut sfcgal_geometry_t = pts[0].to_sfcgeometry()?;
166 let p1: *mut sfcgal_geometry_t = pts[1].to_sfcgeometry()?;
167 let p2: *mut sfcgal_geometry_t = pts[2].to_sfcgeometry()?;
168 sfcgal_triangle_set_vertex(out_triangle, 0, p0);
169 sfcgal_triangle_set_vertex(out_triangle, 1, p1);
170 sfcgal_triangle_set_vertex(out_triangle, 2, p2);
171 sfcgal_geometry_delete(p0);
172 sfcgal_geometry_delete(p1);
173 sfcgal_geometry_delete(p2);
174 Ok(out_triangle)
175}
176
177impl<T: ToSFCGALGeom + CoordType> ToSFCGAL for CoordSeq<T> {
183 fn to_sfcgal(&self) -> Result<SFCGeometry> {
188 match self {
189 CoordSeq::Point(pt) => unsafe { SFCGeometry::new_from_raw(pt.to_sfcgeometry()?, true) },
190 CoordSeq::Multipoint(pts) => make_sfcgal_multi_geom!(
191 sfcgal_multi_point_create(),
192 pts.iter()
193 .map(|p| p.to_sfcgeometry())
194 .collect::<Result<Vec<_>>>()?
195 ),
196 CoordSeq::Linestring(pts) => {
197 let out_linestring = pts.to_sfcgeometry()?;
198 unsafe { SFCGeometry::new_from_raw(out_linestring, true) }
199 }
200 CoordSeq::Multilinestring(ref linestring_list) => make_sfcgal_multi_geom!(
201 sfcgal_multi_linestring_create(),
202 linestring_list
203 .iter()
204 .map(|l| l.to_sfcgeometry())
205 .collect::<Result<Vec<_>>>()?
206 ),
207 CoordSeq::Polygon(ref rings) => {
208 let out_polygon = coords_polygon_to_sfcgal(rings)?;
209 unsafe { SFCGeometry::new_from_raw(out_polygon, true) }
210 }
211 CoordSeq::Multipolygon(ref polygons) => make_sfcgal_multi_geom!(
212 sfcgal_multi_polygon_create(),
213 polygons
214 .iter()
215 .map(|p| coords_polygon_to_sfcgal(p))
216 .collect::<Result<Vec<_>>>()?
217 ),
218 CoordSeq::Geometrycollection(ref geoms) => {
219 let out_geom_collection = unsafe { sfcgal_geometry_collection_create() };
220 check_null_geom(out_geom_collection)?;
221 for g_geom in geoms {
222 let mut sfcgeom = g_geom.to_sfcgal()?;
223 unsafe {
224 sfcgeom.owned = false;
228 sfcgal_geometry_collection_add_geometry(
229 out_geom_collection,
230 sfcgeom.c_geom.as_ptr(),
231 );
232 };
233 }
234 unsafe { SFCGeometry::new_from_raw(out_geom_collection, true) }
235 }
236 CoordSeq::Triangle(ref pts) => unsafe {
237 SFCGeometry::new_from_raw(pts_to_triangle_sfcgal(pts)?, true)
238 },
239 CoordSeq::Triangulatedsurface(ref triangles) => unsafe {
240 let out_surface = sfcgal_triangulated_surface_create();
241 check_null_geom(out_surface)?;
242 triangles
243 .iter()
244 .map(|pts| {
245 sfcgal_triangulated_surface_add_triangle(
246 out_surface,
247 pts_to_triangle_sfcgal(pts)?,
248 );
249 Ok(())
250 })
251 .collect::<Result<Vec<_>>>()?;
252 SFCGeometry::new_from_raw(out_surface, true)
253 },
254 CoordSeq::Polyhedralsurface(ref polygons) => {
255 let out_surface = coords_polyhedralsurface_to_sfcgal(polygons)?;
256 unsafe { SFCGeometry::new_from_raw(out_surface, true) }
257 }
258 CoordSeq::Solid(ref polyhedres) => {
259 let n_part = polyhedres.len();
260 let out_solid = if n_part == 0 {
261 unsafe { sfcgal_solid_create() }
262 } else if n_part == 1 {
263 let exterior = coords_polyhedralsurface_to_sfcgal(&polyhedres[0])?;
264 unsafe { sfcgal_solid_create_from_exterior_shell(exterior) }
265 } else {
266 let exterior = coords_polyhedralsurface_to_sfcgal(&polyhedres[0])?;
267 let r_solid = unsafe { sfcgal_solid_create_from_exterior_shell(exterior) };
268 polyhedres
269 .iter()
270 .skip(1)
271 .map(|poly| {
272 unsafe {
273 sfcgal_solid_add_interior_shell(
274 r_solid,
275 coords_polyhedralsurface_to_sfcgal(poly)?,
276 )
277 };
278 Ok(())
279 })
280 .collect::<Result<Vec<_>>>()?;
281 r_solid
282 };
283 unsafe { SFCGeometry::new_from_raw(out_solid, true) }
284 }
285 _ => unimplemented!(),
286 }
287 }
288}
289
290macro_rules! sfcgal_pt_to_coords {
291 ($geom: expr, T) => {{
292 T::from_sfcgeometry($geom)
293 }};
294}
295
296macro_rules! sfcgal_line_to_coords {
297 ($geom: expr, $type_pt: ident) => {{
298 let g = $geom;
299 let n_points = unsafe { sfcgal_linestring_num_points(g) };
300 let mut v_points = Vec::with_capacity(n_points as usize);
301 for i in 0..n_points {
302 let pt_sfcgal = unsafe { sfcgal_linestring_point_n(g, i) };
303 v_points.push(sfcgal_pt_to_coords!(pt_sfcgal, $type_pt));
305 }
306 v_points
307 }};
308}
309
310macro_rules! sfcgal_polygon_to_coords {
311 ($geom: expr, $type_pt: ident) => {{
312 let g = $geom;
313 let nrings = unsafe { sfcgal_polygon_num_interior_rings(g) };
314 let exterior_sfcgal = unsafe { sfcgal_polygon_exterior_ring(g) };
315 let mut rings = Vec::with_capacity(nrings as usize + 1);
316 rings.push(sfcgal_line_to_coords!(exterior_sfcgal, $type_pt));
317 for ix in 0..nrings {
318 let line_sfcgal = unsafe { sfcgal_polygon_interior_ring_n(g, ix) };
319 rings.push(sfcgal_line_to_coords!(line_sfcgal, $type_pt));
320 }
321 rings
322 }};
323}
324
325macro_rules! sfcgal_triangle_to_coords {
326 ($geom: expr, $type_pt: ident) => {{
327 let g = $geom;
328 let (p0, p1, p2) = unsafe {
329 (
330 sfcgal_pt_to_coords!(sfcgal_triangle_vertex(g, 0), $type_pt),
331 sfcgal_pt_to_coords!(sfcgal_triangle_vertex(g, 1), $type_pt),
332 sfcgal_pt_to_coords!(sfcgal_triangle_vertex(g, 2), $type_pt),
333 )
334 };
335 vec![p0, p1, p2]
336 }};
337}
338
339macro_rules! sfcgal_polyhedral_surface_to_coords {
340 ($geom: expr, $type_pt: ident) => {{
341 let g = $geom;
342 let ngeoms = unsafe { sfcgal_polyhedral_surface_num_polygons(g) };
343 let mut polygons = Vec::with_capacity(ngeoms as usize);
344 for ix in 0..ngeoms {
345 let poly = unsafe { sfcgal_polyhedral_surface_polygon_n(g, ix) };
346 polygons.push(sfcgal_polygon_to_coords!(poly, $type_pt));
347 }
348 polygons
349 }};
350}
351
352fn get_nb_geometry(geom: *const sfcgal_geometry_t) -> usize {
353 unsafe { sfcgal_geometry_collection_num_geometries(geom) }
354}
355
356fn get_geom_at_index(geom: *const sfcgal_geometry_t, ix: usize) -> *const sfcgal_geometry_t {
357 unsafe { sfcgal_geometry_collection_geometry_n(geom, ix) }
358}
359
360impl ToCoordinates for SFCGeometry {
367 fn to_coordinates<T>(&self) -> Result<CoordSeq<T>>
368 where
369 T: FromSFCGALGeom + CoordType,
370 {
371 match self._type()? {
372 GeomType::Point => Ok(CoordSeq::Point(sfcgal_pt_to_coords!(
373 unsafe { self.c_geom.as_ref() },
374 T
375 ))),
376 GeomType::Multipoint => {
377 let geom = unsafe { self.c_geom.as_ref() };
378 let ngeoms = get_nb_geometry(geom);
379 let mut pts = Vec::with_capacity(ngeoms);
380 for ix in 0..ngeoms {
381 pts.push(sfcgal_pt_to_coords!(get_geom_at_index(geom, ix), T));
382 }
383 Ok(CoordSeq::Multipoint(pts))
384 }
385 GeomType::Linestring => Ok(CoordSeq::Linestring(sfcgal_line_to_coords!(
386 unsafe { self.c_geom.as_ref() },
387 T
388 ))),
389 GeomType::Multilinestring => {
390 let geom = unsafe { self.c_geom.as_ref() };
391 let ngeoms = get_nb_geometry(geom);
392 let mut lines = Vec::with_capacity(ngeoms);
393 for ix in 0..ngeoms {
394 lines.push(sfcgal_line_to_coords!(get_geom_at_index(geom, ix), T));
395 }
396 Ok(CoordSeq::Multilinestring(lines))
397 }
398 GeomType::Polygon => {
399 let geom = unsafe { self.c_geom.as_ref() };
400 Ok(CoordSeq::Polygon(sfcgal_polygon_to_coords!(geom, T)))
401 }
402 GeomType::Multipolygon => {
403 let geom = unsafe { self.c_geom.as_ref() };
404 let ngeoms = get_nb_geometry(geom);
405 let mut polygons = Vec::with_capacity(ngeoms);
406 for ix in 0..ngeoms {
407 polygons.push(sfcgal_polygon_to_coords!(get_geom_at_index(geom, ix), T));
408 }
409 Ok(CoordSeq::Multipolygon(polygons))
410 }
411 GeomType::Geometrycollection => {
412 let geom = unsafe { self.c_geom.as_ref() };
413 let ngeoms = get_nb_geometry(geom);
414 let mut geoms = Vec::with_capacity(ngeoms);
415 for ix in 0..ngeoms {
416 let inner_geom = unsafe {
417 SFCGeometry::new_from_raw(
419 sfcgal_geometry_collection_geometry_n(geom, ix)
420 as *mut sfcgal_geometry_t,
421 false,
422 )?
423 };
424 let coords: CoordSeq<T> = inner_geom.to_coordinates()?;
425 geoms.push(coords);
426 }
427 Ok(CoordSeq::Geometrycollection(geoms))
428 }
429 GeomType::Triangle => Ok(CoordSeq::Triangle(sfcgal_triangle_to_coords!(
430 unsafe { self.c_geom.as_ref() },
431 T
432 ))),
433 GeomType::Triangulatedsurface => {
434 let geom = unsafe { self.c_geom.as_ref() };
435 let ngeoms = unsafe { sfcgal_triangulated_surface_num_triangles(geom) };
436 let mut triangles = Vec::with_capacity(ngeoms);
437 for ix in 0..ngeoms {
438 let triangle = unsafe { sfcgal_triangulated_surface_triangle_n(geom, ix) };
439 triangles.push(sfcgal_triangle_to_coords!(triangle, T));
440 }
441 Ok(CoordSeq::Triangulatedsurface(triangles))
442 }
443 GeomType::Polyhedralsurface => Ok(CoordSeq::Polyhedralsurface(
444 sfcgal_polyhedral_surface_to_coords!(unsafe { self.c_geom.as_ref() }, T),
445 )),
446 GeomType::Solid => {
447 let geom = unsafe { self.c_geom.as_ref() };
448 let ngeoms = unsafe { sfcgal_solid_num_shells(geom) };
449 let mut polyhedres = Vec::with_capacity(ngeoms);
450 for ix in 0..ngeoms {
451 let poly = unsafe { sfcgal_solid_shell_n(geom, ix) };
452 polyhedres.push(sfcgal_polyhedral_surface_to_coords!(poly, T));
453 }
454 Ok(CoordSeq::Solid(polyhedres))
455 }
456 GeomType::Multisolid => {
457 let geom_ms = unsafe { self.c_geom.as_ref() };
458 let n_solides = get_nb_geometry(geom_ms);
459 let mut solides = Vec::with_capacity(n_solides);
460 for ix_geom in 0..n_solides {
461 let solid = get_geom_at_index(geom_ms, ix_geom);
462 let n_shell = unsafe { sfcgal_solid_num_shells(solid) };
463 let mut polyhedres = Vec::with_capacity(n_shell);
464 for ix in 0..n_shell {
465 let poly = unsafe { sfcgal_solid_shell_n(solid, ix) };
466 polyhedres.push(sfcgal_polyhedral_surface_to_coords!(poly, T));
467 }
468 solides.push(polyhedres);
469 }
470 Ok(CoordSeq::Multisolid(solides))
471 }
472 }
473 }
474}
475
476#[cfg(test)]
477mod tests {
478 use super::{Point2d, Point3d};
479 use crate::*;
480
481 #[test]
482 fn point_2d_sfcgal_to_coordinates_to_sfcgal() {
483 let input_wkt = "POINT (0.1 0.9)";
484 let pt_sfcgal = SFCGeometry::new(input_wkt).unwrap();
485 let coords: CoordSeq<Point2d> = pt_sfcgal.to_coordinates().unwrap();
486 if let CoordSeq::Point(ref coords) = coords {
487 assert_ulps_eq!(coords.0, 0.1);
488 assert_ulps_eq!(coords.1, 0.9);
489 } else {
490 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
491 }
492 let pt_sfcgal = coords.to_sfcgal().unwrap();
493 assert_eq!(input_wkt, pt_sfcgal.to_wkt_decim(1).unwrap())
494 }
495
496 #[test]
497 fn point_3d_sfcgal_to_coordinates_to_sfcgal() {
498 let input_wkt = "POINT Z (0.1 0.9 1.0)";
499 let pt_sfcgal = SFCGeometry::new(input_wkt).unwrap();
500 let coords: CoordSeq<Point3d> = pt_sfcgal.to_coordinates().unwrap();
501 if let CoordSeq::Point(ref coords) = coords {
502 assert_ulps_eq!(coords.0, 0.1);
503 assert_ulps_eq!(coords.1, 0.9);
504 assert_ulps_eq!(coords.2, 1.0);
505 } else {
506 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
507 }
508 let pt_sfcgal = coords.to_sfcgal().unwrap();
509 assert_eq!(input_wkt, pt_sfcgal.to_wkt_decim(1).unwrap())
510 }
511
512 #[test]
513 fn multipoint_2d_sfcgal_to_coordinates_to_sfcgal() {
514 let input_wkt = "MULTIPOINT ((3.5 5.6),(4.8 10.5))";
515 let multipt_sfcgal = SFCGeometry::new(input_wkt).unwrap();
516 let coords: CoordSeq<Point2d> = multipt_sfcgal.to_coordinates().unwrap();
517 if let CoordSeq::Multipoint(ref coords) = coords {
518 assert_ulps_eq!(coords[0].0, 3.5);
519 assert_ulps_eq!(coords[0].1, 5.6);
520 assert_ulps_eq!(coords[1].0, 4.8);
521 assert_ulps_eq!(coords[1].1, 10.5);
522 } else {
523 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
524 }
525 let mpt_sfcgal = coords.to_sfcgal().unwrap();
526 assert_eq!(input_wkt, mpt_sfcgal.to_wkt_decim(1).unwrap());
527 }
528
529 #[test]
530 fn multipoint_3d_sfcgal_to_coordinates_to_sfcgal() {
531 let input_wkt = "MULTIPOINT Z ((3.5 5.6 1.0),(4.8 10.5 1.0))";
532 let multipt_sfcgal = SFCGeometry::new(input_wkt).unwrap();
533 let coords: CoordSeq<Point3d> = multipt_sfcgal.to_coordinates().unwrap();
534 if let CoordSeq::Multipoint(ref coords) = coords {
535 assert_ulps_eq!(coords[0].0, 3.5);
536 assert_ulps_eq!(coords[0].1, 5.6);
537 assert_ulps_eq!(coords[0].2, 1.0);
538 assert_ulps_eq!(coords[1].0, 4.8);
539 assert_ulps_eq!(coords[1].1, 10.5);
540 assert_ulps_eq!(coords[1].2, 1.0);
541 } else {
542 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
543 }
544 let mpt_sfcgal = coords.to_sfcgal().unwrap();
545 assert_eq!(input_wkt, mpt_sfcgal.to_wkt_decim(1).unwrap());
546 }
547
548 #[test]
549 fn linestring_2d_sfcgal_to_coordinates() {
550 let input_wkt = "LINESTRING (3.5 5.6,4.8 10.5)";
551 let linestring_sfcgal = SFCGeometry::new(input_wkt).unwrap();
552 let coords: CoordSeq<Point2d> = linestring_sfcgal.to_coordinates().unwrap();
553 if let CoordSeq::Linestring(ref coords) = coords {
554 assert_ulps_eq!(coords[0].0, 3.5);
555 assert_ulps_eq!(coords[0].1, 5.6);
556 assert_ulps_eq!(coords[1].0, 4.8);
557 assert_ulps_eq!(coords[1].1, 10.5);
558 } else {
559 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
560 }
561 let line_sfcgal = coords.to_sfcgal().unwrap();
562 assert_eq!(input_wkt, line_sfcgal.to_wkt_decim(1).unwrap());
563 }
564
565 #[test]
566 fn linestring_3d_sfcgal_to_coordinates() {
567 let input_wkt = "LINESTRING Z (3.5 5.6 1.0,4.8 10.5 1.0)";
568 let linestring_sfcgal = SFCGeometry::new(input_wkt).unwrap();
569 let coords: CoordSeq<Point3d> = linestring_sfcgal.to_coordinates().unwrap();
570 if let CoordSeq::Linestring(ref coords) = coords {
571 assert_ulps_eq!(coords[0].0, 3.5);
572 assert_ulps_eq!(coords[0].1, 5.6);
573 assert_ulps_eq!(coords[0].2, 1.0);
574 assert_ulps_eq!(coords[1].0, 4.8);
575 assert_ulps_eq!(coords[1].1, 10.5);
576 assert_ulps_eq!(coords[1].2, 1.0);
577 } else {
578 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
579 }
580 let line_sfcgal = coords.to_sfcgal().unwrap();
581 assert_eq!(input_wkt, line_sfcgal.to_wkt_decim(1).unwrap());
582 }
583
584 #[test]
585 fn multilinestring_2d_sfcgal_to_coordinates() {
586 let input_wkt = "MULTILINESTRING ((3.5 5.6,4.8 10.5),(8.9 12.9,2.1 3.5),(1.1 4.8,6.2 7.5))";
587 let mls_sfcgal = SFCGeometry::new(input_wkt).unwrap();
588 let coords: CoordSeq<Point2d> = mls_sfcgal.to_coordinates().unwrap();
589 if let CoordSeq::Multilinestring(ref coords) = coords {
590 assert_ulps_eq!(coords[0][0].0, 3.5);
591 assert_ulps_eq!(coords[0][0].1, 5.6);
592 assert_ulps_eq!(coords[1][1].0, 2.1);
593 assert_ulps_eq!(coords[1][1].1, 3.5);
594 assert_ulps_eq!(coords[2][0].0, 1.1);
595 assert_ulps_eq!(coords[2][0].1, 4.8);
596 } else {
597 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
598 }
599 let mls_sfcgal = coords.to_sfcgal().unwrap();
600 assert_eq!(input_wkt, mls_sfcgal.to_wkt_decim(1).unwrap());
601 }
602
603 #[test]
604 fn multilinestring_3d_sfcgal_to_coordinates() {
605 let input_wkt = "MULTILINESTRING Z ((3.5 5.6 1.0,4.8 10.5 1.0),(8.9 12.9 4.0,2.1 3.5 4.0),(1.1 4.8 4.0,6.2 7.5 4.0))";
606 let multilinestring_sfcgal = SFCGeometry::new(input_wkt).unwrap();
607 let coords: CoordSeq<Point3d> = multilinestring_sfcgal.to_coordinates().unwrap();
608 if let CoordSeq::Multilinestring(ref coords) = coords {
609 assert_ulps_eq!(coords[0][0].0, 3.5);
610 assert_ulps_eq!(coords[0][0].1, 5.6);
611 assert_ulps_eq!(coords[0][0].2, 1.0);
612 assert_ulps_eq!(coords[1][1].0, 2.1);
613 assert_ulps_eq!(coords[1][1].1, 3.5);
614 assert_ulps_eq!(coords[1][1].2, 4.0);
615 assert_ulps_eq!(coords[2][0].0, 1.1);
616 assert_ulps_eq!(coords[2][0].1, 4.8);
617 assert_ulps_eq!(coords[2][0].2, 4.0);
618 } else {
619 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
620 }
621 let mls_sfcgal = coords.to_sfcgal().unwrap();
622 assert_eq!(input_wkt, mls_sfcgal.to_wkt_decim(1).unwrap());
623 }
624
625 #[test]
626 fn triangle_2d_sfcgal_to_coordinates_to_sfcgal() {
627 let input_wkt = "TRIANGLE ((0.0 1.0,1.0 0.0,1.0 1.0,0.0 1.0))";
628 let triangle_sfcgal = SFCGeometry::new(input_wkt).unwrap();
629 let coords: CoordSeq<Point2d> = triangle_sfcgal.to_coordinates().unwrap();
630 if let CoordSeq::Triangle(ref pts) = coords {
631 assert_ulps_eq!(pts[0].0, 0.0);
632 assert_ulps_eq!(pts[0].1, 1.0);
633 assert_ulps_eq!(pts[1].0, 1.0);
634 assert_ulps_eq!(pts[1].1, 0.0);
635 assert_ulps_eq!(pts[2].0, 1.0);
636 assert_ulps_eq!(pts[2].1, 1.0);
637 } else {
638 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
639 }
640 let triangle_sfcgal = coords.to_sfcgal().unwrap();
641 assert_eq!(input_wkt, triangle_sfcgal.to_wkt_decim(1).unwrap());
642 }
643
644 #[test]
645 fn solid_3d_sfcgal_to_coordinates() {
646 let input_wkt = "SOLID Z ((((0 0 0,0 0 1,0 1 1,0 1 0,0 0 0)),\
647 ((0 0 0,0 1 0,1 1 0,1 0 0,0 0 0)),\
648 ((0 0 0,1 0 0,1 0 1,0 0 1,0 0 0)),\
649 ((1 0 0,1 1 0,1 1 1,1 0 1,1 0 0)),\
650 ((0 0 1,1 0 1,1 1 1,0 1 1,0 0 1)),\
651 ((0 1 0,0 1 1,1 1 1,1 1 0,0 1 0))))";
652 let cube = SFCGeometry::new(input_wkt).unwrap();
653 let coords: CoordSeq<Point3d> = cube.to_coordinates().unwrap();
654 if let CoordSeq::Solid(ref polys) = coords {
655 let coords_1st_face_polygon = &polys[0][0];
656 assert_eq!(
657 format!("{:?}", coords_1st_face_polygon),
658 "[[(0.0, 0.0, 0.0), (0.0, 0.0, 1.0), (0.0, 1.0, 1.0), (0.0, 1.0, 0.0), (0.0, 0.0, 0.0)]]");
659 assert_eq!(polys[0].len(), 6usize);
660 } else {
661 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
662 }
663 let cube_sfcgal = coords.to_sfcgal().unwrap();
664 assert_eq!(input_wkt, cube_sfcgal.to_wkt_decim(0).unwrap());
665 }
666
667 #[test]
668 fn solid_with_interior_shell_3d_sfcgal_to_coordinates() {
669 let input_wkt = "SOLID Z ((\
670 ((0.0 0.0 0.0,0.0 1.0 0.0,1.0 1.0 0.0,1.0 0.0 0.0,0.0 0.0 0.0)),\
671 ((1.0 0.0 0.0,1.0 1.0 0.0,1.0 1.0 1.0,1.0 0.0 1.0,1.0 0.0 0.0)),\
672 ((0.0 1.0 0.0,0.0 1.0 1.0,1.0 1.0 1.0,1.0 1.0 0.0,0.0 1.0 0.0)),\
673 ((0.0 0.0 1.0,0.0 1.0 1.0,0.0 1.0 0.0,0.0 0.0 0.0,0.0 0.0 1.0)),\
674 ((1.0 0.0 1.0,1.0 1.0 1.0,0.0 1.0 1.0,0.0 0.0 1.0,1.0 0.0 1.0)),\
675 ((1.0 0.0 0.0,1.0 0.0 1.0,0.0 0.0 1.0,0.0 0.0 0.0,1.0 0.0 0.0))\
676 ),(\
677 ((0.0 0.0 0.0,0.0 0.5 0.0,0.5 0.5 0.0,0.5 0.0 0.0,0.0 0.0 0.0)),\
678 ((0.5 0.0 0.0,0.5 0.5 0.0,0.5 0.5 0.5,0.5 0.0 0.5,0.5 0.0 0.0)),\
679 ((0.0 0.5 0.0,0.0 0.5 0.5,0.5 0.5 0.5,0.5 0.5 0.0,0.0 0.5 0.0)),\
680 ((0.0 0.0 0.5,0.0 0.5 0.5,0.0 0.5 0.0,0.0 0.0 0.0,0.0 0.0 0.5)),\
681 ((0.5 0.0 0.5,0.5 0.5 0.5,0.0 0.5 0.5,0.0 0.0 0.5,0.5 0.0 0.5)),\
682 ((0.5 0.0 0.0,0.5 0.0 0.5,0.0 0.0 0.5,0.0 0.0 0.0,0.5 0.0 0.0))\
683 ))";
684 let cube = SFCGeometry::new(input_wkt).unwrap();
685 let coords: CoordSeq<Point3d> = cube.to_coordinates().unwrap();
686 if let CoordSeq::Solid(ref polys) = coords {
687 let num_shell = polys.len();
688 assert_eq!(num_shell, 2usize);
689 let coords_1st_face_polygon = &polys[0][0];
690 assert_eq!(
691 format!("{:?}", coords_1st_face_polygon),
692 "[[(0.0, 0.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 0.0, 0.0), (0.0, 0.0, 0.0)]]");
693 assert_eq!(polys[0].len(), 6usize);
694 } else {
695 panic!("Unexpected coordinates when converting from SFCGAL to coordinates as tuples.")
696 }
697 let cube_sfcgal = coords.to_sfcgal().unwrap();
698 assert_eq!(input_wkt, cube_sfcgal.to_wkt_decim(1).unwrap());
699 }
700
701 #[test]
702 fn geometrycollection_3d_sfcgal_to_coordinates() {
703 let input_wkt =
704 "GEOMETRYCOLLECTION Z (POINT Z (4.0 6.0 1.0),LINESTRING Z (4.0 6.0 1.0,7.0 10.0 1.0))";
705 let gc_sfcgal = SFCGeometry::new(input_wkt).unwrap();
706 let coords: CoordSeq<Point3d> = gc_sfcgal.to_coordinates().unwrap();
707 if let CoordSeq::Geometrycollection(ref seq_coords) = coords {
708 if let CoordSeq::Point(ref coords) = &seq_coords[0] {
709 assert_ulps_eq!(coords.0, 4.0);
710 assert_ulps_eq!(coords.1, 6.0);
711 assert_ulps_eq!(coords.2, 1.0);
712 } else {
713 panic!("Error with first member of geometry collection");
714 }
715 if let CoordSeq::Linestring(ref coords) = &seq_coords[1] {
716 assert_ulps_eq!(coords[0].0, 4.0);
717 assert_ulps_eq!(coords[0].1, 6.0);
718 assert_ulps_eq!(coords[0].2, 1.0);
719 assert_ulps_eq!(coords[1].0, 7.0);
720 assert_ulps_eq!(coords[1].1, 10.0);
721 assert_ulps_eq!(coords[1].2, 1.0);
722 } else {
723 panic!("Error with second member of geometry collection");
724 }
725 }
726 let gc_sfcgal = coords.to_sfcgal().unwrap();
727 assert_eq!(input_wkt, gc_sfcgal.to_wkt_decim(1).unwrap());
728 }
729
730 #[test]
731 fn sfcgal_geometry_construction_from_coordinates() {
732 let coords_point = (1.1, 1.1);
733 let coords_multipoint = vec![(1.1, 1.1), (2.2, 2.2)];
734 let coords_linestring = vec![(1.1, 1.1), (2.2, 2.2)];
735 let coords_multilinestring = vec![
736 vec![(1.1, 1.1), (2.2, 2.2)],
737 vec![(3.1, 3.1), (5.2, 5.2), (5.2, 5.2)],
738 vec![(1.1, 1.1), (2.2, 2.2), (5.2, 5.2)],
739 ];
740 let coords_polygon = vec![
741 vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)], vec![(0.1, 0.1), (0.1, 0.9), (0.9, 0.9), (0.9, 0.1), (0.1, 0.1)], ];
744 let coords_multipolygon = vec![
745 vec![
746 vec![(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)], vec![(0.1, 0.1), (0.1, 0.9), (0.9, 0.9), (0.9, 0.1), (0.1, 0.1)], ],
750 vec![
751 vec![(2., 2.), (6., 2.), (6., 6.), (2., 6.), (2., 2.)], ],
754 ];
755 let pt_sfcgal = CoordSeq::Point(coords_point).to_sfcgal().unwrap();
756 let multipt_sfcgal = CoordSeq::Multipoint(coords_multipoint).to_sfcgal().unwrap();
757 let line_sfcgal = CoordSeq::Linestring(coords_linestring).to_sfcgal().unwrap();
758 let multiline_sfcgal = CoordSeq::Multilinestring(coords_multilinestring)
759 .to_sfcgal()
760 .unwrap();
761 let polygon_sfcgal = CoordSeq::Polygon(coords_polygon).to_sfcgal().unwrap();
762 let multipolygon_sfcgal = CoordSeq::Multipolygon(coords_multipolygon)
763 .to_sfcgal()
764 .unwrap();
765 assert_eq!("POINT (1.1 1.1)", pt_sfcgal.to_wkt_decim(1).unwrap());
766 assert_eq!(
767 "MULTIPOINT ((1.1 1.1),(2.2 2.2))",
768 multipt_sfcgal.to_wkt_decim(1).unwrap()
769 );
770 assert_eq!(
771 "LINESTRING (1.1 1.1,2.2 2.2)",
772 line_sfcgal.to_wkt_decim(1).unwrap()
773 );
774 assert_eq!(
775 "MULTILINESTRING ((1.1 1.1,2.2 2.2),\
776 (3.1 3.1,5.2 5.2,5.2 5.2),\
777 (1.1 1.1,2.2 2.2,5.2 5.2))",
778 multiline_sfcgal.to_wkt_decim(1).unwrap(),
779 );
780 assert_eq!(
781 "POLYGON ((0.0 0.0,1.0 0.0,1.0 1.0,0.0 1.0,0.0 0.0),\
782 (0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1))",
783 polygon_sfcgal.to_wkt_decim(1).unwrap(),
784 );
785 assert_eq!(
786 "MULTIPOLYGON (((0.0 0.0,1.0 0.0,1.0 1.0,0.0 1.0,0.0 0.0),\
787 (0.1 0.1,0.1 0.9,0.9 0.9,0.9 0.1,0.1 0.1)),\
788 ((2.0 2.0,6.0 2.0,6.0 6.0,2.0 6.0,2.0 2.0)))",
789 multipolygon_sfcgal.to_wkt_decim(1).unwrap(),
790 );
791 }
792
793 #[test]
794 fn sfcgal_geometry_construction_from_coordinates_3d() {
795 let coords_point = (1.1, 1.1, 1.1);
796 let coords_multipoint = vec![(1.1, 1.1, 5.0), (2.2, 2.2, 5.0)];
797 let coords_linestring = vec![(1.1, 1.1, 5.0), (2.2, 2.2, 5.0)];
798 let coords_multilinestring = vec![
799 vec![(1.1, 1.1, 3.0), (2.2, 2.2, 3.0)],
800 vec![(3.1, 3.1, 3.0), (5.2, 5.2, 3.0), (5.2, 5.2, 3.0)],
801 vec![(1.1, 1.1, 3.0), (2.2, 2.2, 3.0), (5.2, 5.2, 3.0)],
802 ];
803 let coords_polygon = vec![
804 vec![
805 (0., 0., 3.0),
806 (1., 0., 3.0),
807 (1., 1., 3.0),
808 (0., 1., 3.0),
809 (0., 0., 3.0),
810 ], vec![
812 (0.1, 0.1, 3.0),
813 (0.1, 0.9, 3.0),
814 (0.9, 0.9, 3.0),
815 (0.9, 0.1, 3.0),
816 (0.1, 0.1, 3.0),
817 ], ];
819 let coords_multipolygon = vec![
820 vec![
821 vec![
823 (0., 0., 3.0),
824 (1., 0., 3.0),
825 (1., 1., 3.0),
826 (0., 1., 3.0),
827 (0., 0., 3.0),
828 ], vec![
830 (0.1, 0.1, 3.0),
831 (0.1, 0.9, 3.0),
832 (0.9, 0.9, 3.0),
833 (0.9, 0.1, 3.0),
834 (0.1, 0.1, 3.0),
835 ], ],
837 vec![
838 vec![
840 (2., 2., 3.),
841 (6., 2., 3.),
842 (6., 6., 3.),
843 (2., 6., 3.),
844 (2., 2., 3.),
845 ], ],
847 ];
848 let pt_sfcgal = CoordSeq::Point(coords_point).to_sfcgal().unwrap();
849 let multipt_sfcgal = CoordSeq::Multipoint(coords_multipoint).to_sfcgal().unwrap();
850 let line_sfcgal = CoordSeq::Linestring(coords_linestring).to_sfcgal().unwrap();
851 let multiline_sfcgal = CoordSeq::Multilinestring(coords_multilinestring)
852 .to_sfcgal()
853 .unwrap();
854 let polygon_sfcgal = CoordSeq::Polygon(coords_polygon).to_sfcgal().unwrap();
855 let multipolygon_sfcgal = CoordSeq::Multipolygon(coords_multipolygon)
856 .to_sfcgal()
857 .unwrap();
858 assert_eq!("POINT Z (1.1 1.1 1.1)", pt_sfcgal.to_wkt_decim(1).unwrap());
859 assert_eq!(
860 "MULTIPOINT Z ((1.1 1.1 5.0),(2.2 2.2 5.0))",
861 multipt_sfcgal.to_wkt_decim(1).unwrap()
862 );
863 assert_eq!(
864 "LINESTRING Z (1.1 1.1 5.0,2.2 2.2 5.0)",
865 line_sfcgal.to_wkt_decim(1).unwrap()
866 );
867 assert_eq!(
868 "MULTILINESTRING Z ((1.1 1.1 3.0,2.2 2.2 3.0),(3.1 3.1 3.0,5.2 5.2 3.0,5.2 5.2 3.0),\
869 (1.1 1.1 3.0,2.2 2.2 3.0,5.2 5.2 3.0))",
870 multiline_sfcgal.to_wkt_decim(1).unwrap(),
871 );
872 assert_eq!(
873 "POLYGON Z ((0.0 0.0 3.0,1.0 0.0 3.0,1.0 1.0 3.0,0.0 1.0 3.0,0.0 0.0 3.0),\
874 (0.1 0.1 3.0,0.1 0.9 3.0,0.9 0.9 3.0,0.9 0.1 3.0,0.1 0.1 3.0))",
875 polygon_sfcgal.to_wkt_decim(1).unwrap(),
876 );
877 assert_eq!(
878 "MULTIPOLYGON Z (((0.0 0.0 3.0,1.0 0.0 3.0,1.0 1.0 3.0,0.0 1.0 3.0,0.0 0.0 3.0),\
879 (0.1 0.1 3.0,0.1 0.9 3.0,0.9 0.9 3.0,0.9 0.1 3.0,0.1 0.1 3.0)),\
880 ((2.0 2.0 3.0,6.0 2.0 3.0,6.0 6.0 3.0,2.0 6.0 3.0,2.0 2.0 3.0)))",
881 multipolygon_sfcgal.to_wkt_decim(1).unwrap(),
882 );
883 }
884}