shapefile_gbk/record/
bbox.rs

1//! Bounding Boxes
2use super::traits::{GrowablePoint, HasM, HasXY, HasZ, ShrinkablePoint};
3use super::EsriShape;
4use super::PointZ;
5use crate::writer::{f64_max, f64_min};
6
7/// The Bounding Box type used in this crate.
8///
9/// Each shape that is a collection of points have a bounding box
10/// associated to it generally accessible using the `bbox()` method.
11///
12/// # Example
13///
14/// ```
15/// use shapefile::{PointM, PolylineM};
16/// let poly = PolylineM::new(vec![
17///     PointM::new(1.0, 2.0, 13.42),
18///     PointM::new(2.0, 1.0, 42.3713),
19/// ]);
20///
21/// let bbox = poly.bbox();
22/// assert_eq!(bbox.min, PointM::new(1.0, 1.0, 13.42));
23/// assert_eq!(bbox.max, PointM::new(2.0, 2.0, 42.3713));
24/// ```
25#[derive(Debug, Copy, Clone, PartialEq)]
26pub struct GenericBBox<PointType> {
27    pub max: PointType,
28    pub min: PointType,
29}
30
31impl<PointType> GenericBBox<PointType> {
32    pub(crate) fn from_points(points: &[PointType]) -> Self
33    where
34        PointType: Copy + ShrinkablePoint + GrowablePoint,
35    {
36        let mut min_point = points[0];
37        let mut max_point = points[0];
38
39        for point in &points[1..] {
40            min_point.shrink(point);
41            max_point.grow(point);
42        }
43
44        Self {
45            max: max_point,
46            min: min_point,
47        }
48    }
49
50    pub(crate) fn grow_from_points(&mut self, points: &[PointType])
51    where
52        PointType: ShrinkablePoint + GrowablePoint,
53    {
54        for point in points {
55            self.min.shrink(point);
56            self.max.grow(point);
57        }
58    }
59
60    pub(crate) fn from_parts(parts: &[Vec<PointType>]) -> Self
61    where
62        PointType: ShrinkablePoint + GrowablePoint + Copy,
63    {
64        let mut bbox = Self::from_points(&parts[0]);
65        for part in &parts[1..] {
66            bbox.grow_from_points(part);
67        }
68        bbox
69    }
70}
71
72impl<PointType: HasXY> GenericBBox<PointType> {
73    pub fn x_range(&self) -> [f64; 2] {
74        [self.min.x(), self.max.x()]
75    }
76
77    pub fn y_range(&self) -> [f64; 2] {
78        [self.min.y(), self.max.y()]
79    }
80}
81
82impl<PointType: HasZ> GenericBBox<PointType> {
83    pub fn z_range(&self) -> [f64; 2] {
84        [self.min.z(), self.max.z()]
85    }
86}
87
88impl<PointType: HasM> GenericBBox<PointType> {
89    pub fn m_range(&self) -> [f64; 2] {
90        [self.min.m(), self.max.m()]
91    }
92}
93
94impl<PointType: Default> Default for GenericBBox<PointType> {
95    fn default() -> Self {
96        Self {
97            max: PointType::default(),
98            min: PointType::default(),
99        }
100    }
101}
102
103pub type BBoxZ = GenericBBox<PointZ>;
104
105impl BBoxZ {
106    pub(crate) fn grow_from_shape<S: EsriShape>(&mut self, shape: &S) {
107        let x_range = shape.x_range();
108        let y_range = shape.y_range();
109        let z_range = shape.z_range();
110        let m_range = shape.m_range();
111
112        self.min.x = f64_min(x_range[0], self.min.x);
113        self.max.x = f64_max(x_range[1], self.max.x);
114        self.min.y = f64_min(y_range[0], self.min.y);
115        self.max.y = f64_max(y_range[1], self.max.y);
116
117        if S::shapetype().has_m() {
118            self.min.m = f64_min(m_range[0], self.min.m);
119            self.max.m = f64_max(m_range[1], self.max.m);
120        }
121
122        if S::shapetype().has_z() {
123            self.min.z = f64_min(z_range[0], self.min.z);
124            self.max.z = f64_max(z_range[1], self.max.z);
125        }
126    }
127}