1use crate::error::Error;
2use crate::model::geometry::DirectPosition;
3use crate::model::geometry::primitives::{
4 LinearRing, Polygon, RingProperty, Solid, SurfaceProperty, TriangulatedSurface,
5};
6use crate::model::geometry::primitives::{RingKind, Shell};
7use crate::model::geometry::primitives::{ShellProperty, SurfaceKind};
8use nalgebra::{Isometry3, Point3, Vector3};
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq, Default)]
19pub struct Envelope {
20 lower_corner: DirectPosition,
21 upper_corner: DirectPosition,
22
23 srs_name: Option<String>,
24 srs_dimension: Option<u8>,
25}
26
27impl Envelope {
28 pub fn new(lower_corner: DirectPosition, upper_corner: DirectPosition) -> Result<Self, Error> {
47 if lower_corner.x() > upper_corner.x() {
48 return Err(Error::InvalidEnvelopeBounds {
49 axis: "x",
50 lower: lower_corner.x(),
51 upper: upper_corner.x(),
52 });
53 }
54 if lower_corner.y() > upper_corner.y() {
55 return Err(Error::InvalidEnvelopeBounds {
56 axis: "y",
57 lower: lower_corner.y(),
58 upper: upper_corner.y(),
59 });
60 }
61 if lower_corner.z() > upper_corner.z() {
62 return Err(Error::InvalidEnvelopeBounds {
63 axis: "z",
64 lower: lower_corner.z(),
65 upper: upper_corner.z(),
66 });
67 }
68
69 Ok(Self {
70 lower_corner,
71 upper_corner,
72 srs_name: None,
73 srs_dimension: None,
74 })
75 }
76
77 pub(crate) fn new_unchecked(
85 lower_corner: DirectPosition,
86 upper_corner: DirectPosition,
87 ) -> Self {
88 debug_assert!(
89 {
90 let lc: Point3<f64> = lower_corner.into();
91 let uc: Point3<f64> = upper_corner.into();
92 lc <= uc
93 },
94 "lower_corner must be <= upper_corner"
95 );
96
97 Self {
98 lower_corner,
99 upper_corner,
100 srs_name: None,
101 srs_dimension: None,
102 }
103 }
104
105 pub fn lower_corner(&self) -> &DirectPosition {
107 &self.lower_corner
108 }
109
110 pub fn upper_corner(&self) -> &DirectPosition {
112 &self.upper_corner
113 }
114
115 pub fn srs_name(&self) -> Option<&str> {
118 self.srs_name.as_deref()
119 }
120
121 pub fn srs_dimension(&self) -> Option<u8> {
124 self.srs_dimension
125 }
126
127 pub fn set_srs_name(&mut self, srs_name: Option<String>) {
131 self.srs_name = srs_name;
132 }
133
134 pub fn set_srs_dimension(&mut self, srs_dimension: Option<u8>) {
137 self.srs_dimension = srs_dimension;
138 }
139
140 pub fn size(&self) -> Vector3<f64> {
142 let lower_corner_point: Point3<f64> = self.lower_corner.into();
143 let upper_corner_point: Point3<f64> = self.upper_corner.into();
144 upper_corner_point - lower_corner_point
145 }
146
147 pub fn size_x(&self) -> f64 {
149 self.upper_corner.x() - self.lower_corner.x()
150 }
151
152 pub fn size_y(&self) -> f64 {
154 self.upper_corner.y() - self.lower_corner.y()
155 }
156
157 pub fn size_z(&self) -> f64 {
159 self.upper_corner.z() - self.lower_corner.z()
160 }
161
162 pub fn volume(&self) -> f64 {
166 self.size_x() * self.size_y() * self.size_z()
167 }
168
169 pub fn is_point(&self) -> bool {
171 self.lower_corner == self.upper_corner
172 }
173
174 #[allow(clippy::nonminimal_bool)]
176 pub fn is_linear(&self) -> bool {
177 let nx = self.size_x() > 0.0;
178 let ny = self.size_y() > 0.0;
179 let nz = self.size_z() > 0.0;
180 (nx && !ny && !nz) || (!nx && ny && !nz) || (!nx && !ny && nz)
181 }
182
183 #[allow(clippy::nonminimal_bool)]
185 pub fn is_surface(&self) -> bool {
186 let nx = self.size_x() > 0.0;
187 let ny = self.size_y() > 0.0;
188 let nz = self.size_z() > 0.0;
189 (nx && ny && !nz) || (nx && !ny && nz) || (!nx && ny && nz)
190 }
191
192 pub fn is_volume(&self) -> bool {
194 self.size_x() > 0.0 && self.size_y() > 0.0 && self.size_z() > 0.0
195 }
196
197 fn non_zero_extents(&self) -> u8 {
198 [self.size_x(), self.size_y(), self.size_z()]
199 .iter()
200 .filter(|&&s| s > 0.0)
201 .count() as u8
202 }
203
204 pub fn center(&self) -> DirectPosition {
208 DirectPosition::new(
209 self.lower_corner.x() + self.size_x() / 2.0,
210 self.lower_corner.y() + self.size_y() / 2.0,
211 self.lower_corner.z() + self.size_z() / 2.0,
212 )
213 .expect("envelope corners are finite")
214 }
215
216 pub fn contains(&self, point: &DirectPosition) -> bool {
218 let lower_corner: Point3<f64> = self.lower_corner.into();
219 let upper_corner: Point3<f64> = self.upper_corner.into();
220 let point: Point3<f64> = (*point).into();
221
222 lower_corner <= point && point <= upper_corner
223 }
224
225 pub fn contains_envelope(&self, envelope: &Envelope) -> bool {
227 self.contains(&envelope.lower_corner) && self.contains(&envelope.upper_corner)
228 }
229
230 pub fn contains_envelope_partially(&self, envelope: &Envelope) -> bool {
232 self.contains(&envelope.lower_corner) || self.contains(&envelope.upper_corner)
233 }
234
235 pub fn enlarge(&self, distance: f64) -> Result<Envelope, Error> {
245 let lower_corner = DirectPosition::new(
246 self.lower_corner.x() - distance,
247 self.lower_corner.y() - distance,
248 self.lower_corner.z() - distance,
249 )?;
250 let upper_corner = DirectPosition::new(
251 self.upper_corner.x() + distance,
252 self.upper_corner.y() + distance,
253 self.upper_corner.z() + distance,
254 )?;
255
256 Envelope::new(lower_corner, upper_corner)
257 }
258
259 pub fn apply_transform(&mut self, m: &Isometry3<f64>) {
282 let transformed_lower_corner: Point3<f64> = m * Point3::from(self.lower_corner);
283 let transformed_upper_corner: Point3<f64> = m * Point3::from(self.upper_corner);
284
285 self.lower_corner = DirectPosition::new(
286 transformed_lower_corner.x.min(transformed_upper_corner.x),
287 transformed_lower_corner.y.min(transformed_upper_corner.y),
288 transformed_lower_corner.z.min(transformed_upper_corner.z),
289 )
290 .expect("envelope corners are finite");
291 self.upper_corner = DirectPosition::new(
292 transformed_lower_corner.x.max(transformed_upper_corner.x),
293 transformed_lower_corner.y.max(transformed_upper_corner.y),
294 transformed_lower_corner.z.max(transformed_upper_corner.z),
295 )
296 .expect("envelope corners are finite");
297 }
298}
299
300impl Envelope {
301 pub fn from_envelopes(envelopes: &[Self]) -> Option<Self> {
306 let first = envelopes.first()?;
307
308 let (lower, upper) = envelopes.iter().skip(1).fold(
309 (first.lower_corner, first.upper_corner),
310 |(lo, hi), e| {
311 let new_lo = DirectPosition::new(
312 lo.x().min(e.lower_corner.x()),
313 lo.y().min(e.lower_corner.y()),
314 lo.z().min(e.lower_corner.z()),
315 )
316 .unwrap();
317 let new_hi = DirectPosition::new(
318 hi.x().max(e.upper_corner.x()),
319 hi.y().max(e.upper_corner.y()),
320 hi.z().max(e.upper_corner.z()),
321 )
322 .unwrap();
323 (new_lo, new_hi)
324 },
325 );
326
327 Some(Envelope::new_unchecked(lower, upper))
328 }
329
330 pub fn from_points(points: &[DirectPosition]) -> Result<Self, Error> {
336 if points.is_empty() {
337 return Err(Error::TooFewElements {
338 geometry: "Envelope::from_points",
339 minimum: 1,
340 spec: None,
341 id: None,
342 detail: None,
343 });
344 }
345
346 let first = &points[0];
347 let (mut min_x, mut min_y, mut min_z) = (first.x(), first.y(), first.z());
348 let (mut max_x, mut max_y, mut max_z) = (first.x(), first.y(), first.z());
349
350 for point in points.iter().skip(1) {
351 min_x = min_x.min(point.x());
352 min_y = min_y.min(point.y());
353 min_z = min_z.min(point.z());
354 max_x = max_x.max(point.x());
355 max_y = max_y.max(point.y());
356 max_z = max_z.max(point.z());
357 }
358
359 let lower_corner = DirectPosition::new(min_x, min_y, min_z)?;
360 let upper_corner = DirectPosition::new(max_x, max_y, max_z)?;
361
362 Ok(Self::new_unchecked(lower_corner, upper_corner))
363 }
364}
365
366impl Envelope {
367 pub fn to_solid(&self) -> Result<Solid, Error> {
376 if !self.is_volume() {
377 return Err(Error::NotAVolume {
378 non_zero_extents: self.non_zero_extents(),
379 });
380 }
381
382 let (lx, ly, lz) = (
383 self.lower_corner.x(),
384 self.lower_corner.y(),
385 self.lower_corner.z(),
386 );
387 let (hx, hy, hz) = (
388 self.upper_corner.x(),
389 self.upper_corner.y(),
390 self.upper_corner.z(),
391 );
392
393 let p000 = DirectPosition::new(lx, ly, lz).expect("envelope corners are finite");
394 let p100 = DirectPosition::new(hx, ly, lz).expect("envelope corners are finite");
395 let p110 = DirectPosition::new(hx, hy, lz).expect("envelope corners are finite");
396 let p010 = DirectPosition::new(lx, hy, lz).expect("envelope corners are finite");
397 let p001 = DirectPosition::new(lx, ly, hz).expect("envelope corners are finite");
398 let p101 = DirectPosition::new(hx, ly, hz).expect("envelope corners are finite");
399 let p111 = DirectPosition::new(hx, hy, hz).expect("envelope corners are finite");
400 let p011 = DirectPosition::new(lx, hy, hz).expect("envelope corners are finite");
401
402 let face_rings: [Vec<DirectPosition>; 6] = [
403 vec![p000, p010, p110, p100], vec![p001, p101, p111, p011], vec![p000, p100, p101, p001], vec![p010, p011, p111, p110], vec![p000, p001, p011, p010], vec![p100, p110, p111, p101], ];
410
411 let members: Vec<SurfaceProperty> = face_rings
412 .into_iter()
413 .map(|points| {
414 let ring = LinearRing::new(points).ok()?;
415 let polygon =
416 Polygon::new(Some(RingProperty::new(RingKind::LinearRing(ring))), vec![])
417 .ok()?;
418 Some(SurfaceProperty::new(SurfaceKind::Polygon(polygon)))
419 })
420 .collect::<Option<_>>()
421 .expect("envelope corners are finite and valid");
422 let shell = Shell::new(members).expect("envelope is valid");
423 let shell_property = ShellProperty::new(shell);
424
425 let solid = Solid::new(Some(shell_property)).expect("envelope is valid");
426 Ok(solid)
427 }
428
429 pub fn to_polygon(&self) -> Result<Polygon, Error> {
438 if !self.is_surface() {
439 return Err(Error::NotASurface {
440 non_zero_extents: self.non_zero_extents(),
441 });
442 }
443
444 let (lx, ly, lz) = (
445 self.lower_corner.x(),
446 self.lower_corner.y(),
447 self.lower_corner.z(),
448 );
449 let (hx, hy, hz) = (
450 self.upper_corner.x(),
451 self.upper_corner.y(),
452 self.upper_corner.z(),
453 );
454
455 let points = if self.size_z() == 0.0 {
456 vec![
458 DirectPosition::new(lx, ly, lz).expect("envelope corners are finite"),
459 DirectPosition::new(hx, ly, lz).expect("envelope corners are finite"),
460 DirectPosition::new(hx, hy, lz).expect("envelope corners are finite"),
461 DirectPosition::new(lx, hy, lz).expect("envelope corners are finite"),
462 ]
463 } else if self.size_y() == 0.0 {
464 vec![
466 DirectPosition::new(lx, ly, lz).expect("envelope corners are finite"),
467 DirectPosition::new(lx, ly, hz).expect("envelope corners are finite"),
468 DirectPosition::new(hx, ly, hz).expect("envelope corners are finite"),
469 DirectPosition::new(hx, ly, lz).expect("envelope corners are finite"),
470 ]
471 } else {
472 vec![
474 DirectPosition::new(lx, ly, lz).expect("envelope corners are finite"),
475 DirectPosition::new(lx, hy, lz).expect("envelope corners are finite"),
476 DirectPosition::new(lx, hy, hz).expect("envelope corners are finite"),
477 DirectPosition::new(lx, ly, hz).expect("envelope corners are finite"),
478 ]
479 };
480
481 let ring = LinearRing::new(points).expect("envelope corners are finite and valid");
482 Polygon::new(Some(RingProperty::new(RingKind::LinearRing(ring))), vec![]).map_err(|_| {
483 Error::NotASurface {
484 non_zero_extents: self.non_zero_extents(),
485 }
486 })
487 }
488
489 pub fn to_triangulated_surface(&self) -> Result<TriangulatedSurface, Error> {
498 if self.is_surface() {
499 self.to_polygon()?.triangulate()
500 } else if self.is_volume() {
501 self.to_solid()?
502 .exterior()
503 .as_ref()
504 .expect("must be created")
505 .object
506 .as_ref()
507 .expect("must be created")
508 .triangulate()
509 } else {
510 Err(Error::NotSurfaceOrVolume {
511 non_zero_extents: self.non_zero_extents(),
512 })
513 }
514 }
515}
516
517impl fmt::Display for Envelope {
518 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
519 write!(
520 f,
521 "Envelope[{}, {}, {} -> {}, {}, {}]",
522 self.lower_corner.x(),
523 self.lower_corner.y(),
524 self.lower_corner.z(),
525 self.upper_corner.x(),
526 self.upper_corner.y(),
527 self.upper_corner.z()
528 )
529 }
530}
531
532#[cfg(test)]
533mod tests {
534 use super::*;
535
536 fn pos(x: f64, y: f64, z: f64) -> DirectPosition {
537 DirectPosition::new(x, y, z).unwrap()
538 }
539
540 fn env(lx: f64, ly: f64, lz: f64, ux: f64, uy: f64, uz: f64) -> Envelope {
541 Envelope::new(pos(lx, ly, lz), pos(ux, uy, uz)).unwrap()
542 }
543
544 #[test]
545 fn from_envelopes_empty_returns_none() {
546 let result = Envelope::from_envelopes(&[]);
547 assert!(result.is_none());
548 }
549
550 #[test]
551 fn from_envelopes_single_returns_same_envelope() {
552 let e = env(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
553 let result = Envelope::from_envelopes(&[e.clone()]).unwrap();
554
555 assert_eq!(result, e);
556 }
557
558 #[test]
559 fn from_envelopes_two_disjoint() {
560 let a = env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
561 let b = env(5.0, 6.0, 7.0, 8.0, 9.0, 10.0);
562 let result = Envelope::from_envelopes(&[a, b]).unwrap();
563
564 assert_eq!(result, env(0.0, 0.0, 0.0, 8.0, 9.0, 10.0));
565 }
566
567 #[test]
568 fn from_envelopes_overlapping() {
569 let a = env(0.0, 0.0, 0.0, 5.0, 5.0, 5.0);
570 let b = env(3.0, 3.0, 3.0, 7.0, 7.0, 7.0);
571 let result = Envelope::from_envelopes(&[a, b]).unwrap();
572
573 assert_eq!(result, env(0.0, 0.0, 0.0, 7.0, 7.0, 7.0));
574 }
575
576 #[test]
577 fn from_envelopes_one_contains_the_other() {
578 let outer = env(0.0, 0.0, 0.0, 10.0, 10.0, 10.0);
579 let inner = env(2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
580 let result = Envelope::from_envelopes(&[outer.clone(), inner]).unwrap();
581
582 assert_eq!(result, outer);
583 }
584
585 #[test]
586 fn from_envelopes_multiple() {
587 let a = env(0.0, 10.0, 20.0, 1.0, 11.0, 21.0);
588 let b = env(-5.0, 8.0, 25.0, 2.0, 12.0, 30.0);
589 let c = env(1.0, 9.0, 18.0, 3.0, 15.0, 22.0);
590 let result = Envelope::from_envelopes(&[a, b, c]).unwrap();
591
592 assert_eq!(result, env(-5.0, 8.0, 18.0, 3.0, 15.0, 30.0));
593 }
594
595 #[test]
596 fn from_envelopes_with_negative_coords() {
597 let a = env(-10.0, -20.0, -30.0, -1.0, -2.0, -3.0);
598 let b = env(-5.0, -25.0, -15.0, 0.0, -1.0, 0.0);
599 let result = Envelope::from_envelopes(&[a, b]).unwrap();
600
601 assert_eq!(result, env(-10.0, -25.0, -30.0, 0.0, -1.0, 0.0));
602 }
603
604 #[test]
605 fn from_envelopes_zero_volume_envelopes() {
606 let a = env(1.0, 1.0, 1.0, 1.0, 1.0, 1.0); let b = env(3.0, 3.0, 3.0, 3.0, 3.0, 3.0); let result = Envelope::from_envelopes(&[a, b]).unwrap();
609
610 assert_eq!(result, env(1.0, 1.0, 1.0, 3.0, 3.0, 3.0));
611 }
612
613 #[test]
614 fn is_point_when_corners_equal() {
615 let e = env(1.0, 2.0, 3.0, 1.0, 2.0, 3.0);
616 assert!(e.is_point());
617 assert!(!e.is_linear());
618 assert!(!e.is_surface());
619 assert!(!e.is_volume());
620 }
621
622 #[test]
623 fn is_linear_along_x() {
624 let e = env(0.0, 0.0, 0.0, 1.0, 0.0, 0.0);
625 assert!(!e.is_point());
626 assert!(e.is_linear());
627 assert!(!e.is_surface());
628 assert!(!e.is_volume());
629 }
630
631 #[test]
632 fn is_linear_along_y() {
633 let e = env(0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
634 assert!(e.is_linear());
635 }
636
637 #[test]
638 fn is_linear_along_z() {
639 let e = env(0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
640 assert!(e.is_linear());
641 }
642
643 #[test]
644 fn is_surface_xy_plane() {
645 let e = env(0.0, 0.0, 0.0, 1.0, 1.0, 0.0);
646 assert!(!e.is_point());
647 assert!(!e.is_linear());
648 assert!(e.is_surface());
649 assert!(!e.is_volume());
650 }
651
652 #[test]
653 fn is_surface_xz_plane() {
654 let e = env(0.0, 0.0, 0.0, 1.0, 0.0, 1.0);
655 assert!(e.is_surface());
656 }
657
658 #[test]
659 fn is_surface_yz_plane() {
660 let e = env(0.0, 0.0, 0.0, 0.0, 1.0, 1.0);
661 assert!(e.is_surface());
662 }
663
664 #[test]
665 fn is_volume_all_extents_nonzero() {
666 let e = env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
667 assert!(!e.is_point());
668 assert!(!e.is_linear());
669 assert!(!e.is_surface());
670 assert!(e.is_volume());
671 }
672
673 #[test]
674 fn to_polygon_returns_err_for_point() {
675 assert_eq!(
676 env(1.0, 1.0, 1.0, 1.0, 1.0, 1.0).to_polygon(),
677 Err(Error::NotASurface {
678 non_zero_extents: 0
679 })
680 );
681 }
682
683 #[test]
684 fn to_polygon_returns_err_for_linear() {
685 assert_eq!(
686 env(0.0, 0.0, 0.0, 1.0, 0.0, 0.0).to_polygon(),
687 Err(Error::NotASurface {
688 non_zero_extents: 1
689 })
690 );
691 }
692
693 #[test]
694 fn to_polygon_returns_err_for_volume() {
695 assert_eq!(
696 env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0).to_polygon(),
697 Err(Error::NotASurface {
698 non_zero_extents: 3
699 })
700 );
701 }
702
703 #[test]
704 fn to_polygon_xy_plane() {
705 assert!(env(0.0, 0.0, 0.0, 2.0, 3.0, 0.0).to_polygon().is_ok());
706 }
707
708 #[test]
709 fn to_polygon_xz_plane() {
710 assert!(env(0.0, 0.0, 0.0, 2.0, 0.0, 3.0).to_polygon().is_ok());
711 }
712
713 #[test]
714 fn to_polygon_yz_plane() {
715 assert!(env(0.0, 0.0, 0.0, 0.0, 2.0, 3.0).to_polygon().is_ok());
716 }
717
718 #[test]
719 fn to_triangulated_surface_returns_err_for_point() {
720 assert_eq!(
721 env(0.0, 0.0, 0.0, 0.0, 0.0, 0.0).to_triangulated_surface(),
722 Err(Error::NotSurfaceOrVolume {
723 non_zero_extents: 0
724 })
725 );
726 }
727
728 #[test]
729 fn to_triangulated_surface_returns_err_for_linear() {
730 assert_eq!(
731 env(0.0, 0.0, 0.0, 1.0, 0.0, 0.0).to_triangulated_surface(),
732 Err(Error::NotSurfaceOrVolume {
733 non_zero_extents: 1
734 })
735 );
736 }
737
738 #[test]
739 fn to_triangulated_surface_surface_has_two_triangles() {
740 let result = env(0.0, 0.0, 0.0, 2.0, 3.0, 0.0)
741 .to_triangulated_surface()
742 .unwrap();
743 assert_eq!(result.triangles().len(), 2);
744 }
745
746 #[test]
747 fn to_triangulated_surface_volume_has_twelve_triangles() {
748 let result = env(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
749 .to_triangulated_surface()
750 .unwrap();
751 assert_eq!(result.triangles().len(), 12);
752 }
753
754 #[test]
755 fn envelope_contains() {
756 let lower_corner = DirectPosition::new(1.0, 2.0, 3.0).unwrap();
757 let upper_corner = DirectPosition::new(2.0, 3.0, 4.0).unwrap();
758 let envelope = Envelope::new(lower_corner, upper_corner).unwrap();
759 let point_a = DirectPosition::new(1.5, 2.5, 3.5).unwrap();
760 let point_b = DirectPosition::new(2.5, 3.5, 4.5).unwrap();
761
762 assert!(envelope.contains(&point_a));
763 assert!(!envelope.contains(&point_b));
764 }
765}