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, Default, PartialEq, PartialOrd)]
51pub enum G {
52 #[default]
54 Null,
55
56 Point(Point),
58 Line(Line),
60 Polygon(Polygon),
62 Points(Points),
64 Lines(Lines),
66 Polygons(Polygons),
68 Vec(Geometries),
70 BBox(BBox),
72}
73
74pub trait GTrait {
76 fn is_2d(&self) -> bool;
78
79 fn to_wkt(&self) -> String {
86 self.to_wkt_fmt(config().default_precision())
87 }
88
89 fn to_wkt_fmt(&self, precision: usize) -> String;
108
109 fn check_coordinates(&self, crs: &CRS) -> Result<(), MyError>;
112
113 fn type_(&self) -> &str;
115
116 fn srid(&self) -> SRID;
118}
119
120impl GTrait for G {
121 fn is_2d(&self) -> bool {
122 match self {
123 G::Point(x) => x.is_2d(),
124 G::Line(x) => x.is_2d(),
125 G::Polygon(x) => x.is_2d(),
126 G::Points(x) => x.is_2d(),
127 G::Lines(x) => x.is_2d(),
128 G::Polygons(x) => x.is_2d(),
129 G::Vec(x) => x.is_2d(),
130 G::BBox(x) => x.is_2d(),
131 _ => unreachable!("N/A for this geometry type"),
132 }
133 }
134
135 fn to_wkt_fmt(&self, precision: usize) -> String {
136 match self {
137 G::Point(x) => x.to_wkt_fmt(precision),
138 G::Line(x) => x.to_wkt_fmt(precision),
139 G::Polygon(x) => x.to_wkt_fmt(precision),
140 G::Points(x) => x.to_wkt_fmt(precision),
141 G::Lines(x) => x.to_wkt_fmt(precision),
142 G::Polygons(x) => x.to_wkt_fmt(precision),
143 G::Vec(x) => x.to_wkt_fmt(precision),
144 G::BBox(x) => x.to_wkt_fmt(precision),
145 _ => unreachable!("N/A for this geometry type"),
146 }
147 }
148
149 fn check_coordinates(&self, crs: &CRS) -> Result<(), MyError> {
150 match self {
151 G::Point(x) => x.check_coordinates(crs),
152 G::Line(x) => x.check_coordinates(crs),
153 G::Polygon(x) => x.check_coordinates(crs),
154 G::Points(x) => x.check_coordinates(crs),
155 G::Lines(x) => x.check_coordinates(crs),
156 G::Polygons(x) => x.check_coordinates(crs),
157 G::Vec(x) => x.check_coordinates(crs),
158 G::BBox(x) => x.check_coordinates(crs),
159 _ => unreachable!("N/A for this geometry type"),
160 }
161 }
162
163 fn type_(&self) -> &str {
164 match self {
165 G::Point(x) => x.type_(),
166 G::Line(x) => x.type_(),
167 G::Polygon(x) => x.type_(),
168 G::Points(x) => x.type_(),
169 G::Lines(x) => x.type_(),
170 G::Polygons(x) => x.type_(),
171 G::Vec(x) => x.type_(),
172 G::BBox(x) => x.type_(),
173 _ => unreachable!("N/A for this geometry type"),
174 }
175 }
176
177 fn srid(&self) -> SRID {
178 match self {
179 G::Point(x) => x.srid(),
180 G::Line(x) => x.srid(),
181 G::Polygon(x) => x.srid(),
182 G::Points(x) => x.srid(),
183 G::Lines(x) => x.srid(),
184 G::Polygons(x) => x.srid(),
185 G::Vec(x) => x.srid(),
186 G::BBox(x) => x.srid(),
187 _ => unreachable!("N/A for this geometry type"),
188 }
189 }
190}
191
192impl G {
193 pub fn as_point(&self) -> Option<&Point> {
195 match self {
196 G::Point(x) => Some(x),
197 _ => None,
198 }
199 }
200
201 pub fn as_line(&self) -> Option<&Line> {
203 match self {
204 G::Line(x) => Some(x),
205 _ => None,
206 }
207 }
208
209 pub fn as_polygon(&self) -> Option<&Polygon> {
211 match self {
212 G::Polygon(x) => Some(x),
213 _ => None,
214 }
215 }
216
217 pub fn as_points(&self) -> Option<&Points> {
219 match self {
220 G::Points(x) => Some(x),
221 _ => None,
222 }
223 }
224
225 pub fn as_lines(&self) -> Option<&Lines> {
227 match self {
228 G::Lines(x) => Some(x),
229 _ => None,
230 }
231 }
232
233 pub fn as_polygons(&self) -> Option<&Polygons> {
235 match self {
236 G::Polygons(x) => Some(x),
237 _ => None,
238 }
239 }
240
241 pub(crate) fn to_geos(&self) -> Result<Geometry, MyError> {
244 match self {
245 G::Point(x) => x.to_geos(),
246 G::Line(x) => x.to_geos(),
247 G::Polygon(x) => x.to_geos(),
248 G::Points(x) => x.to_geos(),
249 G::Lines(x) => x.to_geos(),
250 G::Polygons(x) => x.to_geos(),
251 G::Vec(x) => x.to_geos(),
252 G::BBox(x) => x.to_geos(),
253 _ => unreachable!("N/A for this geometry type"),
254 }
255 }
256
257 pub(crate) fn intersects(&self, other: &G) -> Result<bool, MyError> {
258 let lhs = self.to_geos()?;
259 let rhs = other.to_geos()?;
260 let result = lhs.intersects(&rhs)?;
261 Ok(result)
262 }
263
264 pub(crate) fn equals(&self, other: &G) -> Result<bool, MyError> {
265 let lhs = self.to_geos()?;
266 let rhs = other.to_geos()?;
267 let result = lhs.equals(&rhs)?;
268 Ok(result)
269 }
270
271 pub(crate) fn disjoint(&self, other: &G) -> Result<bool, MyError> {
272 let lhs = self.to_geos()?;
273 let rhs = other.to_geos()?;
274 let result = lhs.disjoint(&rhs)?;
275 Ok(result)
276 }
277
278 pub(crate) fn touches(&self, other: &G) -> Result<bool, MyError> {
279 let lhs = self.to_geos()?;
280 let rhs = other.to_geos()?;
281 let result = lhs.touches(&rhs)?;
282 Ok(result)
283 }
284
285 pub(crate) fn within(&self, other: &G) -> Result<bool, MyError> {
286 let lhs = self.to_geos()?;
287 let rhs = other.to_geos()?;
288 let result = lhs.within(&rhs)?;
289 Ok(result)
290 }
291
292 pub(crate) fn overlaps(&self, other: &G) -> Result<bool, MyError> {
293 let lhs = self.to_geos()?;
294 let rhs = other.to_geos()?;
295 let result = lhs.overlaps(&rhs)?;
296 Ok(result)
297 }
298
299 pub(crate) fn crosses(&self, other: &G) -> Result<bool, MyError> {
300 let lhs = self.to_geos()?;
301 let rhs = other.to_geos()?;
302 let result = lhs.crosses(&rhs)?;
303 Ok(result)
304 }
305
306 pub(crate) fn contains(&self, other: &G) -> Result<bool, MyError> {
307 let lhs = self.to_geos()?;
308 let rhs = other.to_geos()?;
309 let result = lhs.contains(&rhs)?;
310 Ok(result)
311 }
312
313 pub(crate) fn boundary(&self) -> Result<Self, MyError> {
316 let g1 = self.to_geos()?;
317 let g2 = g1.boundary()?;
318 let it = G::try_from(g2)?;
319 Ok(it)
320 }
321
322 pub(crate) fn buffer(&self, width: f64, quadsegs: i32) -> Result<Self, MyError> {
323 let g1 = self.to_geos()?;
324 let g2 = g1.buffer(width, quadsegs)?;
325 let it = G::try_from(g2)?;
326 Ok(it)
327 }
328
329 pub(crate) fn envelope(&self) -> Result<Self, MyError> {
330 let g1 = self.to_geos()?;
331 let g2 = g1.envelope()?;
332 let it = G::try_from(g2)?;
333 Ok(it)
334 }
335
336 pub(crate) fn centroid(&self) -> Result<Self, MyError> {
337 let g1 = self.to_geos()?;
338 let g2 = g1.get_centroid()?;
339 let it = G::try_from(g2)?;
340 Ok(it)
341 }
342
343 pub(crate) fn convex_hull(&self) -> Result<Self, MyError> {
344 let g1 = self.to_geos()?;
345 let g2 = g1.convex_hull()?;
346 let it = G::try_from(g2)?;
347 Ok(it)
348 }
349
350 pub(crate) fn get_x(&self) -> Result<f64, MyError> {
351 if let Some(pt) = self.as_point() {
352 Ok(pt.x())
353 } else {
354 Err(MyError::Runtime("This is NOT a Point".into()))
355 }
356 }
357
358 pub(crate) fn get_y(&self) -> Result<f64, MyError> {
359 if let Some(pt) = self.as_point() {
360 Ok(pt.y())
361 } else {
362 Err(MyError::Runtime("This is NOT a Point".into()))
363 }
364 }
365
366 pub(crate) fn get_z(&self) -> Result<f64, MyError> {
367 if let Some(pt) = self.as_point() {
368 if let Some(z) = pt.z() {
369 Ok(z)
370 } else {
371 Err(MyError::Runtime("This is NOT a 3D Point".into()))
372 }
373 } else {
374 Err(MyError::Runtime("This is NOT a Point".into()))
375 }
376 }
377
378 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().as_usize()?;
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 x => {
483 let msg = format!("Unknown ({x:?}) geometry type");
484 error!("Failed: {msg}");
485 Err(MyError::Runtime(msg.into()))
486 }
487 }
488 }
489}
490
491impl TryFrom<ConstGeometry<'_>> for G {
493 type Error = MyError;
494
495 fn try_from(value: ConstGeometry) -> Result<Self, Self::Error> {
496 match value.geometry_type() {
497 GeometryTypes::Point => {
498 let g = Point::try_from(value)?;
499 Ok(G::Point(g))
500 }
501 GeometryTypes::LineString | GeometryTypes::LinearRing => {
502 let g = Line::try_from(value)?;
503 Ok(G::Line(g))
504 }
505 GeometryTypes::Polygon => {
506 let g = Polygon::try_from(value)?;
507 Ok(G::Polygon(g))
508 }
509 GeometryTypes::MultiPoint => {
510 let g = Points::try_from(value)?;
511 Ok(G::Points(g))
512 }
513 GeometryTypes::MultiLineString => {
514 let g = Lines::try_from(value)?;
515 Ok(G::Lines(g))
516 }
517 GeometryTypes::MultiPolygon => {
518 let g = Polygons::try_from(value)?;
519 Ok(G::Polygons(g))
520 }
521 GeometryTypes::GeometryCollection => {
522 let g = Geometries::try_from(value)?;
523 Ok(G::Vec(g))
524 }
525 x => {
526 let msg = format!("Unknown ({x:?}) geometry type");
527 error!("Failed: {msg}");
528 Err(MyError::Runtime(msg.into()))
529 }
530 }
531 }
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537 use crate::{expr::E, text::cql2};
538 use geos::Geom;
539 use std::error::Error;
540
541 #[test]
542 #[tracing_test::traced_test]
543 fn test_to_wkt() {
544 const G: &str = r#"Polygon Z (
545 (
546 -49.88024 0.5 -75993.341684,
547 -1.5 -0.99999 -100000.0,
548 0.0 0.5 -0.333333,
549 -49.88024 0.5 -75993.341684
550 ), (
551 -65.887123 2.00001 -100000.0,
552 0.333333 -53.017711 -79471.332949,
553 180.0 0.0 1852.616704,
554 -65.887123 2.00001 -100000.0
555 ))"#;
556 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))";
557
558 let exp = cql2::geom_expression(G);
559 let spa = exp.expect("Failed parsing Polygon WKT");
561 let g = match spa {
562 E::Spatial(G::Polygon(x)) => x,
563 _ => panic!("Not a Polygon..."),
564 };
565 assert_eq!(g.is_2d(), false);
567
568 let wkt = g.to_wkt_fmt(6);
569 assert_eq!(WKT, wkt);
570 }
571
572 #[test]
573 #[tracing_test::traced_test]
574 fn test_to_geos() -> Result<(), Box<dyn Error>> {
575 let g = G::try_from("POINT(17.03 45.87)")?;
576 assert!(matches!(g, G::Point(_)));
578 let gg = g.to_geos()?;
580 assert_eq!(gg.get_type()?, "Point");
581 assert_eq!(gg.get_x()?, 17.03);
582 assert_eq!(gg.get_y()?, 45.87);
583 assert!(!gg.has_z()?);
584
585 let g = G::try_from("LINESTRING(-49.85 0.5, -1.5 -0.999, 0.0 0.5, -49.88 0.5)")?;
586 assert!(matches!(g, G::Line(_)));
588 let gg = g.to_geos()?;
590 assert_eq!(gg.get_type()?, "LineString");
591 assert_eq!(gg.get_num_points()?, 4);
592 assert_eq!(gg.get_start_point()?.get_x()?, -49.85);
593 assert_eq!(gg.get_end_point()?.get_y()?, 0.5);
594
595 let g = G::try_from(
596 r#"PolyGon ((
597 -0.333333 89.0,
598 -102.723546 -0.5,
599 -179.0 -89.0,
600 -1.9 89.0,
601 -0.0 89.0,
602 2.00001 -1.9,
603 -0.333333 89.0))"#,
604 )?;
605 assert!(matches!(g, G::Polygon(_)));
607 let gg = g.to_geos()?;
609 assert_eq!(gg.get_type()?, "Polygon");
610 assert_eq!(gg.get_num_interior_rings()?, 0);
611 assert_eq!(gg.get_exterior_ring()?.get_num_coordinates()?, 7);
612
613 let g = G::try_from("MULTIPOINT(17.03 45.87, -0.33 89.02)")?;
616 assert!(matches!(g, G::Points(_)));
618 let gg = g.to_geos()?;
620 assert_eq!(gg.get_type()?, "MultiPoint");
621 assert_eq!(gg.get_num_geometries()?, 2);
622 assert_eq!(gg.get_geometry_n(0)?.get_x()?, 17.03);
623 assert_eq!(gg.get_geometry_n(1)?.get_y()?, 89.02);
624
625 let g = G::try_from(
626 r#"MULTILINESTRING(
627 (-49.85 0.5, -1.5 -0.999, 0.0 0.5),
628 (34.3 3.2, 0.1 0.2))"#,
629 )?;
630 assert!(matches!(g, G::Lines(_)));
632 let gg = g.to_geos()?;
634 assert_eq!(gg.get_type()?, "MultiLineString");
635 assert_eq!(gg.get_num_geometries()?, 2);
636 assert_eq!(gg.get_geometry_n(0)?.get_start_point()?.get_x()?, -49.85);
637 assert_eq!(gg.get_geometry_n(1)?.get_end_point()?.get_y()?, 0.2);
638
639 let g = G::try_from(
640 r#"MULTIPOLYGON (
641 ((
642 180.0 -16.0671326636424,
643 180.0 -16.5552165666392,
644 179.364142661964 -16.8013540769469,
645 178.725059362997 -17.012041674368,
646 178.596838595117 -16.63915,
647 179.096609362997 -16.4339842775474,
648 179.413509362997 -16.3790542775474,
649 180.0 -16.0671326636424
650 )),((
651 178.12557 -17.50481,
652 178.3736 -17.33992,
653 178.71806 -17.62846,
654 178.55271 -18.15059,
655 177.93266 -18.28799,
656 177.38146 -18.16432,
657 177.28504 -17.72465,
658 177.67087 -17.38114,
659 178.12557 -17.50481
660 )),((
661 -179.793320109049 -16.0208822567412,
662 -179.917369384765 -16.5017831356494,
663 -180 -16.5552165666392,
664 -180 -16.0671326636424,
665 -179.793320109049 -16.0208822567412
666 ))
667 )"#,
668 )?;
669 assert!(matches!(g, G::Polygons(_)));
671 let gg = g.to_geos()?;
673 assert_eq!(gg.get_type()?, "MultiPolygon");
674 assert_eq!(gg.get_num_geometries()?, 3);
675 let p1 = gg.get_geometry_n(0)?;
676 assert_eq!(p1.get_type()?, "Polygon");
677 assert_eq!(p1.get_exterior_ring()?.get_num_coordinates()?, 8);
678 assert_eq!(p1.get_num_interior_rings()?, 0);
679
680 let g = G::try_from(
681 r#"GEOMETRYCOLLECTION(
682 POINT(17.03 45.87),
683 LINESTRING(-49.85 0.5, -1.5 -0.999, 0.0 0.5, -49.88 0.5)
684 )"#,
685 )?;
686 assert!(matches!(g, G::Vec(_)));
688 let gg = g.to_geos()?;
690 assert_eq!(gg.get_type()?, "GeometryCollection");
691 assert_eq!(gg.get_num_geometries()?, 2);
692 Ok(())
693 }
694
695 #[test]
696 #[tracing_test::traced_test]
697 fn test_geos() -> Result<(), Box<dyn Error>> {
698 const G: &str = r#"MultiLineString(
699 (-49.85 0.5, -1.5 -0.999, 0.0 0.5, -49.88 0.5 ),
700 (-65.87 2.01, 0.33 -53.07, 180.0 0)
701 )"#;
702
703 let exp = cql2::geom_expression(G);
704 let spa = exp.expect("Failed parsing Polygon WKT");
706 let g = match spa {
707 E::Spatial(G::Lines(x)) => x,
708 _ => panic!("Not a Lines..."),
709 };
710 assert_eq!(g.is_2d(), true);
711 assert_eq!(g.num_lines(), 2);
712
713 let geos = g.to_geos().expect("Failed converting to GEOS geometry");
714 assert_eq!(geos.get_num_geometries()?, g.num_lines());
715 let l1 = geos.get_geometry_n(0)?;
716 assert_eq!(l1.get_num_coordinates()?, 4);
717 let l2 = geos.get_geometry_n(1)?;
718 assert_eq!(l2.get_num_coordinates()?, 3);
719
720 Ok(())
721 }
722
723 #[test]
724 #[tracing_test::traced_test]
725 fn test_new_from_wkt() -> Result<(), Box<dyn Error>> {
726 const PT: &str = "POINT (-46.03556 -7.5325)";
727 const LS: &str = "LINESTRING (-180 -45, 0 -45)";
728 const P: &str = "POLYGON ((-180 -90, -90 -90, -90 90, -180 90, -180 -90), (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50))";
729 const MPT: &str = "MULTIPOINT ((7.02 49.92), (90 180))";
730 const MLS: &str = "MULTILINESTRING ((-180 -45, 0 -45), (0 45, 180 45))";
732 const MP: &str = r#"MULTIPOLYGON(
733 ((-180 -90, -90 -90, -90 90, -180 90, -180 -90),
734 (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50)),
735 ((0 0, 10 0, 10 10, 0 10, 0 0))
736 )"#;
737 const MG: &str = r#"GEOMETRYCOLLECTION(
738 POINT(7.02 49.92),
739 POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))
740 )"#;
741
742 let pt = Geometry::new_from_wkt(PT);
743 assert!(pt.is_ok());
744 assert_eq!(pt?.to_wkt()?, PT);
745
746 let ls = Geometry::new_from_wkt(LS);
747 assert!(ls.is_ok());
748 assert_eq!(ls?.to_wkt()?, LS);
750
751 let poly = Geometry::new_from_wkt(P);
752 assert!(poly.is_ok());
753 assert_eq!(poly?.to_wkt()?, P);
755
756 let points = Geometry::new_from_wkt(MPT);
757 assert!(points.is_ok());
758 assert_eq!(points?.to_wkt()?, MPT);
760
761 let lines = Geometry::new_from_wkt(MLS);
762 assert!(lines.is_ok());
763 assert_eq!(lines?.to_wkt()?, MLS);
765
766 let polys = Geometry::new_from_wkt(MP);
767 assert!(polys.is_ok());
768 assert_eq!(polys?.get_type()?, "MultiPolygon");
770
771 let geometries = Geometry::new_from_wkt(MG);
772 assert!(geometries.is_ok());
773 assert_eq!(geometries?.get_type()?, "GeometryCollection");
774
775 Ok(())
776 }
777
778 #[test]
779 fn test_point_in_polygon() -> Result<(), Box<dyn Error>> {
780 const WKT1: &str = "POINT(-46.03556 -7.5325)";
781 const WKT2: &str =
782 "POLYGON((-65.887123 2.00001, 0.333333 -53.017711, 180.0 0.0, -65.887123 2.00001))";
783
784 let pt = Geometry::new_from_wkt(WKT1).expect("Failed parsing point");
785 let polygon = Geometry::new_from_wkt(WKT2).expect("Failed parsing polygon");
786
787 pt.within(&polygon)?;
788 polygon.contains(&pt)?;
790
791 Ok(())
792 }
793
794 #[test]
795 fn test_try_from_wkt() -> Result<(), Box<dyn Error>> {
796 #[rustfmt::skip]
799 const TV: [(&str, &str, usize); 8] = [
800 (
801 "POINT(-46.035560 -7.532500)",
802 "POINT (-46.03556 -7.53250)",
803 5
804 ), (
805 "LINESTRING (-180 -45, 0 -45)",
806 "LINESTRING (-180.0 -45.0, 0.0 -45.0)",
807 1
808 ), (
809 r#"POLYGON (
810 (-180 -90, -90 -90, -90 90, -180 90, -180 -90),
811 (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50)
812 )"#,
813 "POLYGON ((-180 -90, -90 -90, -90 90, -180 90, -180 -90), (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50))",
814 0
815 ), (
816 "MULTIPOINT ((7.02 49.92), (90 180))",
817 "MULTIPOINT (7.02 49.92, 90.00 180.00)",
818 2
819 ), (
820 "MULTILINESTRING ((-180 -45, 0 -45), (0 45, 180 45))",
821 "MULTILINESTRING ((-180.0 -45.0, 0.0 -45.0), (0.0 45.0, 180.0 45.0))",
822 1
823 ), (
824 r#"MULTIPOLYGON((
825 (-180 -90, -90 -90, -90 90, -180 90, -180 -90),
826 (-120 -50, -100 -50, -100 -40, -120 -40, -120 -50)
827 ), (
828 (0 0, 10 0, 10 10, 0 10, 0 0)
829 ))"#,
830 "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)))",
831 0
832 ), (
833 "GEOMETRYCOLLECTION(POINT(7.02 49.92),POLYGON((0 0, 10 0, 10 10, 0 10, 0 0)))",
834 "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)))",
835 1
836 ), (
837 "BBOX(51.43,2.54,55.77,6.40)",
838 "BBOX (51.43, 2.54, 55.77, 6.40)",
839 2
840 ),
841 ];
842
843 for (ndx, (wkt, expected, precision)) in TV.iter().enumerate() {
844 if let Ok(g) = G::try_from(*wkt) {
846 let actual = g.to_wkt_fmt(*precision);
847 assert_eq!(actual, *expected);
848 } else {
849 panic!("Failed parsing WKT at index #{ndx}")
850 }
851 }
852
853 Ok(())
854 }
855
856 #[test]
857 #[tracing_test::traced_test]
858 fn test_geos_envelope() -> Result<(), Box<dyn Error>> {
859 let mut geom = geos::Geometry::new_from_wkt("LINESTRING(0 0, 1 3)")?;
860 geom.set_srid(3587);
861
862 let envelope = geom.envelope()?;
863 let srid = envelope.get_srid()?;
864 tracing::debug!("envelope SRS id = {srid}");
865 assert_eq!(envelope.to_wkt()?, "POLYGON ((0 0, 1 0, 1 3, 0 3, 0 0))");
866
867 Ok(())
868 }
869
870 #[test]
871 #[ignore = "GEOS possible bug"]
872 fn test_geos_wkt() -> Result<(), Box<dyn Error>> {
873 let expected = "POINT (1.0 3.0)";
874
875 let geom = geos::Geometry::new_from_wkt("POINT(1 3)")?;
876 let actual = geom.to_wkt_precision(0)?;
877
878 assert_eq!(actual, expected);
879 Ok(())
880 }
881
882 #[test]
883 #[ignore = "GEOS possible bug"]
884 fn test_geos_wkt_writer() -> Result<(), Box<dyn Error>> {
885 let expected = "POINT (1.00 3.00)";
886
887 let geom = geos::Geometry::new_from_wkt("POINT(1 3)")?;
888 let mut writer = geos::WKTWriter::new()?;
889 writer.set_rounding_precision(2);
890 let actual = writer.write(&geom)?;
891
892 assert_eq!(actual, expected);
893 Ok(())
894 }
895}