1use crate::error::{AlgorithmError, Result};
31use oxigdal_core::vector::{
32 Coordinate, Geometry, GeometryCollection, LineString, MultiLineString, MultiPoint,
33 MultiPolygon, Point, Polygon,
34};
35
36pub fn envelope(geometry: &Geometry) -> Result<Polygon> {
69 match geometry {
70 Geometry::Point(p) => envelope_point(p),
71 Geometry::LineString(ls) => envelope_linestring(ls),
72 Geometry::Polygon(p) => envelope_polygon(p),
73 Geometry::MultiPoint(mp) => envelope_multipoint(mp),
74 Geometry::MultiLineString(mls) => envelope_multilinestring(mls),
75 Geometry::MultiPolygon(mp) => envelope_multipolygon(mp),
76 Geometry::GeometryCollection(gc) => envelope_collection(gc),
77 }
78}
79
80pub fn envelope_point(point: &Point) -> Result<Polygon> {
97 let x = point.coord.x;
98 let y = point.coord.y;
99
100 let coords = vec![
102 Coordinate::new_2d(x, y),
103 Coordinate::new_2d(x, y),
104 Coordinate::new_2d(x, y),
105 Coordinate::new_2d(x, y),
106 Coordinate::new_2d(x, y),
107 ];
108
109 let exterior = LineString::new(coords).map_err(AlgorithmError::Core)?;
110 Polygon::new(exterior, vec![]).map_err(AlgorithmError::Core)
111}
112
113pub fn envelope_linestring(linestring: &LineString) -> Result<Polygon> {
129 if linestring.coords.is_empty() {
130 return Err(AlgorithmError::EmptyInput {
131 operation: "envelope_linestring",
132 });
133 }
134
135 let bounds = linestring
136 .bounds()
137 .ok_or_else(|| AlgorithmError::GeometryError {
138 message: "failed to compute bounds for linestring".to_string(),
139 })?;
140
141 create_envelope_polygon(bounds)
142}
143
144pub fn envelope_polygon(polygon: &Polygon) -> Result<Polygon> {
160 if polygon.exterior.coords.is_empty() {
161 return Err(AlgorithmError::EmptyInput {
162 operation: "envelope_polygon",
163 });
164 }
165
166 let bounds = polygon
167 .bounds()
168 .ok_or_else(|| AlgorithmError::GeometryError {
169 message: "failed to compute bounds for polygon".to_string(),
170 })?;
171
172 create_envelope_polygon(bounds)
173}
174
175pub fn envelope_multipoint(multipoint: &MultiPoint) -> Result<Polygon> {
191 if multipoint.points.is_empty() {
192 return Err(AlgorithmError::EmptyInput {
193 operation: "envelope_multipoint",
194 });
195 }
196
197 let bounds = multipoint
198 .bounds()
199 .ok_or_else(|| AlgorithmError::GeometryError {
200 message: "failed to compute bounds for multipoint".to_string(),
201 })?;
202
203 create_envelope_polygon(bounds)
204}
205
206pub fn envelope_multilinestring(multilinestring: &MultiLineString) -> Result<Polygon> {
222 if multilinestring.line_strings.is_empty() {
223 return Err(AlgorithmError::EmptyInput {
224 operation: "envelope_multilinestring",
225 });
226 }
227
228 let bounds = multilinestring
229 .bounds()
230 .ok_or_else(|| AlgorithmError::GeometryError {
231 message: "failed to compute bounds for multilinestring".to_string(),
232 })?;
233
234 create_envelope_polygon(bounds)
235}
236
237pub fn envelope_multipolygon(multipolygon: &MultiPolygon) -> Result<Polygon> {
253 if multipolygon.polygons.is_empty() {
254 return Err(AlgorithmError::EmptyInput {
255 operation: "envelope_multipolygon",
256 });
257 }
258
259 let bounds = multipolygon
260 .bounds()
261 .ok_or_else(|| AlgorithmError::GeometryError {
262 message: "failed to compute bounds for multipolygon".to_string(),
263 })?;
264
265 create_envelope_polygon(bounds)
266}
267
268pub fn envelope_collection(collection: &GeometryCollection) -> Result<Polygon> {
284 if collection.geometries.is_empty() {
285 return Err(AlgorithmError::EmptyInput {
286 operation: "envelope_collection",
287 });
288 }
289
290 let bounds = collection
291 .bounds()
292 .ok_or_else(|| AlgorithmError::GeometryError {
293 message: "failed to compute bounds for geometry collection".to_string(),
294 })?;
295
296 create_envelope_polygon(bounds)
297}
298
299fn create_envelope_polygon(bounds: (f64, f64, f64, f64)) -> Result<Polygon> {
313 let (min_x, min_y, max_x, max_y) = bounds;
314
315 let coords = vec![
317 Coordinate::new_2d(min_x, min_y),
318 Coordinate::new_2d(max_x, min_y),
319 Coordinate::new_2d(max_x, max_y),
320 Coordinate::new_2d(min_x, max_y),
321 Coordinate::new_2d(min_x, min_y), ];
323
324 let exterior = LineString::new(coords).map_err(AlgorithmError::Core)?;
325 Polygon::new(exterior, vec![]).map_err(AlgorithmError::Core)
326}
327
328pub fn envelope_with_buffer(geometry: &Geometry, buffer: f64) -> Result<Polygon> {
361 if buffer < 0.0 {
362 return Err(AlgorithmError::InvalidParameter {
363 parameter: "buffer",
364 message: "buffer must be non-negative".to_string(),
365 });
366 }
367
368 let base_envelope = envelope(geometry)?;
369 let bounds = base_envelope
370 .bounds()
371 .ok_or_else(|| AlgorithmError::GeometryError {
372 message: "failed to compute bounds for envelope".to_string(),
373 })?;
374
375 let (min_x, min_y, max_x, max_y) = bounds;
376
377 let expanded_bounds = (
379 min_x - buffer,
380 min_y - buffer,
381 max_x + buffer,
382 max_y + buffer,
383 );
384
385 create_envelope_polygon(expanded_bounds)
386}
387
388pub fn envelope_contains_point(envelope: &Polygon, point: &Point) -> bool {
399 if let Some((min_x, min_y, max_x, max_y)) = envelope.bounds() {
400 point.coord.x >= min_x
401 && point.coord.x <= max_x
402 && point.coord.y >= min_y
403 && point.coord.y <= max_y
404 } else {
405 false
406 }
407}
408
409pub fn envelopes_intersect(env1: &Polygon, env2: &Polygon) -> bool {
420 if let (Some(b1), Some(b2)) = (env1.bounds(), env2.bounds()) {
421 let (min_x1, min_y1, max_x1, max_y1) = b1;
422 let (min_x2, min_y2, max_x2, max_y2) = b2;
423
424 !(max_x1 < min_x2 || max_x2 < min_x1 || max_y1 < min_y2 || max_y2 < min_y1)
426 } else {
427 false
428 }
429}
430
431pub fn envelope_union(env1: &Polygon, env2: &Polygon) -> Result<Polygon> {
448 let b1 = env1.bounds().ok_or_else(|| AlgorithmError::GeometryError {
449 message: "failed to compute bounds for first envelope".to_string(),
450 })?;
451
452 let b2 = env2.bounds().ok_or_else(|| AlgorithmError::GeometryError {
453 message: "failed to compute bounds for second envelope".to_string(),
454 })?;
455
456 let (min_x1, min_y1, max_x1, max_y1) = b1;
457 let (min_x2, min_y2, max_x2, max_y2) = b2;
458
459 let union_bounds = (
460 min_x1.min(min_x2),
461 min_y1.min(min_y2),
462 max_x1.max(max_x2),
463 max_y1.max(max_y2),
464 );
465
466 create_envelope_polygon(union_bounds)
467}
468
469pub fn envelope_intersection(env1: &Polygon, env2: &Polygon) -> Result<Option<Polygon>> {
486 if !envelopes_intersect(env1, env2) {
487 return Ok(None);
488 }
489
490 let b1 = env1.bounds().ok_or_else(|| AlgorithmError::GeometryError {
491 message: "failed to compute bounds for first envelope".to_string(),
492 })?;
493
494 let b2 = env2.bounds().ok_or_else(|| AlgorithmError::GeometryError {
495 message: "failed to compute bounds for second envelope".to_string(),
496 })?;
497
498 let (min_x1, min_y1, max_x1, max_y1) = b1;
499 let (min_x2, min_y2, max_x2, max_y2) = b2;
500
501 let intersection_bounds = (
502 min_x1.max(min_x2),
503 min_y1.max(min_y2),
504 max_x1.min(max_x2),
505 max_y1.min(max_y2),
506 );
507
508 if intersection_bounds.0 <= intersection_bounds.2
510 && intersection_bounds.1 <= intersection_bounds.3
511 {
512 Ok(Some(create_envelope_polygon(intersection_bounds)?))
513 } else {
514 Ok(None)
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521
522 fn create_test_linestring() -> Result<LineString> {
523 let coords = vec![
524 Coordinate::new_2d(1.0, 1.0),
525 Coordinate::new_2d(5.0, 3.0),
526 Coordinate::new_2d(3.0, 7.0),
527 ];
528 LineString::new(coords).map_err(AlgorithmError::Core)
529 }
530
531 fn create_test_polygon() -> Result<Polygon> {
532 let coords = vec![
533 Coordinate::new_2d(2.0, 2.0),
534 Coordinate::new_2d(8.0, 2.0),
535 Coordinate::new_2d(8.0, 6.0),
536 Coordinate::new_2d(2.0, 6.0),
537 Coordinate::new_2d(2.0, 2.0),
538 ];
539 let exterior = LineString::new(coords).map_err(AlgorithmError::Core)?;
540 Polygon::new(exterior, vec![]).map_err(AlgorithmError::Core)
541 }
542
543 #[test]
544 fn test_envelope_point() {
545 let point = Point::new(3.0, 5.0);
546 let env = envelope_point(&point);
547
548 assert!(env.is_ok());
549 if let Ok(e) = env {
550 let bounds = e.bounds();
551 assert!(bounds.is_some());
552 if let Some((min_x, min_y, max_x, max_y)) = bounds {
553 assert_eq!(min_x, 3.0);
554 assert_eq!(min_y, 5.0);
555 assert_eq!(max_x, 3.0);
556 assert_eq!(max_y, 5.0);
557 }
558 }
559 }
560
561 #[test]
562 fn test_envelope_linestring() {
563 let line = create_test_linestring();
564 assert!(line.is_ok());
565
566 if let Ok(l) = line {
567 let env = envelope_linestring(&l);
568 assert!(env.is_ok());
569
570 if let Ok(e) = env {
571 let bounds = e.bounds();
572 assert!(bounds.is_some());
573 if let Some((min_x, min_y, max_x, max_y)) = bounds {
574 assert_eq!(min_x, 1.0);
575 assert_eq!(min_y, 1.0);
576 assert_eq!(max_x, 5.0);
577 assert_eq!(max_y, 7.0);
578 }
579 }
580 }
581 }
582
583 #[test]
584 fn test_envelope_polygon() {
585 let poly = create_test_polygon();
586 assert!(poly.is_ok());
587
588 if let Ok(p) = poly {
589 let env = envelope_polygon(&p);
590 assert!(env.is_ok());
591
592 if let Ok(e) = env {
593 let bounds = e.bounds();
594 assert!(bounds.is_some());
595 if let Some((min_x, min_y, max_x, max_y)) = bounds {
596 assert_eq!(min_x, 2.0);
597 assert_eq!(min_y, 2.0);
598 assert_eq!(max_x, 8.0);
599 assert_eq!(max_y, 6.0);
600 }
601 }
602 }
603 }
604
605 #[test]
606 fn test_envelope_multipoint() {
607 let points = vec![
608 Point::new(1.0, 1.0),
609 Point::new(5.0, 3.0),
610 Point::new(3.0, 7.0),
611 ];
612 let mp = MultiPoint::new(points);
613
614 let env = envelope_multipoint(&mp);
615 assert!(env.is_ok());
616
617 if let Ok(e) = env {
618 let bounds = e.bounds();
619 assert!(bounds.is_some());
620 if let Some((min_x, min_y, max_x, max_y)) = bounds {
621 assert_eq!(min_x, 1.0);
622 assert_eq!(min_y, 1.0);
623 assert_eq!(max_x, 5.0);
624 assert_eq!(max_y, 7.0);
625 }
626 }
627 }
628
629 #[test]
630 fn test_envelope_with_buffer() {
631 let point = Point::new(5.0, 5.0);
632 let geom = Geometry::Point(point);
633
634 let env = envelope_with_buffer(&geom, 2.0);
635 assert!(env.is_ok());
636
637 if let Ok(e) = env {
638 let bounds = e.bounds();
639 assert!(bounds.is_some());
640 if let Some((min_x, min_y, max_x, max_y)) = bounds {
641 assert_eq!(min_x, 3.0);
642 assert_eq!(min_y, 3.0);
643 assert_eq!(max_x, 7.0);
644 assert_eq!(max_y, 7.0);
645 }
646 }
647 }
648
649 #[test]
650 fn test_envelope_with_negative_buffer() {
651 let point = Point::new(5.0, 5.0);
652 let geom = Geometry::Point(point);
653
654 let env = envelope_with_buffer(&geom, -1.0);
655 assert!(env.is_err());
656 }
657
658 #[test]
659 fn test_envelope_contains_point() {
660 let poly = create_test_polygon();
661 assert!(poly.is_ok());
662
663 if let Ok(p) = poly {
664 let env = envelope_polygon(&p);
665 assert!(env.is_ok());
666
667 if let Ok(e) = env {
668 let inside = Point::new(5.0, 4.0);
670 assert!(envelope_contains_point(&e, &inside));
671
672 let boundary = Point::new(2.0, 4.0);
674 assert!(envelope_contains_point(&e, &boundary));
675
676 let outside = Point::new(10.0, 10.0);
678 assert!(!envelope_contains_point(&e, &outside));
679 }
680 }
681 }
682
683 #[test]
684 fn test_envelopes_intersect() {
685 let poly1 = create_test_polygon();
686 let coords2 = vec![
687 Coordinate::new_2d(5.0, 4.0),
688 Coordinate::new_2d(10.0, 4.0),
689 Coordinate::new_2d(10.0, 8.0),
690 Coordinate::new_2d(5.0, 8.0),
691 Coordinate::new_2d(5.0, 4.0),
692 ];
693 let ext2 = LineString::new(coords2);
694
695 assert!(poly1.is_ok() && ext2.is_ok());
696 if let (Ok(p1), Ok(e2)) = (poly1, ext2) {
697 let poly2 = Polygon::new(e2, vec![]);
698 assert!(poly2.is_ok());
699
700 if let Ok(p2) = poly2 {
701 let env1 = envelope_polygon(&p1);
702 let env2 = envelope_polygon(&p2);
703
704 assert!(env1.is_ok() && env2.is_ok());
705 if let (Ok(e1), Ok(e2)) = (env1, env2) {
706 assert!(envelopes_intersect(&e1, &e2));
707 }
708 }
709 }
710 }
711
712 #[test]
713 fn test_envelopes_no_intersect() {
714 let poly1 = create_test_polygon();
715 let coords2 = vec![
716 Coordinate::new_2d(20.0, 20.0),
717 Coordinate::new_2d(25.0, 20.0),
718 Coordinate::new_2d(25.0, 25.0),
719 Coordinate::new_2d(20.0, 25.0),
720 Coordinate::new_2d(20.0, 20.0),
721 ];
722 let ext2 = LineString::new(coords2);
723
724 assert!(poly1.is_ok() && ext2.is_ok());
725 if let (Ok(p1), Ok(e2)) = (poly1, ext2) {
726 let poly2 = Polygon::new(e2, vec![]);
727 assert!(poly2.is_ok());
728
729 if let Ok(p2) = poly2 {
730 let env1 = envelope_polygon(&p1);
731 let env2 = envelope_polygon(&p2);
732
733 assert!(env1.is_ok() && env2.is_ok());
734 if let (Ok(e1), Ok(e2)) = (env1, env2) {
735 assert!(!envelopes_intersect(&e1, &e2));
736 }
737 }
738 }
739 }
740
741 #[test]
742 fn test_envelope_union() {
743 let coords1 = vec![
744 Coordinate::new_2d(0.0, 0.0),
745 Coordinate::new_2d(5.0, 0.0),
746 Coordinate::new_2d(5.0, 5.0),
747 Coordinate::new_2d(0.0, 5.0),
748 Coordinate::new_2d(0.0, 0.0),
749 ];
750 let coords2 = vec![
751 Coordinate::new_2d(3.0, 3.0),
752 Coordinate::new_2d(8.0, 3.0),
753 Coordinate::new_2d(8.0, 8.0),
754 Coordinate::new_2d(3.0, 8.0),
755 Coordinate::new_2d(3.0, 3.0),
756 ];
757
758 let ext1 = LineString::new(coords1);
759 let ext2 = LineString::new(coords2);
760
761 assert!(ext1.is_ok() && ext2.is_ok());
762 if let (Ok(e1), Ok(e2)) = (ext1, ext2) {
763 let poly1 = Polygon::new(e1, vec![]);
764 let poly2 = Polygon::new(e2, vec![]);
765
766 assert!(poly1.is_ok() && poly2.is_ok());
767 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
768 let env1 = envelope_polygon(&p1);
769 let env2 = envelope_polygon(&p2);
770
771 assert!(env1.is_ok() && env2.is_ok());
772 if let (Ok(e1), Ok(e2)) = (env1, env2) {
773 let union = envelope_union(&e1, &e2);
774 assert!(union.is_ok());
775
776 if let Ok(u) = union {
777 let bounds = u.bounds();
778 assert!(bounds.is_some());
779 if let Some((min_x, min_y, max_x, max_y)) = bounds {
780 assert_eq!(min_x, 0.0);
781 assert_eq!(min_y, 0.0);
782 assert_eq!(max_x, 8.0);
783 assert_eq!(max_y, 8.0);
784 }
785 }
786 }
787 }
788 }
789 }
790
791 #[test]
792 fn test_envelope_intersection() {
793 let coords1 = vec![
794 Coordinate::new_2d(0.0, 0.0),
795 Coordinate::new_2d(6.0, 0.0),
796 Coordinate::new_2d(6.0, 6.0),
797 Coordinate::new_2d(0.0, 6.0),
798 Coordinate::new_2d(0.0, 0.0),
799 ];
800 let coords2 = vec![
801 Coordinate::new_2d(3.0, 3.0),
802 Coordinate::new_2d(9.0, 3.0),
803 Coordinate::new_2d(9.0, 9.0),
804 Coordinate::new_2d(3.0, 9.0),
805 Coordinate::new_2d(3.0, 3.0),
806 ];
807
808 let ext1 = LineString::new(coords1);
809 let ext2 = LineString::new(coords2);
810
811 assert!(ext1.is_ok() && ext2.is_ok());
812 if let (Ok(e1), Ok(e2)) = (ext1, ext2) {
813 let poly1 = Polygon::new(e1, vec![]);
814 let poly2 = Polygon::new(e2, vec![]);
815
816 assert!(poly1.is_ok() && poly2.is_ok());
817 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
818 let env1 = envelope_polygon(&p1);
819 let env2 = envelope_polygon(&p2);
820
821 assert!(env1.is_ok() && env2.is_ok());
822 if let (Ok(e1), Ok(e2)) = (env1, env2) {
823 let intersection = envelope_intersection(&e1, &e2);
824 assert!(intersection.is_ok());
825
826 if let Ok(Some(i)) = intersection {
827 let bounds = i.bounds();
828 assert!(bounds.is_some());
829 if let Some((min_x, min_y, max_x, max_y)) = bounds {
830 assert_eq!(min_x, 3.0);
831 assert_eq!(min_y, 3.0);
832 assert_eq!(max_x, 6.0);
833 assert_eq!(max_y, 6.0);
834 }
835 }
836 }
837 }
838 }
839 }
840
841 #[test]
842 fn test_envelope_intersection_no_overlap() {
843 let coords1 = vec![
844 Coordinate::new_2d(0.0, 0.0),
845 Coordinate::new_2d(5.0, 0.0),
846 Coordinate::new_2d(5.0, 5.0),
847 Coordinate::new_2d(0.0, 5.0),
848 Coordinate::new_2d(0.0, 0.0),
849 ];
850 let coords2 = vec![
851 Coordinate::new_2d(10.0, 10.0),
852 Coordinate::new_2d(15.0, 10.0),
853 Coordinate::new_2d(15.0, 15.0),
854 Coordinate::new_2d(10.0, 15.0),
855 Coordinate::new_2d(10.0, 10.0),
856 ];
857
858 let ext1 = LineString::new(coords1);
859 let ext2 = LineString::new(coords2);
860
861 assert!(ext1.is_ok() && ext2.is_ok());
862 if let (Ok(e1), Ok(e2)) = (ext1, ext2) {
863 let poly1 = Polygon::new(e1, vec![]);
864 let poly2 = Polygon::new(e2, vec![]);
865
866 assert!(poly1.is_ok() && poly2.is_ok());
867 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
868 let env1 = envelope_polygon(&p1);
869 let env2 = envelope_polygon(&p2);
870
871 assert!(env1.is_ok() && env2.is_ok());
872 if let (Ok(e1), Ok(e2)) = (env1, env2) {
873 let intersection = envelope_intersection(&e1, &e2);
874 assert!(intersection.is_ok());
875
876 if let Ok(result) = intersection {
877 assert!(result.is_none());
878 }
879 }
880 }
881 }
882 }
883
884 #[test]
885 fn test_envelope_empty_linestring() {
886 let coords: Vec<Coordinate> = vec![];
887 let line = LineString::new(coords);
888
889 assert!(line.is_err());
891 }
892
893 #[test]
894 fn test_envelope_multipoint_empty() {
895 let mp = MultiPoint::empty();
896 let env = envelope_multipoint(&mp);
897 assert!(env.is_err());
898 }
899
900 #[test]
901 fn test_envelope_geometry_dispatch() {
902 let point = Point::new(3.0, 5.0);
903 let geom = Geometry::Point(point);
904
905 let env = envelope(&geom);
906 assert!(env.is_ok());
907
908 if let Ok(e) = env {
909 let bounds = e.bounds();
910 assert!(bounds.is_some());
911 }
912 }
913}