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