s2json_core/geometry/
bbox.rs

1use crate::*;
2use alloc::fmt;
3use serde::{
4    de::{self, SeqAccess, Visitor},
5    ser::SerializeTuple,
6    Deserialize, Deserializer, Serialize, Serializer,
7};
8
9/// A BBOX is defined in lon-lat space and helps with zooming motion to
10/// see the entire line or polygon
11/// The order is (left, bottom, right, top)
12/// If WM, then the projection is lon-lat
13/// If S2, then the projection is s-t
14#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
15pub struct BBox<T = f64> {
16    /// left most longitude (WM) or S (S2)
17    pub left: T,
18    /// bottom most latitude (WM) or T (S2)
19    pub bottom: T,
20    /// right most longitude (WM) or T (S2)
21    pub right: T,
22    /// top most latitude (WM) or S (S2)
23    pub top: T,
24}
25impl<T> BBox<T> {
26    /// Creates a new BBox
27    pub fn new(left: T, bottom: T, right: T, top: T) -> Self
28    where
29        T: Copy,
30    {
31        BBox { left, bottom, right, top }
32    }
33
34    /// Checks if a point is within the BBox
35    pub fn point_overlap<M: MValueCompatible>(&self, point: VectorPoint<M>) -> bool
36    where
37        T: Into<f64> + Copy, // Ensures that comparison operators work for type T
38    {
39        point.x >= self.left.into()
40            && point.x <= self.right.into()
41            && point.y >= self.bottom.into()
42            && point.y <= self.top.into()
43    }
44
45    /// Merges another bounding box with this one
46    pub fn merge(&self, other: &Self) -> Self
47    where
48        T: PartialOrd + Copy,
49    {
50        let mut new_bbox = *self;
51        new_bbox.left = if new_bbox.left < other.left { new_bbox.left } else { other.left };
52        new_bbox.bottom =
53            if new_bbox.bottom < other.bottom { new_bbox.bottom } else { other.bottom };
54        new_bbox.right = if new_bbox.right > other.right { new_bbox.right } else { other.right };
55        new_bbox.top = if new_bbox.top > other.top { new_bbox.top } else { other.top };
56
57        new_bbox
58    }
59
60    /// Merges in place another bounding box with this one
61    pub fn merge_in_place(&mut self, other: &Self)
62    where
63        T: PartialOrd + Copy,
64    {
65        self.left = if self.left < other.left { self.left } else { other.left };
66        self.bottom = if self.bottom < other.bottom { self.bottom } else { other.bottom };
67        self.right = if self.right > other.right { self.right } else { other.right };
68        self.top = if self.top > other.top { self.top } else { other.top };
69    }
70
71    /// Checks if another bounding box overlaps with this one and returns the overlap
72    pub fn overlap(&self, other: &BBox<T>) -> Option<BBox<T>>
73    where
74        T: PartialOrd + Copy,
75    {
76        if self.left > other.right
77            || self.right < other.left
78            || self.bottom > other.top
79            || self.top < other.bottom
80        {
81            None
82        } else {
83            let left = if self.left > other.left { self.left } else { other.left };
84            let bottom = if self.bottom > other.bottom { self.bottom } else { other.bottom };
85            let right = if self.right < other.right { self.right } else { other.right };
86            let top = if self.top < other.top { self.top } else { other.top };
87
88            Some(BBox { left, bottom, right, top })
89        }
90    }
91
92    /// Clips the bounding box along an axis
93    pub fn clip(&self, axis: Axis, k1: T, k2: T) -> BBox<T>
94    where
95        T: PartialOrd + Copy,
96    {
97        let mut new_bbox = *self;
98        if axis == Axis::X {
99            new_bbox.left = if new_bbox.left > k1 { new_bbox.left } else { k1 };
100            new_bbox.right = if new_bbox.right < k2 { new_bbox.right } else { k2 };
101        } else {
102            new_bbox.bottom = if new_bbox.bottom > k1 { new_bbox.bottom } else { k1 };
103            new_bbox.top = if new_bbox.top < k2 { new_bbox.top } else { k2 };
104        }
105
106        new_bbox
107    }
108}
109impl Default for BBox<f64> {
110    fn default() -> Self {
111        BBox::new(f64::INFINITY, f64::INFINITY, -f64::INFINITY, -f64::INFINITY)
112    }
113}
114impl BBox<f64> {
115    /// Creates a new BBox from a point
116    pub fn from_point<M: MValueCompatible>(point: &VectorPoint<M>) -> Self {
117        BBox::new(point.x, point.y, point.x, point.y)
118    }
119
120    /// Creates a new BBox from a linestring
121    pub fn from_linestring<M: MValueCompatible>(line: &VectorLineString<M>) -> Self {
122        let mut bbox = BBox::from_point(&line[0]);
123        for point in line {
124            bbox.extend_from_point(point);
125        }
126        bbox
127    }
128
129    /// Creates a new BBox from a multi-linestring
130    pub fn from_multi_linestring<M: MValueCompatible>(lines: &VectorMultiLineString<M>) -> Self {
131        let mut bbox = BBox::from_point(&lines[0][0]);
132        for line in lines {
133            for point in line {
134                bbox.extend_from_point(point);
135            }
136        }
137        bbox
138    }
139
140    /// Creates a new BBox from a polygon
141    pub fn from_polygon<M: MValueCompatible>(polygon: &VectorPolygon<M>) -> Self {
142        BBox::<f64>::from_multi_linestring(polygon)
143    }
144
145    /// Creates a new BBox from a multi-polygon
146    pub fn from_multi_polygon<M: MValueCompatible>(polygons: &VectorMultiPolygon<M>) -> Self {
147        let mut bbox = BBox::from_point(&polygons[0][0][0]);
148        for polygon in polygons {
149            for line in polygon {
150                for point in line {
151                    bbox.extend_from_point(point);
152                }
153            }
154        }
155        bbox
156    }
157
158    /// Extends the bounding box with a point
159    pub fn extend_from_point<M: MValueCompatible>(&mut self, point: &VectorPoint<M>) {
160        self.merge_in_place(&BBox::from_point(point));
161    }
162
163    /// Creates a new BBox from zoom-uv coordinates
164    pub fn from_uv_zoom(u: f64, v: f64, zoom: u8) -> Self {
165        let division_factor = 2. / (1 << zoom) as f64;
166
167        BBox {
168            left: division_factor * u - 1.0,
169            bottom: division_factor * v - 1.0,
170            right: division_factor * (u + 1.0) - 1.0,
171            top: division_factor * (v + 1.0) - 1.0,
172        }
173    }
174
175    /// Creates a new BBox from zoom-st coordinates
176    pub fn from_st_zoom(s: f64, t: f64, zoom: u8) -> Self {
177        let division_factor = (2. / (1 << zoom) as f64) * 0.5;
178
179        BBox {
180            left: division_factor * s,
181            bottom: division_factor * t,
182            right: division_factor * (s + 1.),
183            top: division_factor * (t + 1.),
184        }
185    }
186}
187impl<T> Serialize for BBox<T>
188where
189    T: Serialize + Copy,
190{
191    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
192    where
193        S: Serializer,
194    {
195        let mut seq = serializer.serialize_tuple(4)?;
196        seq.serialize_element(&self.left)?;
197        seq.serialize_element(&self.bottom)?;
198        seq.serialize_element(&self.right)?;
199        seq.serialize_element(&self.top)?;
200        seq.end()
201    }
202}
203impl<'de, T> Deserialize<'de> for BBox<T>
204where
205    T: Deserialize<'de> + Copy,
206{
207    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
208    where
209        D: Deserializer<'de>,
210    {
211        struct BBoxVisitor<T> {
212            marker: core::marker::PhantomData<T>,
213        }
214
215        impl<'de, T> Visitor<'de> for BBoxVisitor<T>
216        where
217            T: Deserialize<'de> + Copy,
218        {
219            type Value = BBox<T>;
220
221            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
222                formatter.write_str("a sequence of four numbers")
223            }
224
225            fn visit_seq<V>(self, mut seq: V) -> Result<BBox<T>, V::Error>
226            where
227                V: SeqAccess<'de>,
228            {
229                let left =
230                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?;
231                let bottom =
232                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?;
233                let right =
234                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;
235                let top = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?;
236                Ok(BBox { left, bottom, right, top })
237            }
238        }
239
240        deserializer.deserialize_tuple(4, BBoxVisitor { marker: core::marker::PhantomData })
241    }
242}
243
244/// A BBOX is defined in lon-lat space and helps with zooming motion to
245/// see the entire 3D line or polygon
246#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
247pub struct BBox3D<T = f64> {
248    /// left most longitude (WM) or S (S2)
249    pub left: T,
250    /// bottom most latitude (WM) or T (S2)
251    pub bottom: T,
252    /// right most longitude (WM) or T (S2)
253    pub right: T,
254    /// top most latitude (WM) or S (S2)
255    pub top: T,
256    /// near most height (WM) or T (S2)
257    /// generic height is relative to the surface of the earth in meters
258    pub near: T,
259    /// far most height (WM) or T (S2)
260    /// generic height is relative to the surface of the earth in meters
261    pub far: T,
262}
263impl<T> BBox3D<T> {
264    /// Creates a new BBox3D
265    pub fn new(left: T, bottom: T, right: T, top: T, near: T, far: T) -> Self
266    where
267        T: Copy,
268    {
269        BBox3D { left, bottom, right, top, near, far }
270    }
271
272    /// Checks if a point is within the BBox
273    pub fn point_overlap<M: MValueCompatible>(&self, point: VectorPoint<M>) -> bool
274    where
275        T: Into<f64> + Copy, // Ensures that comparison operators work for type T
276    {
277        let z = point.z.unwrap_or_default();
278        point.x >= self.left.into()
279            && point.x <= self.right.into()
280            && point.y >= self.bottom.into()
281            && point.y <= self.top.into()
282            && z >= self.near.into()
283            && z <= self.far.into()
284    }
285
286    /// Merges another bounding box with this one
287    pub fn merge(&self, other: &BBox3D<T>) -> BBox3D<T>
288    where
289        T: PartialOrd + Copy,
290    {
291        let mut new_bbox = *self;
292        new_bbox.left = if new_bbox.left < other.left { new_bbox.left } else { other.left };
293        new_bbox.bottom =
294            if new_bbox.bottom < other.bottom { new_bbox.bottom } else { other.bottom };
295        new_bbox.right = if new_bbox.right > other.right { new_bbox.right } else { other.right };
296        new_bbox.top = if new_bbox.top > other.top { new_bbox.top } else { other.top };
297        new_bbox.near = if new_bbox.near < other.near { new_bbox.near } else { other.near };
298        new_bbox.far = if new_bbox.far > other.far { new_bbox.far } else { other.far };
299
300        new_bbox
301    }
302
303    /// Merges in place another bounding box with this one
304    pub fn merge_in_place(&mut self, other: &Self)
305    where
306        T: PartialOrd + Copy,
307    {
308        self.left = if self.left < other.left { self.left } else { other.left };
309        self.bottom = if self.bottom < other.bottom { self.bottom } else { other.bottom };
310        self.right = if self.right > other.right { self.right } else { other.right };
311        self.top = if self.top > other.top { self.top } else { other.top };
312        self.near = if self.near < other.near { self.near } else { other.near };
313        self.far = if self.far > other.far { self.far } else { other.far };
314    }
315
316    /// Checks if another bounding box overlaps with this one and returns the overlap
317    pub fn overlap(&self, other: &BBox3D<T>) -> Option<BBox3D<T>>
318    where
319        T: PartialOrd + Copy,
320    {
321        if self.left > other.right
322            || self.right < other.left
323            || self.bottom > other.top
324            || self.top < other.bottom
325            || self.near > other.far
326            || self.far < other.near
327        {
328            None
329        } else {
330            let left = if self.left > other.left { self.left } else { other.left };
331            let bottom = if self.bottom > other.bottom { self.bottom } else { other.bottom };
332            let right = if self.right < other.right { self.right } else { other.right };
333            let top = if self.top < other.top { self.top } else { other.top };
334
335            let near = if self.near > other.near { self.near } else { other.near };
336            let far = if self.far < other.far { self.far } else { other.far };
337
338            Some(BBox3D { left, bottom, right, top, near, far })
339        }
340    }
341
342    /// Clips the bounding box along an axis
343    pub fn clip(&self, axis: Axis, k1: T, k2: T) -> BBox3D<T>
344    where
345        T: PartialOrd + Copy,
346    {
347        let mut new_bbox = *self;
348        if axis == Axis::X {
349            new_bbox.left = if new_bbox.left > k1 { new_bbox.left } else { k1 };
350            new_bbox.right = if new_bbox.right < k2 { new_bbox.right } else { k2 };
351        } else {
352            new_bbox.bottom = if new_bbox.bottom > k1 { new_bbox.bottom } else { k1 };
353            new_bbox.top = if new_bbox.top < k2 { new_bbox.top } else { k2 };
354        }
355
356        new_bbox
357    }
358}
359impl<T> Serialize for BBox3D<T>
360where
361    T: Serialize + Copy,
362{
363    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
364    where
365        S: Serializer,
366    {
367        let mut seq = serializer.serialize_tuple(6)?;
368        seq.serialize_element(&self.left)?;
369        seq.serialize_element(&self.bottom)?;
370        seq.serialize_element(&self.right)?;
371        seq.serialize_element(&self.top)?;
372        seq.serialize_element(&self.near)?;
373        seq.serialize_element(&self.far)?;
374        seq.end()
375    }
376}
377impl Default for BBox3D<f64> {
378    fn default() -> Self {
379        BBox3D::new(
380            f64::INFINITY,
381            f64::INFINITY,
382            -f64::INFINITY,
383            -f64::INFINITY,
384            f64::INFINITY,
385            -f64::INFINITY,
386        )
387    }
388}
389impl BBox3D<f64> {
390    /// Creates a new BBox3D from a point
391    pub fn from_point<M: MValueCompatible>(point: &VectorPoint<M>) -> Self {
392        BBox3D::new(
393            point.x,
394            point.y,
395            point.x,
396            point.y,
397            point.z.unwrap_or(f64::INFINITY),
398            point.z.unwrap_or(-f64::INFINITY),
399        )
400    }
401
402    /// Creates a new BBox from a linestring
403    pub fn from_linestring<M: MValueCompatible>(line: &VectorLineString<M>) -> Self {
404        let mut bbox = BBox3D::from_point(&line[0]);
405        for point in line {
406            bbox.extend_from_point(point);
407        }
408        bbox
409    }
410
411    /// Creates a new BBox from a multi-linestring
412    pub fn from_multi_linestring<M: MValueCompatible>(lines: &VectorMultiLineString<M>) -> Self {
413        let mut bbox = BBox3D::from_point(&lines[0][0]);
414        for line in lines {
415            for point in line {
416                bbox.extend_from_point(point);
417            }
418        }
419        bbox
420    }
421
422    /// Creates a new BBox from a polygon
423    pub fn from_polygon<M: MValueCompatible>(polygon: &VectorPolygon<M>) -> Self {
424        BBox3D::<f64>::from_multi_linestring(polygon)
425    }
426
427    /// Creates a new BBox from a multi-polygon
428    pub fn from_multi_polygon<M: MValueCompatible>(polygons: &VectorMultiPolygon<M>) -> Self {
429        let mut bbox = BBox3D::from_point(&polygons[0][0][0]);
430        for polygon in polygons {
431            for line in polygon {
432                for point in line {
433                    bbox.extend_from_point(point);
434                }
435            }
436        }
437        bbox
438    }
439
440    /// Creates a new BBox3D from a BBox
441    pub fn from_bbox(bbox: &BBox) -> Self {
442        BBox3D::new(bbox.left, bbox.bottom, bbox.right, bbox.top, 0., 0.)
443    }
444
445    /// Extends the bounding box with a point
446    pub fn extend_from_point<M: MValueCompatible>(&mut self, point: &VectorPoint<M>) {
447        self.merge_in_place(&BBox3D::from_point(point));
448    }
449
450    /// Creates a new BBox3D from zoom-uv coordinates
451    pub fn from_uv_zoom(u: f64, v: f64, zoom: u8) -> Self {
452        let division_factor = 2. / (1 << zoom) as f64;
453
454        BBox3D {
455            left: division_factor * u - 1.0,
456            bottom: division_factor * v - 1.0,
457            right: division_factor * (u + 1.0) - 1.0,
458            top: division_factor * (v + 1.0) - 1.0,
459            near: f64::INFINITY,
460            far: -f64::INFINITY,
461        }
462    }
463
464    /// Creates a new BBox from zoom-st coordinates
465    pub fn from_st_zoom(s: f64, t: f64, zoom: u8) -> Self {
466        let division_factor = (2. / (1 << zoom) as f64) * 0.5;
467
468        BBox3D {
469            left: division_factor * s,
470            bottom: division_factor * t,
471            right: division_factor * (s + 1.),
472            top: division_factor * (t + 1.),
473            near: f64::INFINITY,
474            far: -f64::INFINITY,
475        }
476    }
477}
478impl From<BBox> for BBox3D<f64> {
479    fn from(bbox: BBox) -> Self {
480        BBox3D::from_bbox(&bbox)
481    }
482}
483impl<'de, T> Deserialize<'de> for BBox3D<T>
484where
485    T: Deserialize<'de> + Copy,
486{
487    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
488    where
489        D: Deserializer<'de>,
490    {
491        struct BBox3DVisitor<T> {
492            marker: core::marker::PhantomData<T>,
493        }
494
495        impl<'de, T> Visitor<'de> for BBox3DVisitor<T>
496        where
497            T: Deserialize<'de> + Copy,
498        {
499            type Value = BBox3D<T>;
500
501            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
502                formatter.write_str("a sequence of six numbers")
503            }
504
505            fn visit_seq<V>(self, mut seq: V) -> Result<BBox3D<T>, V::Error>
506            where
507                V: SeqAccess<'de>,
508            {
509                let left =
510                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(0, &self))?;
511                let bottom =
512                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(1, &self))?;
513                let right =
514                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(2, &self))?;
515                let top = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(3, &self))?;
516                let near =
517                    seq.next_element()?.ok_or_else(|| de::Error::invalid_length(4, &self))?;
518                let far = seq.next_element()?.ok_or_else(|| de::Error::invalid_length(5, &self))?;
519                Ok(BBox3D { left, bottom, right, top, near, far })
520            }
521        }
522
523        deserializer.deserialize_tuple(6, BBox3DVisitor { marker: core::marker::PhantomData })
524    }
525}
526
527/// BBox or BBox3D
528#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
529pub enum BBOX {
530    /// 2D bounding box
531    BBox(BBox),
532    /// 3D bounding box
533    BBox3D(BBox3D),
534}
535impl Default for BBOX {
536    fn default() -> Self {
537        BBOX::BBox(BBox::default())
538    }
539}