s2json_core/geometry/
bbox.rs

1use crate::*;
2use alloc::fmt;
3use core::{
4    cmp::Ordering,
5    ops::{Mul, MulAssign, Sub},
6};
7use serde::{
8    Deserialize, Deserializer, Serialize, Serializer,
9    de::{self, SeqAccess, Visitor},
10    ser::SerializeTuple,
11};
12
13/// Trait for types that have a min and max value. Used by [`BBox`] and [`BBox3D`]
14pub trait Bounded {
15    /// Get the minimum value
16    fn min_value() -> Self;
17    /// Get the maximum value
18    fn max_value() -> Self;
19}
20macro_rules! impl_bounded {
21    ($($t:ty),*) => {
22        $(
23            impl Bounded for $t {
24                fn min_value() -> Self {
25                    <$t>::MIN
26                }
27                fn max_value() -> Self {
28                    <$t>::MAX
29                }
30            }
31        )*
32    };
33}
34
35// Implement for common numeric types
36impl_bounded!(i8, i16, i32, i64, i128, u8, u16, u32, u64, u128, isize, usize, f32, f64);
37
38/// # Bounding Box
39///
40/// ## Description
41///
42/// A Bounding Box ensures a min-max range of values
43///
44/// The order is (left, bottom, right, top) when storing as a flattened array.
45///
46/// If WM, then the projection is lon-lat. If S2, then the projection is s-t
47///
48/// Defaults to f64 as the base type.
49///
50/// Implements [`MValueCompatible`]
51///
52/// ## Usage
53///
54/// ## From/To
55/// - [`MValue`]
56/// - [`ValueType`]
57///
58/// ### Any Type BBox
59/// - [`BBox::new`]: Creates a new BBox
60/// - [`BBox::point_overlap`]: Checks if a point is within the BBox
61/// - [`BBox::merge`]: Merges another bounding box with this one
62/// - [`BBox::merge_in_place`]: Merges in place another bounding box with this one
63/// - [`BBox::overlap`]: Checks if another bounding box overlaps with this one
64/// - [`BBox::clip`]: Clips the bounding box along an axis
65/// - [`BBox::inside`]: Checks if this bounding box is inside another
66/// - [`BBox::area`]: Returns the area of the bounding box
67///
68/// ### `f64` Type BBox
69/// Note that all the input geometry uses the [`GetXY`] trait.
70/// - [`BBox::from_point`]: Creates a new BBox from a point
71/// - [`BBox::from_linestring`]: Creates a new BBox from a linestring
72/// - [`BBox::from_multi_linestring`]: Creates a new BBox from a multi-linestring
73/// - [`BBox::from_polygon`]: Creates a new BBox from a polygon
74/// - [`BBox::from_multi_polygon`]: Creates a new BBox from a multi-polygon
75/// - [`BBox::extend_from_point`]: Extends the bounding box with a point
76/// - [`BBox::from_uv_zoom`]: Creates a new BBox from zoom-uv coordinates
77/// - [`BBox::from_st_zoom`]: Creates a new BBox from zoom-st coordinates
78#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
79pub struct BBox<T = f64> {
80    /// left most longitude (WM) or S (S2)
81    pub left: T,
82    /// bottom most latitude (WM) or T (S2)
83    pub bottom: T,
84    /// right most longitude (WM) or T (S2)
85    pub right: T,
86    /// top most latitude (WM) or S (S2)
87    pub top: T,
88}
89impl<T> From<BBox<T>> for MValue
90where
91    T: Into<ValueType>,
92{
93    fn from(bbox: BBox<T>) -> MValue {
94        MValue::from([
95            ("left".into(), bbox.left.into()),
96            ("bottom".into(), bbox.bottom.into()),
97            ("right".into(), bbox.right.into()),
98            ("top".into(), bbox.top.into()),
99        ])
100    }
101}
102impl<T> From<&BBox<T>> for MValue
103where
104    T: Copy + Into<ValueType>,
105{
106    fn from(bbox: &BBox<T>) -> MValue {
107        MValue::from([
108            ("left".into(), bbox.left.into()),
109            ("bottom".into(), bbox.bottom.into()),
110            ("right".into(), bbox.right.into()),
111            ("top".into(), bbox.top.into()),
112        ])
113    }
114}
115impl<T> From<MValue> for BBox<T>
116where
117    T: From<ValueType>,
118{
119    fn from(mvalue: MValue) -> Self {
120        BBox {
121            left: mvalue.get("left").unwrap().clone().into(),
122            bottom: mvalue.get("bottom").unwrap().clone().into(),
123            right: mvalue.get("right").unwrap().clone().into(),
124            top: mvalue.get("top").unwrap().clone().into(),
125        }
126    }
127}
128impl<T> From<&MValue> for BBox<T>
129where
130    T: From<ValueType>,
131{
132    fn from(mvalue: &MValue) -> Self {
133        BBox {
134            left: mvalue.get("left").unwrap().clone().into(),
135            bottom: mvalue.get("bottom").unwrap().clone().into(),
136            right: mvalue.get("right").unwrap().clone().into(),
137            top: mvalue.get("top").unwrap().clone().into(),
138        }
139    }
140}
141impl<T> MValueCompatible for BBox<T>
142where
143    ValueType: From<T>,
144    T: From<ValueType> + Default + Bounded + Copy + Interpolate,
145{
146}
147impl<T> Default for BBox<T>
148where
149    T: Default + Bounded + Copy,
150{
151    fn default() -> Self {
152        BBox::new(T::max_value(), T::max_value(), T::min_value(), T::min_value())
153    }
154}
155impl<T> BBox<T> {
156    /// Creates a new BBox
157    pub fn new(left: T, bottom: T, right: T, top: T) -> Self
158    where
159        T: Copy,
160    {
161        BBox { left, bottom, right, top }
162    }
163
164    /// Checks if a point is within the BBox
165    pub fn point_overlap<P: GetXY>(&self, point: &P) -> bool
166    where
167        T: Into<f64> + Copy, // Ensures that comparison operators work for type T
168    {
169        point.x() >= self.left.into()
170            && point.x() <= self.right.into()
171            && point.y() >= self.bottom.into()
172            && point.y() <= self.top.into()
173    }
174
175    /// Merges another bounding box with this one
176    pub fn merge(&self, other: &Self) -> Self
177    where
178        T: PartialOrd + Copy,
179    {
180        let mut new_bbox = *self;
181        new_bbox.left = if new_bbox.left < other.left { new_bbox.left } else { other.left };
182        new_bbox.bottom =
183            if new_bbox.bottom < other.bottom { new_bbox.bottom } else { other.bottom };
184        new_bbox.right = if new_bbox.right > other.right { new_bbox.right } else { other.right };
185        new_bbox.top = if new_bbox.top > other.top { new_bbox.top } else { other.top };
186
187        new_bbox
188    }
189
190    /// Merges in place another bounding box with this one
191    pub fn merge_in_place(&mut self, other: &Self)
192    where
193        T: PartialOrd + Copy,
194    {
195        self.left = if self.left < other.left { self.left } else { other.left };
196        self.bottom = if self.bottom < other.bottom { self.bottom } else { other.bottom };
197        self.right = if self.right > other.right { self.right } else { other.right };
198        self.top = if self.top > other.top { self.top } else { other.top };
199    }
200
201    /// Checks if another bounding box overlaps with this one and returns the overlap
202    pub fn overlap(&self, other: &BBox<T>) -> Option<BBox<T>>
203    where
204        T: PartialOrd + Copy,
205    {
206        if self.left > other.right
207            || self.right < other.left
208            || self.bottom > other.top
209            || self.top < other.bottom
210        {
211            None
212        } else {
213            let left = if self.left > other.left { self.left } else { other.left };
214            let bottom = if self.bottom > other.bottom { self.bottom } else { other.bottom };
215            let right = if self.right < other.right { self.right } else { other.right };
216            let top = if self.top < other.top { self.top } else { other.top };
217
218            Some(BBox { left, bottom, right, top })
219        }
220    }
221
222    /// Clips the bounding box along an axis
223    pub fn clip(&self, axis: Axis, k1: T, k2: T) -> BBox<T>
224    where
225        T: PartialOrd + Copy,
226    {
227        let mut new_bbox = *self;
228        if axis == Axis::X {
229            new_bbox.left = if new_bbox.left > k1 { new_bbox.left } else { k1 };
230            new_bbox.right = if new_bbox.right < k2 { new_bbox.right } else { k2 };
231        } else {
232            new_bbox.bottom = if new_bbox.bottom > k1 { new_bbox.bottom } else { k1 };
233            new_bbox.top = if new_bbox.top < k2 { new_bbox.top } else { k2 };
234        }
235
236        new_bbox
237    }
238
239    /// Checks if this bounding box is inside another
240    pub fn inside(&self, other: &BBox<T>) -> bool
241    where
242        T: PartialOrd + Copy,
243    {
244        self.left >= other.left
245            && self.right <= other.right
246            && self.bottom >= other.bottom
247            && self.top <= other.top
248    }
249
250    /// Returns the area of the bounding box
251    pub fn area(&self) -> T
252    where
253        T: Sub<Output = T> + Mul<Output = T> + Copy,
254    {
255        (self.right - self.left) * (self.top - self.bottom)
256    }
257}
258impl BBox<f64> {
259    /// Creates a new BBox from a point
260    pub fn from_point<P: GetXY>(point: &P) -> Self {
261        BBox::new(point.x(), point.y(), point.x(), point.y())
262    }
263
264    /// Creates a new BBox from a linestring
265    pub fn from_linestring<P: GetXY>(line: &[P]) -> Self {
266        let mut bbox = BBox::default();
267        for point in line {
268            bbox.extend_from_point(point);
269        }
270        bbox
271    }
272
273    /// Creates a new BBox from a multi-linestring
274    pub fn from_multi_linestring<P: GetXY>(lines: &[Vec<P>]) -> Self {
275        let mut bbox = BBox::default();
276        for line in lines {
277            for point in line {
278                bbox.extend_from_point(point);
279            }
280        }
281        bbox
282    }
283
284    /// Creates a new BBox from a polygon
285    pub fn from_polygon<P: GetXY>(polygon: &[Vec<P>]) -> Self {
286        Self::from_multi_linestring(polygon)
287    }
288
289    /// Creates a new BBox from a multi-polygon
290    pub fn from_multi_polygon<P: GetXY>(polygons: &[Vec<Vec<P>>]) -> Self {
291        let mut bbox = BBox::default();
292        for polygon in polygons {
293            for line in polygon {
294                for point in line {
295                    bbox.extend_from_point(point);
296                }
297            }
298        }
299        bbox
300    }
301
302    /// Extends the bounding box with a point
303    pub fn extend_from_point<P: GetXY>(&mut self, point: &P) {
304        self.merge_in_place(&BBox::from_point(point));
305    }
306
307    /// Creates a new BBox from zoom-uv coordinates
308    pub fn from_uv_zoom(u: f64, v: f64, zoom: u8) -> Self {
309        let division_factor = 2. / (1 << zoom) as f64;
310
311        BBox {
312            left: division_factor * u - 1.0,
313            bottom: division_factor * v - 1.0,
314            right: division_factor * (u + 1.0) - 1.0,
315            top: division_factor * (v + 1.0) - 1.0,
316        }
317    }
318
319    /// Creates a new BBox from zoom-st coordinates
320    pub fn from_st_zoom(s: f64, t: f64, zoom: u8) -> Self {
321        let division_factor = (2. / (1 << zoom) as f64) * 0.5;
322
323        BBox {
324            left: division_factor * s,
325            bottom: division_factor * t,
326            right: division_factor * (s + 1.),
327            top: division_factor * (t + 1.),
328        }
329    }
330}
331impl<T> Serialize for BBox<T>
332where
333    T: Serialize + Copy,
334{
335    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
336    where
337        S: Serializer,
338    {
339        let mut seq = serializer.serialize_tuple(4)?;
340        seq.serialize_element(&self.left)?;
341        seq.serialize_element(&self.bottom)?;
342        seq.serialize_element(&self.right)?;
343        seq.serialize_element(&self.top)?;
344        seq.end()
345    }
346}
347impl<T: Copy> From<BBox3D<T>> for BBox<T> {
348    fn from(bbox: BBox3D<T>) -> Self {
349        BBox::new(bbox.left, bbox.bottom, bbox.right, bbox.top)
350    }
351}
352impl<'de, T> Deserialize<'de> for BBox<T>
353where
354    T: Deserialize<'de> + Copy,
355{
356    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
357    where
358        D: Deserializer<'de>,
359    {
360        struct BBoxVisitor<T> {
361            marker: core::marker::PhantomData<T>,
362        }
363
364        impl<'de, T> Visitor<'de> for BBoxVisitor<T>
365        where
366            T: Deserialize<'de> + Copy,
367        {
368            type Value = BBox<T>;
369
370            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
371                formatter.write_str("a sequence of four numbers")
372            }
373
374            fn visit_seq<V>(self, mut seq: V) -> Result<BBox<T>, V::Error>
375            where
376                V: SeqAccess<'de>,
377            {
378                let left =
379                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?;
380                let bottom =
381                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?;
382                let right =
383                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;
384                let top = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?;
385                Ok(BBox { left, bottom, right, top })
386            }
387        }
388
389        deserializer.deserialize_tuple(4, BBoxVisitor { marker: core::marker::PhantomData })
390    }
391}
392
393/// # 3D Bounding Box
394///
395/// ## Description
396///
397/// A 3D Bounding Box ensures a min-max range of values and includes a near-far range
398///
399/// The order is (left, bottom, right, top, near, far) when storing as a flattened array.
400///
401/// If WM, then the projection is lon-lat. If S2, then the projection is s-t
402///
403/// Defaults to f64 as the base type.
404///
405/// Implements [`MValueCompatible`] as well as [`Interpolate`].
406///
407/// ## Usage
408///
409/// ## From/To
410/// - [`MValue`]
411/// - [`ValueType`]
412///
413/// ### Any Type BBox
414/// - [`BBox3D::new`]: Creates a new BBox
415/// - [`BBox3D::point_overlap`]: Checks if a point is within the BBox
416/// - [`BBox3D::merge`]: Merges another bounding box with this one
417/// - [`BBox3D::merge_in_place`]: Merges in place another bounding box with this one
418/// - [`BBox3D::overlap`]: Checks if another bounding box overlaps with this one
419/// - [`BBox3D::clip`]: Clips the bounding box along an axis
420/// - [`BBox3D::inside`]: Checks if this bounding box is inside another
421/// - [`BBox3D::from_bbox`]: Creates a new BBox3D from a BBox
422/// - [`BBox3D::area`]: Returns the area of the bounding box
423///
424/// ### `f64` Type BBox
425/// Note that all the input geometry uses the [`GetXY`] and [`GetZ`] traits.
426/// - [`BBox3D::from_point`]: Creates a new BBox from a point
427/// - [`BBox3D::from_linestring`]: Creates a new BBox from a linestring
428/// - [`BBox3D::from_multi_linestring`]: Creates a new BBox from a multi-linestring
429/// - [`BBox3D::from_polygon`]: Creates a new BBox from a polygon
430/// - [`BBox3D::from_multi_polygon`]: Creates a new BBox from a multi-polygon
431/// - [`BBox3D::extend_from_point`]: Extends the bounding box with a point
432/// - [`BBox3D::from_uv_zoom`]: Creates a new BBox from zoom-uv coordinates
433/// - [`BBox3D::from_st_zoom`]: Creates a new BBox from zoom-st coordinates
434#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
435pub struct BBox3D<T = f64> {
436    /// left most longitude (WM) or S (S2)
437    pub left: T,
438    /// bottom most latitude (WM) or T (S2)
439    pub bottom: T,
440    /// right most longitude (WM) or T (S2)
441    pub right: T,
442    /// top most latitude (WM) or S (S2)
443    pub top: T,
444    /// near most height (WM) or T (S2)
445    /// generic height is relative to the surface of the earth in meters
446    pub near: T,
447    /// far most height (WM) or T (S2)
448    /// generic height is relative to the surface of the earth in meters
449    pub far: T,
450}
451impl<T> From<BBox3D<T>> for MValue
452where
453    T: Into<ValueType>,
454{
455    fn from(bbox: BBox3D<T>) -> MValue {
456        MValue::from([
457            ("left".into(), bbox.left.into()),
458            ("bottom".into(), bbox.bottom.into()),
459            ("right".into(), bbox.right.into()),
460            ("top".into(), bbox.top.into()),
461            ("near".into(), bbox.near.into()),
462            ("far".into(), bbox.far.into()),
463        ])
464    }
465}
466impl<T> From<&BBox3D<T>> for MValue
467where
468    T: Copy + Into<ValueType>,
469{
470    fn from(bbox: &BBox3D<T>) -> MValue {
471        MValue::from([
472            ("left".into(), bbox.left.into()),
473            ("bottom".into(), bbox.bottom.into()),
474            ("right".into(), bbox.right.into()),
475            ("top".into(), bbox.top.into()),
476            ("near".into(), bbox.near.into()),
477            ("far".into(), bbox.far.into()),
478        ])
479    }
480}
481impl<T> From<MValue> for BBox3D<T>
482where
483    T: From<ValueType>,
484{
485    fn from(mvalue: MValue) -> Self {
486        BBox3D {
487            left: mvalue.get("left").unwrap().clone().into(),
488            bottom: mvalue.get("bottom").unwrap().clone().into(),
489            right: mvalue.get("right").unwrap().clone().into(),
490            top: mvalue.get("top").unwrap().clone().into(),
491            near: mvalue.get("near").unwrap().clone().into(),
492            far: mvalue.get("far").unwrap().clone().into(),
493        }
494    }
495}
496impl<T> From<&MValue> for BBox3D<T>
497where
498    T: From<ValueType>,
499{
500    fn from(mvalue: &MValue) -> Self {
501        BBox3D {
502            left: mvalue.get("left").unwrap().clone().into(),
503            bottom: mvalue.get("bottom").unwrap().clone().into(),
504            right: mvalue.get("right").unwrap().clone().into(),
505            top: mvalue.get("top").unwrap().clone().into(),
506            near: mvalue.get("near").unwrap().clone().into(),
507            far: mvalue.get("far").unwrap().clone().into(),
508        }
509    }
510}
511impl<T> MValueCompatible for BBox3D<T>
512where
513    ValueType: From<T>,
514    T: From<ValueType> + Default + Bounded + Copy + Interpolate,
515{
516}
517impl<T> BBox3D<T> {
518    /// Creates a new BBox3D
519    pub fn new(left: T, bottom: T, right: T, top: T, near: T, far: T) -> Self
520    where
521        T: Copy,
522    {
523        BBox3D { left, bottom, right, top, near, far }
524    }
525
526    /// Checks if a point is within the BBox
527    pub fn point_overlap<P: GetXY + GetZ>(&self, point: &P) -> bool
528    where
529        T: Into<f64> + Copy, // Ensures that comparison operators work for type T
530    {
531        let z = point.z().unwrap_or_default();
532        point.x() >= self.left.into()
533            && point.x() <= self.right.into()
534            && point.y() >= self.bottom.into()
535            && point.y() <= self.top.into()
536            && z >= self.near.into()
537            && z <= self.far.into()
538    }
539
540    /// Merges another bounding box with this one
541    pub fn merge(&self, other: &BBox3D<T>) -> BBox3D<T>
542    where
543        T: PartialOrd + Copy,
544    {
545        let mut new_bbox = *self;
546        new_bbox.left = if new_bbox.left < other.left { new_bbox.left } else { other.left };
547        new_bbox.bottom =
548            if new_bbox.bottom < other.bottom { new_bbox.bottom } else { other.bottom };
549        new_bbox.right = if new_bbox.right > other.right { new_bbox.right } else { other.right };
550        new_bbox.top = if new_bbox.top > other.top { new_bbox.top } else { other.top };
551        new_bbox.near = if new_bbox.near < other.near { new_bbox.near } else { other.near };
552        new_bbox.far = if new_bbox.far > other.far { new_bbox.far } else { other.far };
553
554        new_bbox
555    }
556
557    /// Merges in place another bounding box with this one
558    pub fn merge_in_place(&mut self, other: &Self)
559    where
560        T: PartialOrd + Copy,
561    {
562        self.left = if self.left < other.left { self.left } else { other.left };
563        self.bottom = if self.bottom < other.bottom { self.bottom } else { other.bottom };
564        self.right = if self.right > other.right { self.right } else { other.right };
565        self.top = if self.top > other.top { self.top } else { other.top };
566        self.near = if self.near < other.near { self.near } else { other.near };
567        self.far = if self.far > other.far { self.far } else { other.far };
568    }
569
570    /// Checks if another bounding box overlaps with this one and returns the overlap
571    pub fn overlap(&self, other: &BBox3D<T>) -> Option<BBox3D<T>>
572    where
573        T: PartialOrd + Copy,
574    {
575        if self.left > other.right
576            || self.right < other.left
577            || self.bottom > other.top
578            || self.top < other.bottom
579            || self.near > other.far
580            || self.far < other.near
581        {
582            None
583        } else {
584            let left = if self.left > other.left { self.left } else { other.left };
585            let bottom = if self.bottom > other.bottom { self.bottom } else { other.bottom };
586            let right = if self.right < other.right { self.right } else { other.right };
587            let top = if self.top < other.top { self.top } else { other.top };
588
589            let near = if self.near > other.near { self.near } else { other.near };
590            let far = if self.far < other.far { self.far } else { other.far };
591
592            Some(BBox3D { left, bottom, right, top, near, far })
593        }
594    }
595
596    /// Clips the bounding box along an axis
597    pub fn clip(&self, axis: Axis, k1: T, k2: T) -> BBox3D<T>
598    where
599        T: PartialOrd + Copy,
600    {
601        let mut new_bbox = *self;
602        if axis == Axis::X {
603            new_bbox.left = if new_bbox.left > k1 { new_bbox.left } else { k1 };
604            new_bbox.right = if new_bbox.right < k2 { new_bbox.right } else { k2 };
605        } else {
606            new_bbox.bottom = if new_bbox.bottom > k1 { new_bbox.bottom } else { k1 };
607            new_bbox.top = if new_bbox.top < k2 { new_bbox.top } else { k2 };
608        }
609
610        new_bbox
611    }
612
613    /// Creates a new BBox3D from a BBox
614    pub fn from_bbox(bbox: &BBox<T>) -> Self
615    where
616        T: Copy + Default,
617    {
618        BBox3D::new(bbox.left, bbox.bottom, bbox.right, bbox.top, T::default(), T::default())
619    }
620
621    /// Checks if this bounding box is inside another
622    pub fn inside(&self, other: &BBox3D<T>) -> bool
623    where
624        T: PartialOrd + Copy,
625    {
626        self.left >= other.left
627            && self.right <= other.right
628            && self.bottom >= other.bottom
629            && self.top <= other.top
630            && self.near >= other.near
631            && self.far <= other.far
632    }
633
634    /// Returns the area of the bounding box
635    pub fn area(&self) -> T
636    where
637        T: Sub<Output = T> + Mul<Output = T> + MulAssign + Into<f64> + Copy,
638    {
639        let mut res = (self.right - self.left) * (self.top - self.bottom);
640        if self.far.into() != 0. || self.near.into() != 0. {
641            res *= self.far - self.near
642        }
643
644        res
645    }
646}
647impl<T> Serialize for BBox3D<T>
648where
649    T: Serialize + Copy,
650{
651    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
652    where
653        S: Serializer,
654    {
655        let mut seq = serializer.serialize_tuple(6)?;
656        seq.serialize_element(&self.left)?;
657        seq.serialize_element(&self.bottom)?;
658        seq.serialize_element(&self.right)?;
659        seq.serialize_element(&self.top)?;
660        seq.serialize_element(&self.near)?;
661        seq.serialize_element(&self.far)?;
662        seq.end()
663    }
664}
665impl<T> Default for BBox3D<T>
666where
667    T: Default + Bounded + Copy,
668{
669    fn default() -> Self {
670        BBox3D::new(
671            T::max_value(),
672            T::max_value(),
673            T::min_value(),
674            T::min_value(),
675            T::max_value(),
676            T::min_value(),
677        )
678    }
679}
680impl BBox3D<f64> {
681    /// Creates a new BBox3D from a point
682    pub fn from_point<P: GetXY + GetZ>(point: &P) -> Self {
683        BBox3D::new(
684            point.x(),
685            point.y(),
686            point.x(),
687            point.y(),
688            point.z().unwrap_or(f64::MAX),
689            point.z().unwrap_or(f64::MIN),
690        )
691    }
692
693    /// Creates a new BBox from a linestring
694    pub fn from_linestring<P: GetXY + GetZ>(line: &[P]) -> Self {
695        let mut bbox = BBox3D::default();
696        for point in line {
697            bbox.extend_from_point(point);
698        }
699        bbox
700    }
701
702    /// Creates a new BBox from a multi-linestring
703    pub fn from_multi_linestring<P: GetXY + GetZ>(lines: &[Vec<P>]) -> Self {
704        let mut bbox = BBox3D::default();
705        for line in lines {
706            for point in line {
707                bbox.extend_from_point(point);
708            }
709        }
710        bbox
711    }
712
713    /// Creates a new BBox from a polygon
714    pub fn from_polygon<P: GetXY + GetZ>(polygon: &[Vec<P>]) -> Self {
715        Self::from_multi_linestring(polygon)
716    }
717
718    /// Creates a new BBox from a multi-polygon
719    pub fn from_multi_polygon<P: GetXY + GetZ>(polygons: &[Vec<Vec<P>>]) -> Self {
720        let mut bbox = BBox3D::default();
721        for polygon in polygons {
722            for line in polygon {
723                for point in line {
724                    bbox.extend_from_point(point);
725                }
726            }
727        }
728        bbox
729    }
730
731    /// Extends the bounding box with a point
732    pub fn extend_from_point<P: GetXY + GetZ>(&mut self, point: &P) {
733        self.merge_in_place(&BBox3D::from_point(point));
734    }
735
736    /// Creates a new BBox3D from zoom-uv coordinates
737    pub fn from_uv_zoom(u: f64, v: f64, zoom: u8) -> Self {
738        let division_factor = 2. / (1 << zoom) as f64;
739
740        BBox3D {
741            left: division_factor * u - 1.0,
742            bottom: division_factor * v - 1.0,
743            right: division_factor * (u + 1.0) - 1.0,
744            top: division_factor * (v + 1.0) - 1.0,
745            near: f64::MAX,
746            far: f64::MIN,
747        }
748    }
749
750    /// Creates a new BBox from zoom-st coordinates
751    pub fn from_st_zoom(s: f64, t: f64, zoom: u8) -> Self {
752        let division_factor = (2. / (1 << zoom) as f64) * 0.5;
753
754        BBox3D {
755            left: division_factor * s,
756            bottom: division_factor * t,
757            right: division_factor * (s + 1.),
758            top: division_factor * (t + 1.),
759            near: f64::MAX,
760            far: f64::MIN,
761        }
762    }
763}
764impl<T: Default + Copy> From<BBox<T>> for BBox3D<T> {
765    fn from(bbox: BBox<T>) -> Self {
766        BBox3D::from_bbox(&bbox)
767    }
768}
769impl<'de, T> Deserialize<'de> for BBox3D<T>
770where
771    T: Deserialize<'de> + Copy,
772{
773    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
774    where
775        D: Deserializer<'de>,
776    {
777        struct BBox3DVisitor<T> {
778            marker: core::marker::PhantomData<T>,
779        }
780
781        impl<'de, T> Visitor<'de> for BBox3DVisitor<T>
782        where
783            T: Deserialize<'de> + Copy,
784        {
785            type Value = BBox3D<T>;
786
787            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
788                formatter.write_str("a sequence of six numbers")
789            }
790
791            fn visit_seq<V>(self, mut seq: V) -> Result<BBox3D<T>, V::Error>
792            where
793                V: SeqAccess<'de>,
794            {
795                let left =
796                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?;
797                let bottom =
798                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?;
799                let right =
800                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;
801                let top = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?;
802                let near =
803                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?;
804                let far = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?;
805                Ok(BBox3D { left, bottom, right, top, near, far })
806            }
807        }
808
809        deserializer.deserialize_tuple(6, BBox3DVisitor { marker: core::marker::PhantomData })
810    }
811}
812
813/// BBox or BBox3D
814#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
815pub enum BBOX {
816    /// 2D bounding box
817    BBox(BBox),
818    /// 3D bounding box
819    BBox3D(BBox3D),
820}
821impl Default for BBOX {
822    fn default() -> Self {
823        BBOX::BBox(BBox::default())
824    }
825}
826impl From<BBox> for BBOX {
827    fn from(bbox: BBox) -> Self {
828        BBOX::BBox(bbox)
829    }
830}
831impl From<BBox3D> for BBOX {
832    fn from(bbox: BBox3D) -> Self {
833        BBOX::BBox3D(bbox)
834    }
835}
836impl Eq for BBOX {}
837impl PartialOrd for BBOX {
838    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
839        Some(self.cmp(other))
840    }
841}
842impl Ord for BBOX {
843    fn cmp(&self, other: &Self) -> Ordering {
844        match (self, other) {
845            (BBOX::BBox(a), BBOX::BBox(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
846            (BBOX::BBox3D(a), BBOX::BBox3D(b)) => a.partial_cmp(b).unwrap_or(Ordering::Equal),
847            // Ensure that BBox and BBox3D are ordered correctly
848            (BBOX::BBox(_), BBOX::BBox3D(_)) => Ordering::Less,
849            (BBOX::BBox3D(_), BBOX::BBox(_)) => Ordering::Greater,
850        }
851    }
852}