Skip to main content

ogcapi_types/common/
bbox.rs

1use std::{fmt, str};
2
3use serde::{Deserialize, Serialize};
4
5type Bbox2D = [f64; 4];
6type Bbox3D = [f64; 6];
7
8#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
9#[serde(untagged)]
10pub enum Bbox {
11    Bbox2D(Bbox2D),
12    Bbox3D(Bbox3D),
13}
14
15impl fmt::Display for Bbox {
16    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17        match self {
18            Bbox::Bbox2D(bbox) => write!(f, "{},{},{},{}", bbox[0], bbox[1], bbox[2], bbox[3]),
19            Bbox::Bbox3D(bbox) => {
20                write!(
21                    f,
22                    "{},{},{},{},{},{}",
23                    bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5]
24                )
25            }
26        }
27    }
28}
29
30impl From<[f64; 4]> for Bbox {
31    fn from(slice: [f64; 4]) -> Self {
32        Bbox::Bbox2D(slice)
33    }
34}
35
36impl From<[f64; 6]> for Bbox {
37    fn from(slice: [f64; 6]) -> Self {
38        Bbox::Bbox3D(slice)
39    }
40}
41
42impl str::FromStr for Bbox {
43    type Err = &'static str;
44
45    fn from_str(s: &str) -> Result<Self, Self::Err> {
46        let numbers: Vec<f64> = s
47            .split(',')
48            .map(|d| d.trim().parse::<f64>())
49            .collect::<Result<Vec<f64>, std::num::ParseFloatError>>()
50            .map_err(|_| "Unable to convert bbox coordinates to float")?;
51
52        match numbers.len() {
53            4 => Ok(Bbox::Bbox2D([
54                numbers[0], numbers[1], numbers[2], numbers[3],
55            ])),
56            6 => Ok(Bbox::Bbox3D([
57                numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], numbers[5],
58            ])),
59            _ => Err("Expected 4 or 6 numbers"),
60        }
61    }
62}
63
64impl TryFrom<&[f64]> for Bbox {
65    type Error = &'static str;
66
67    fn try_from(value: &[f64]) -> Result<Self, Self::Error> {
68        match value.len() {
69            4 => Ok(Bbox::Bbox2D([value[0], value[1], value[2], value[3]])),
70            6 => Ok(Bbox::Bbox3D([
71                value[0], value[1], value[2], value[3], value[4], value[5],
72            ])),
73            _ => Err("Bbox can only be of lenth 4 or 6!"),
74        }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use std::str::FromStr;
81
82    use super::*;
83
84    #[test]
85    fn from() {
86        let numbers = [160.6, -55.95, -170.0, -25.89];
87        let _bbox: Bbox = numbers.into();
88    }
89
90    #[test]
91    fn try_from() {
92        let numbers = &[160.6, -55.95, -170.0, -25.89];
93        let _bbox: Bbox = numbers.as_slice().try_into().unwrap();
94    }
95
96    #[test]
97    fn from_str() {
98        let s = "160.6,-55.95, -170, -25.89";
99        let _bbox: Bbox = Bbox::from_str(s).unwrap();
100    }
101
102    #[test]
103    fn serde_json() {
104        let s = "[ 160.6, -55.95, -170, -25.89 ]";
105        let bbox: Bbox = serde_json::from_str(s).unwrap();
106
107        match bbox {
108            Bbox::Bbox2D { .. } => {}
109            Bbox::Bbox3D { .. } => panic!("expected bbox to be 2 dimensional"),
110        }
111        assert_eq!(
112            "[160.6,-55.95,-170.0,-25.89]",
113            serde_json::to_string(&bbox).unwrap()
114        );
115    }
116}