1#![warn(missing_docs)]
4
5mod bbox;
9mod collection;
10mod line;
11mod lines;
12mod point;
13mod points;
14mod polygon;
15mod polygons;
16
17pub use bbox::*;
18pub use collection::*;
19pub use line::*;
20pub use lines::*;
21pub use point::*;
22pub use points::*;
23pub use polygon::*;
24pub use polygons::*;
25
26use crate::{MyError, config::config, crs::CRS, srid::SRID, text::cql2::wkt, wkb::*};
27use core::fmt;
28use geos::{ConstGeometry, Geom, Geometry, GeometryTypes};
29
30pub(crate) type XY1V = Vec<f64>;
32pub(crate) type XY2V = Vec<Vec<f64>>;
33pub(crate) type XY3V = Vec<Vec<Vec<f64>>>;
34pub(crate) type XY4V = Vec<Vec<Vec<Vec<f64>>>>;
35
36fn ensure_precision(x: &f64) -> f64 {
39 let d = 10.0_f64.powi(
40 config()
41 .default_precision()
42 .try_into()
43 .expect("Failed coercing DEFAULT_PRECISION"),
44 );
45 (x * d).round() / d
46}
47
48#[derive(Debug, Clone, Default, PartialEq, PartialOrd)]
50pub enum G {
51 #[default]
53 Null,
54
55 Point(Point),
57 Line(Line),
59 Polygon(Polygon),
61 Points(Points),
63 Lines(Lines),
65 Polygons(Polygons),
67 Vec(Geometries),
69 BBox(BBox),
71}
72
73pub trait GTrait {
75 fn is_2d(&self) -> bool;
77
78 fn to_wkt(&self) -> String {
85 self.to_wkt_fmt(config().default_precision())
86 }
87
88 fn to_wkt_fmt(&self, precision: usize) -> String;
107
108 fn check_coordinates(&self, crs: &CRS) -> Result<(), MyError>;
111
112 fn type_(&self) -> &str;
114
115 fn srid(&self) -> SRID;
117}
118
119impl GTrait for G {
120 fn is_2d(&self) -> bool {
121 match self {
122 G::Point(x) => x.is_2d(),
123 G::Line(x) => x.is_2d(),
124 G::Polygon(x) => x.is_2d(),
125 G::Points(x) => x.is_2d(),
126 G::Lines(x) => x.is_2d(),
127 G::Polygons(x) => x.is_2d(),
128 G::Vec(x) => x.is_2d(),
129 G::BBox(x) => x.is_2d(),
130 _ => unreachable!("N/A for this geometry type"),
131 }
132 }
133
134 fn to_wkt_fmt(&self, precision: usize) -> String {
135 match self {
136 G::Point(x) => x.to_wkt_fmt(precision),
137 G::Line(x) => x.to_wkt_fmt(precision),
138 G::Polygon(x) => x.to_wkt_fmt(precision),
139 G::Points(x) => x.to_wkt_fmt(precision),
140 G::Lines(x) => x.to_wkt_fmt(precision),
141 G::Polygons(x) => x.to_wkt_fmt(precision),
142 G::Vec(x) => x.to_wkt_fmt(precision),
143 G::BBox(x) => x.to_wkt_fmt(precision),
144 _ => unreachable!("N/A for this geometry type"),
145 }
146 }
147
148 fn check_coordinates(&self, crs: &CRS) -> Result<(), MyError> {
149 match self {
150 G::Point(x) => x.check_coordinates(crs),
151 G::Line(x) => x.check_coordinates(crs),
152 G::Polygon(x) => x.check_coordinates(crs),
153 G::Points(x) => x.check_coordinates(crs),
154 G::Lines(x) => x.check_coordinates(crs),
155 G::Polygons(x) => x.check_coordinates(crs),
156 G::Vec(x) => x.check_coordinates(crs),
157 G::BBox(x) => x.check_coordinates(crs),
158 _ => unreachable!("N/A for this geometry type"),
159 }
160 }
161
162 fn type_(&self) -> &str {
163 match self {
164 G::Point(x) => x.type_(),
165 G::Line(x) => x.type_(),
166 G::Polygon(x) => x.type_(),
167 G::Points(x) => x.type_(),
168 G::Lines(x) => x.type_(),
169 G::Polygons(x) => x.type_(),
170 G::Vec(x) => x.type_(),
171 G::BBox(x) => x.type_(),
172 _ => unreachable!("N/A for this geometry type"),
173 }
174 }
175
176 fn srid(&self) -> SRID {
177 match self {
178 G::Point(x) => x.srid(),
179 G::Line(x) => x.srid(),
180 G::Polygon(x) => x.srid(),
181 G::Points(x) => x.srid(),
182 G::Lines(x) => x.srid(),
183 G::Polygons(x) => x.srid(),
184 G::Vec(x) => x.srid(),
185 G::BBox(x) => x.srid(),
186 _ => unreachable!("N/A for this geometry type"),
187 }
188 }
189}
190
191impl G {
192 pub fn as_point(&self) -> Option<&Point> {
194 match self {
195 G::Point(x) => Some(x),
196 _ => None,
197 }
198 }
199
200 pub fn as_line(&self) -> Option<&Line> {
202 match self {
203 G::Line(x) => Some(x),
204 _ => None,
205 }
206 }
207
208 pub fn as_polygon(&self) -> Option<&Polygon> {
210 match self {
211 G::Polygon(x) => Some(x),
212 _ => None,
213 }
214 }
215
216 pub fn as_points(&self) -> Option<&Points> {
218 match self {
219 G::Points(x) => Some(x),
220 _ => None,
221 }
222 }
223
224 pub fn as_lines(&self) -> Option<&Lines> {
226 match self {
227 G::Lines(x) => Some(x),
228 _ => None,
229 }
230 }
231
232 pub fn as_polygons(&self) -> Option<&Polygons> {
234 match self {
235 G::Polygons(x) => Some(x),
236 _ => None,
237 }
238 }
239
240 pub(crate) fn to_geos(&self) -> Result<Geometry, MyError> {
243 match self {
244 G::Point(x) => x.to_geos(),
245 G::Line(x) => x.to_geos(),
246 G::Polygon(x) => x.to_geos(),
247 G::Points(x) => x.to_geos(),
248 G::Lines(x) => x.to_geos(),
249 G::Polygons(x) => x.to_geos(),
250 G::Vec(x) => x.to_geos(),
251 G::BBox(x) => x.to_geos(),
252 _ => unreachable!("N/A for this geometry type"),
253 }
254 }
255
256 pub(crate) fn intersects(&self, other: &G) -> Result<bool, MyError> {
257 let lhs = self.to_geos()?;
258 let rhs = other.to_geos()?;
259 let result = lhs.intersects(&rhs)?;
260 Ok(result)
261 }
262
263 pub(crate) fn equals(&self, other: &G) -> Result<bool, MyError> {
264 let lhs = self.to_geos()?;
265 let rhs = other.to_geos()?;
266 let result = lhs.equals(&rhs)?;
267 Ok(result)
268 }
269
270 pub(crate) fn disjoint(&self, other: &G) -> Result<bool, MyError> {
271 let lhs = self.to_geos()?;
272 let rhs = other.to_geos()?;
273 let result = lhs.disjoint(&rhs)?;
274 Ok(result)
275 }
276
277 pub(crate) fn touches(&self, other: &G) -> Result<bool, MyError> {
278 let lhs = self.to_geos()?;
279 let rhs = other.to_geos()?;
280 let result = lhs.touches(&rhs)?;
281 Ok(result)
282 }
283
284 pub(crate) fn within(&self, other: &G) -> Result<bool, MyError> {
285 let lhs = self.to_geos()?;
286 let rhs = other.to_geos()?;
287 let result = lhs.within(&rhs)?;
288 Ok(result)
289 }
290
291 pub(crate) fn overlaps(&self, other: &G) -> Result<bool, MyError> {
292 let lhs = self.to_geos()?;
293 let rhs = other.to_geos()?;
294 let result = lhs.overlaps(&rhs)?;
295 Ok(result)
296 }
297
298 pub(crate) fn crosses(&self, other: &G) -> Result<bool, MyError> {
299 let lhs = self.to_geos()?;
300 let rhs = other.to_geos()?;
301 let result = lhs.crosses(&rhs)?;
302 Ok(result)
303 }
304
305 pub(crate) fn contains(&self, other: &G) -> Result<bool, MyError> {
306 let lhs = self.to_geos()?;
307 let rhs = other.to_geos()?;
308 let result = lhs.contains(&rhs)?;
309 Ok(result)
310 }
311
312 pub(crate) fn boundary(&self) -> Result<Self, MyError> {
315 let g1 = self.to_geos()?;
316 let g2 = g1.boundary()?;
317 let it = G::try_from(g2)?;
318 Ok(it)
319 }
320
321 pub(crate) fn buffer(&self, width: f64, quadsegs: i32) -> Result<Self, MyError> {
322 let g1 = self.to_geos()?;
323 let g2 = g1.buffer(width, quadsegs)?;
324 let it = G::try_from(g2)?;
325 Ok(it)
326 }
327
328 pub(crate) fn envelope(&self) -> Result<Self, MyError> {
329 let g1 = self.to_geos()?;
330 let g2 = g1.envelope()?;
331 let it = G::try_from(g2)?;
332 Ok(it)
333 }
334
335 pub(crate) fn centroid(&self) -> Result<Self, MyError> {
336 let g1 = self.to_geos()?;
337 let g2 = g1.get_centroid()?;
338 let it = G::try_from(g2)?;
339 Ok(it)
340 }
341
342 pub(crate) fn convex_hull(&self) -> Result<Self, MyError> {
343 let g1 = self.to_geos()?;
344 let g2 = g1.convex_hull()?;
345 let it = G::try_from(g2)?;
346 Ok(it)
347 }
348
349 pub(crate) fn get_x(&self) -> Result<f64, MyError> {
350 if let Some(pt) = self.as_point() {
351 Ok(pt.x())
352 } else {
353 Err(MyError::Runtime("This is NOT a Point".into()))
354 }
355 }
356
357 pub(crate) fn get_y(&self) -> Result<f64, MyError> {
358 if let Some(pt) = self.as_point() {
359 Ok(pt.y())
360 } else {
361 Err(MyError::Runtime("This is NOT a Point".into()))
362 }
363 }
364
365 pub(crate) fn get_z(&self) -> Result<f64, MyError> {
366 if let Some(pt) = self.as_point() {
367 if let Some(z) = pt.z() {
368 Ok(z)
369 } else {
370 Err(MyError::Runtime("This is NOT a 3D Point".into()))
371 }
372 } else {
373 Err(MyError::Runtime("This is NOT a Point".into()))
374 }
375 }
376
377 #[cfg(any(feature = "gpkg_ds", feature = "pg_ds"))]
380 pub(crate) fn to_sql(&self) -> Result<String, MyError> {
381 match self {
382 G::BBox(x) => x.to_sql(),
383 x => {
384 let wkt = x.to_wkt();
385 let srid = self.srid().into_inner();
386 Ok(format!("ST_GeomFromText('{wkt}', {srid})"))
387 }
388 }
389 }
390
391 pub(crate) fn set_srid_unchecked(&mut self, srid: &SRID) {
394 match self {
395 G::Point(x) => x.set_srid_unchecked(srid),
396 G::Line(x) => x.set_srid_unchecked(srid),
397 G::Polygon(x) => x.set_srid_unchecked(srid),
398 G::Points(x) => x.set_srid_unchecked(srid),
399 G::Lines(x) => x.set_srid_unchecked(srid),
400 G::Polygons(x) => x.set_srid_unchecked(srid),
401 G::Vec(x) => x.set_srid_unchecked(srid),
402 G::BBox(x) => x.set_srid_unchecked(srid),
403 _ => unreachable!("N/A for this geometry type"),
404 }
405 }
406}
407
408impl fmt::Display for G {
409 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410 match self {
411 G::Null => write!(f, ""),
412 G::Point(x) => write!(f, "{x}"),
413 G::Line(x) => write!(f, "{x}"),
414 G::Polygon(x) => write!(f, "{x}"),
415 G::Points(x) => write!(f, "{x}"),
416 G::Lines(x) => write!(f, "{x}"),
417 G::Polygons(x) => write!(f, "{x}"),
418 G::Vec(x) => write!(f, "{x}"),
419 G::BBox(x) => write!(f, "{x}"),
420 }
421 }
422}
423
424impl TryFrom<&str> for G {
426 type Error = MyError;
427
428 fn try_from(value: &str) -> Result<Self, Self::Error> {
429 let mut g = wkt(value).map_err(MyError::Text)?;
430 g.set_srid_unchecked(config().default_srid());
433
434 Ok(g)
435 }
436}
437
438impl TryFrom<&[u8]> for G {
440 type Error = MyError;
441
442 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
443 let wkb = GeoPackageBinary::try_from(value)?;
444 Ok(wkb.geom())
445 }
446}
447
448impl TryFrom<Geometry> for G {
450 type Error = MyError;
451
452 fn try_from(value: Geometry) -> Result<Self, Self::Error> {
453 match value.geometry_type()? {
454 GeometryTypes::Point => {
455 let g = Point::try_from(value)?;
456 Ok(G::Point(g))
457 }
458 GeometryTypes::LineString | GeometryTypes::LinearRing => {
459 let g = Line::try_from(value)?;
460 Ok(G::Line(g))
461 }
462 GeometryTypes::Polygon => {
463 let g = Polygon::try_from(value)?;
464 Ok(G::Polygon(g))
465 }
466 GeometryTypes::MultiPoint => {
467 let g = Points::try_from(value)?;
468 Ok(G::Points(g))
469 }
470 GeometryTypes::MultiLineString => {
471 let g = Lines::try_from(value)?;
472 Ok(G::Lines(g))
473 }
474 GeometryTypes::MultiPolygon => {
475 let g = Polygons::try_from(value)?;
476 Ok(G::Polygons(g))
477 }
478 GeometryTypes::GeometryCollection => {
479 let g = Geometries::try_from(value)?;
480 Ok(G::Vec(g))
481 }
482 }
489 }
490}
491
492impl TryFrom<ConstGeometry<'_>> for G {
494 type Error = MyError;
495
496 fn try_from(value: ConstGeometry) -> Result<Self, Self::Error> {
497 match value.geometry_type()? {
498 GeometryTypes::Point => {
499 let g = Point::try_from(value)?;
500 Ok(G::Point(g))
501 }
502 GeometryTypes::LineString | GeometryTypes::LinearRing => {
503 let g = Line::try_from(value)?;
504 Ok(G::Line(g))
505 }
506 GeometryTypes::Polygon => {
507 let g = Polygon::try_from(value)?;
508 Ok(G::Polygon(g))
509 }
510 GeometryTypes::MultiPoint => {
511 let g = Points::try_from(value)?;
512 Ok(G::Points(g))
513 }
514 GeometryTypes::MultiLineString => {
515 let g = Lines::try_from(value)?;
516 Ok(G::Lines(g))
517 }
518 GeometryTypes::MultiPolygon => {
519 let g = Polygons::try_from(value)?;
520 Ok(G::Polygons(g))
521 }
522 GeometryTypes::GeometryCollection => {
523 let g = Geometries::try_from(value)?;
524 Ok(G::Vec(g))
525 }
526 }
533 }
534}
535
536#[cfg(test)]
537mod tests {
538 use super::*;
539 use crate::{expr::E, text::cql2};
540 use geos::Geom;
541 use std::error::Error;
542
543 #[test]
544 #[tracing_test::traced_test]
545 fn test_to_wkt() {
546 const G: &str = r#"Polygon Z (
547 (
548 -49.88024 0.5 -75993.341684,
549 -1.5 -0.99999 -100000.0,
550 0.0 0.5 -0.333333,
551 -49.88024 0.5 -75993.341684
552 ), (
553 -65.887123 2.00001 -100000.0,
554 0.333333 -53.017711 -79471.332949,
555 180.0 0.0 1852.616704,
556 -65.887123 2.00001 -100000.0
557 ))"#;
558 const WKT: &str = "POLYGON Z ((-49.880240 0.500000 -75993.341684, -1.500000 -0.999990 -100000.000000, 0.000000 0.500000 -0.333333, -49.880240 0.500000 -75993.341684), (-65.887123 2.000010 -100000.000000, 0.333333 -53.017711 -79471.332949, 180.000000 0.000000 1852.616704, -65.887123 2.000010 -100000.000000))";
559
560 let exp = cql2::geom_expression(G);
561 let spa = exp.expect("Failed parsing Polygon WKT");
563 let g = match spa {
564 E::Spatial(G::Polygon(x)) => x,
565 _ => panic!("Not a Polygon..."),
566 };
567 assert_eq!(g.is_2d(), false);
569
570 let wkt = g.to_wkt_fmt(6);
571 assert_eq!(WKT, wkt);
572 }
573
574 #[test]
575 #[tracing_test::traced_test]
576 fn test_to_geos() -> Result<(), Box<dyn Error>> {
577 let g = G::try_from("POINT(17.03 45.87)")?;
578 assert!(matches!(g, G::Point(_)));
580 let gg = g.to_geos()?;
582 assert_eq!(gg.get_type()?, "Point");
583 assert_eq!(gg.get_x()?, 17.03);
584 assert_eq!(gg.get_y()?, 45.87);
585 assert!(!gg.has_z()?);
586
587 let g = G::try_from("LINESTRING(-49.85 0.5, -1.5 -0.999, 0.0 0.5, -49.88 0.5)")?;
588 assert!(matches!(g, G::Line(_)));
590 let gg = g.to_geos()?;
592 assert_eq!(gg.get_type()?, "LineString");
593 assert_eq!(gg.get_num_points()?, 4);
594 assert_eq!(gg.get_start_point()?.get_x()?, -49.85);
595 assert_eq!(gg.get_end_point()?.get_y()?, 0.5);
596
597 let g = G::try_from(
598 r#"PolyGon ((
599 -0.333333 89.0,
600 -102.723546 -0.5,
601 -179.0 -89.0,
602 -1.9 89.0,
603 -0.0 89.0,
604 2.00001 -1.9,
605 -0.333333 89.0))"#,
606 )?;
607 assert!(matches!(g, G::Polygon(_)));
609 let gg = g.to_geos()?;
611 assert_eq!(gg.get_type()?, "Polygon");
612 assert_eq!(gg.get_num_interior_rings()?, 0);
613 assert_eq!(gg.get_exterior_ring()?.get_num_coordinates()?, 7);
614
615 let g = G::try_from("MULTIPOINT(17.03 45.87, -0.33 89.02)")?;
618 assert!(matches!(g, G::Points(_)));
620 let gg = g.to_geos()?;
622 assert_eq!(gg.get_type()?, "MultiPoint");
623 assert_eq!(gg.get_num_geometries()?, 2);
624 assert_eq!(gg.get_geometry_n(0)?.get_x()?, 17.03);
625 assert_eq!(gg.get_geometry_n(1)?.get_y()?, 89.02);
626
627 let g = G::try_from(
628 r#"MULTILINESTRING(
629 (-49.85 0.5, -1.5 -0.999, 0.0 0.5),
630 (34.3 3.2, 0.1 0.2))"#,
631 )?;
632 assert!(matches!(g, G::Lines(_)));
634 let gg = g.to_geos()?;
636 assert_eq!(gg.get_type()?, "MultiLineString");
637 assert_eq!(gg.get_num_geometries()?, 2);
638 assert_eq!(gg.get_geometry_n(0)?.get_start_point()?.get_x()?, -49.85);
639 assert_eq!(gg.get_geometry_n(1)?.get_end_point()?.get_y()?, 0.2);
640
641 let g = G::try_from(
642 r#"MULTIPOLYGON (
643 ((
644 180.0 -16.0671326636424,
645 180.0 -16.5552165666392,
646 179.364142661964 -16.8013540769469,
647 178.725059362997 -17.012041674368,
648 178.596838595117 -16.63915,
649 179.096609362997 -16.4339842775474,
650 179.413509362997 -16.3790542775474,
651 180.0 -16.0671326636424
652 )),((
653 178.12557 -17.50481,
654 178.3736 -17.33992,
655 178.71806 -17.62846,
656 178.55271 -18.15059,
657 177.93266 -18.28799,
658 177.38146 -18.16432,
659 177.28504 -17.72465,
660 177.67087 -17.38114,
661 178.12557 -17.50481
662 )),((
663 -179.793320109049 -16.0208822567412,
664 -179.917369384765 -16.5017831356494,
665 -180 -16.5552165666392,
666 -180 -16.0671326636424,
667 -179.793320109049 -16.0208822567412
668 ))
669 )"#,
670 )?;
671 assert!(matches!(g, G::Polygons(_)));
673 let gg = g.to_geos()?;
675 assert_eq!(gg.get_type()?, "MultiPolygon");
676 assert_eq!(gg.get_num_geometries()?, 3);
677 let p1 = gg.get_geometry_n(0)?;
678 assert_eq!(p1.get_type()?, "Polygon");
679 assert_eq!(p1.get_exterior_ring()?.get_num_coordinates()?, 8);
680 assert_eq!(p1.get_num_interior_rings()?, 0);
681
682 let g = G::try_from(
683 r#"GEOMETRYCOLLECTION(
684 POINT(17.03 45.87),
685 LINESTRING(-49.85 0.5, -1.5 -0.999, 0.0 0.5, -49.88 0.5)
686 )"#,
687 )?;
688 assert!(matches!(g, G::Vec(_)));
690 let gg = g.to_geos()?;
692 assert_eq!(gg.get_type()?, "GeometryCollection");
693 assert_eq!(gg.get_num_geometries()?, 2);
694 Ok(())
695 }
696
697 #[test]
698 #[tracing_test::traced_test]
699 fn test_geos() -> Result<(), Box<dyn Error>> {
700 const G: &str = r#"MultiLineString(
701 (-49.85 0.5, -1.5 -0.999, 0.0 0.5, -49.88 0.5 ),
702 (-65.87 2.01, 0.33 -53.07, 180.0 0)
703 )"#;
704
705 let exp = cql2::geom_expression(G);
706 let spa = exp.expect("Failed parsing Polygon WKT");
708 let g = match spa {
709 E::Spatial(G::Lines(x)) => x,
710 _ => panic!("Not a Lines..."),
711 };
712 assert_eq!(g.is_2d(), true);
713 assert_eq!(g.num_lines(), 2);
714
715 let geos = g.to_geos().expect("Failed converting to GEOS geometry");
716 assert_eq!(geos.get_num_geometries()?, g.num_lines());
717 let l1 = geos.get_geometry_n(0)?;
718 assert_eq!(l1.get_num_coordinates()?, 4);
719 let l2 = geos.get_geometry_n(1)?;
720 assert_eq!(l2.get_num_coordinates()?, 3);
721
722 Ok(())
723 }
724
725 #[test]
726 #[tracing_test::traced_test]
727 fn test_new_from_wkt() -> Result<(), Box<dyn Error>> {
728 const PT: &str = "POINT (-46.03556 -7.5325)";
729 const LS: &str = "LINESTRING (-180 -45, 0 -45)";
730 const P: &str = "POLYGON ((-180 -90, -90 -90, -90 90, -180 90, -180 -90), (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50))";
731 const MPT: &str = "MULTIPOINT ((7.02 49.92), (90 180))";
732 const MLS: &str = "MULTILINESTRING ((-180 -45, 0 -45), (0 45, 180 45))";
734 const MP: &str = r#"MULTIPOLYGON(
735 ((-180 -90, -90 -90, -90 90, -180 90, -180 -90),
736 (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50)),
737 ((0 0, 10 0, 10 10, 0 10, 0 0))
738 )"#;
739 const MG: &str = r#"GEOMETRYCOLLECTION(
740 POINT(7.02 49.92),
741 POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))
742 )"#;
743
744 let pt = Geometry::new_from_wkt(PT);
745 assert!(pt.is_ok());
746 assert_eq!(pt?.to_wkt()?, PT);
747
748 let ls = Geometry::new_from_wkt(LS);
749 assert!(ls.is_ok());
750 assert_eq!(ls?.to_wkt()?, LS);
752
753 let poly = Geometry::new_from_wkt(P);
754 assert!(poly.is_ok());
755 assert_eq!(poly?.to_wkt()?, P);
757
758 let points = Geometry::new_from_wkt(MPT);
759 assert!(points.is_ok());
760 assert_eq!(points?.to_wkt()?, MPT);
762
763 let lines = Geometry::new_from_wkt(MLS);
764 assert!(lines.is_ok());
765 assert_eq!(lines?.to_wkt()?, MLS);
767
768 let polys = Geometry::new_from_wkt(MP);
769 assert!(polys.is_ok());
770 assert_eq!(polys?.get_type()?, "MultiPolygon");
772
773 let geometries = Geometry::new_from_wkt(MG);
774 assert!(geometries.is_ok());
775 assert_eq!(geometries?.get_type()?, "GeometryCollection");
776
777 Ok(())
778 }
779
780 #[test]
781 fn test_point_in_polygon() -> Result<(), Box<dyn Error>> {
782 const WKT1: &str = "POINT(-46.03556 -7.5325)";
783 const WKT2: &str =
784 "POLYGON((-65.887123 2.00001, 0.333333 -53.017711, 180.0 0.0, -65.887123 2.00001))";
785
786 let pt = Geometry::new_from_wkt(WKT1).expect("Failed parsing point");
787 let polygon = Geometry::new_from_wkt(WKT2).expect("Failed parsing polygon");
788
789 pt.within(&polygon)?;
790 polygon.contains(&pt)?;
792
793 Ok(())
794 }
795
796 #[test]
797 fn test_try_from_wkt() -> Result<(), Box<dyn Error>> {
798 #[rustfmt::skip]
801 const TV: [(&str, &str, usize); 8] = [
802 (
803 "POINT(-46.035560 -7.532500)",
804 "POINT (-46.03556 -7.53250)",
805 5
806 ), (
807 "LINESTRING (-180 -45, 0 -45)",
808 "LINESTRING (-180.0 -45.0, 0.0 -45.0)",
809 1
810 ), (
811 r#"POLYGON (
812 (-180 -90, -90 -90, -90 90, -180 90, -180 -90),
813 (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50)
814 )"#,
815 "POLYGON ((-180 -90, -90 -90, -90 90, -180 90, -180 -90), (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50))",
816 0
817 ), (
818 "MULTIPOINT ((7.02 49.92), (90 180))",
819 "MULTIPOINT (7.02 49.92, 90.00 180.00)",
820 2
821 ), (
822 "MULTILINESTRING ((-180 -45, 0 -45), (0 45, 180 45))",
823 "MULTILINESTRING ((-180.0 -45.0, 0.0 -45.0), (0.0 45.0, 180.0 45.0))",
824 1
825 ), (
826 r#"MULTIPOLYGON((
827 (-180 -90, -90 -90, -90 90, -180 90, -180 -90),
828 (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50)
829 ), (
830 (0 0, 10 0, 10 10, 0 10, 0 0)
831 ))"#,
832 "MULTIPOLYGON (((-180 -90, -90 -90, -90 90, -180 90, -180 -90), (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50)), ((0 0, 10 0, 10 10, 0 10, 0 0)))",
833 0
834 ), (
835 "GEOMETRYCOLLECTION(POINT(7.02 49.92),POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))",
836 "GEOMETRYCOLLECTION (POINT (7.0 49.9), POLYGON ((0.0 0.0, 10.0 0.0, 10.0 10.0, 0.0 10.0, 0.0 0.0)))",
837 1
838 ), (
839 "BBOX(51.43,2.54,55.77,6.40)",
840 "BBOX (51.43, 2.54, 55.77, 6.40)",
841 2
842 ),
843 ];
844
845 for (ndx, (wkt, expected, precision)) in TV.iter().enumerate() {
846 if let Ok(g) = G::try_from(*wkt) {
848 let actual = g.to_wkt_fmt(*precision);
849 assert_eq!(actual, *expected);
850 } else {
851 panic!("Failed parsing WKT at index #{ndx}")
852 }
853 }
854
855 Ok(())
856 }
857
858 #[test]
859 #[tracing_test::traced_test]
860 fn test_geos_envelope() -> Result<(), Box<dyn Error>> {
861 let mut geom = geos::Geometry::new_from_wkt("LINESTRING(0 0, 1 3)")?;
862 geom.set_srid(3587);
863
864 let envelope = geom.envelope()?;
865 let srid = envelope.get_srid()?;
866 tracing::debug!("envelope SRS id = {srid}");
867 assert_eq!(envelope.to_wkt()?, "POLYGON ((0 0, 1 0, 1 3, 0 3, 0 0))");
868
869 Ok(())
870 }
871
872 #[test]
873 #[ignore = "GEOS possible bug"]
874 fn test_geos_wkt() -> Result<(), Box<dyn Error>> {
875 let expected = "POINT (1.0 3.0)";
876
877 let geom = geos::Geometry::new_from_wkt("POINT(1 3)")?;
878 let actual = geom.to_wkt_precision(0)?;
879
880 assert_eq!(actual, expected);
881 Ok(())
882 }
883
884 #[test]
885 #[ignore = "GEOS possible bug"]
886 fn test_geos_wkt_writer() -> Result<(), Box<dyn Error>> {
887 let expected = "POINT (1.00 3.00)";
888
889 let geom = geos::Geometry::new_from_wkt("POINT(1 3)")?;
890 let mut writer = geos::WKTWriter::new()?;
891 writer.set_rounding_precision(2);
892 let actual = writer.write(&geom)?;
893
894 assert_eq!(actual, expected);
895 Ok(())
896 }
897}