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