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