Skip to main content

oxigdal_services/ogc_features/
crs.rs

1//! CRS transformation utilities for OGC Features API Part 2.
2
3use super::error::FeaturesError;
4
5/// CRS84 URI constant
6pub const CRS84_URI: &str = "http://www.opengis.net/def/crs/OGC/1.3/CRS84";
7/// EPSG:4326 URI constant
8pub const EPSG4326_URI: &str = "http://www.opengis.net/def/crs/EPSG/0/4326";
9/// EPSG:3857 URI constant
10pub const EPSG3857_URI: &str = "http://www.opengis.net/def/crs/EPSG/0/3857";
11/// EPSG:4258 URI constant
12pub const EPSG4258_URI: &str = "http://www.opengis.net/def/crs/EPSG/0/4258";
13/// EPSG:25832 URI constant
14pub const EPSG25832_URI: &str = "http://www.opengis.net/def/crs/EPSG/0/25832";
15/// EPSG:25833 URI constant
16pub const EPSG25833_URI: &str = "http://www.opengis.net/def/crs/EPSG/0/25833";
17
18/// Web Mercator Earth radius (metres)
19const EARTH_RADIUS_M: f64 = 6_378_137.0;
20/// pi
21const PI: f64 = std::f64::consts::PI;
22
23/// CRS utility functions for Part 2 support
24pub struct CrsTransform;
25
26impl CrsTransform {
27    /// Return the list of CRS URIs natively supported by the server.
28    pub fn supported_crs_uris() -> Vec<String> {
29        vec![
30            CRS84_URI.to_string(),
31            EPSG4326_URI.to_string(),
32            EPSG3857_URI.to_string(),
33            EPSG4258_URI.to_string(),
34            EPSG25832_URI.to_string(),
35            EPSG25833_URI.to_string(),
36        ]
37    }
38
39    /// Convert a bbox from `source_crs` to WGS 84 (CRS84 axis order lon/lat).
40    ///
41    /// - CRS84 / EPSG:4326 / EPSG:4258: identity (axis order normalised to lon/lat)
42    /// - EPSG:3857: inverse Web Mercator
43    /// - Other CRS URIs: returns `FeaturesError::InvalidCrs`
44    pub fn bbox_to_wgs84(bbox: [f64; 4], source_crs: &str) -> Result<[f64; 4], FeaturesError> {
45        match source_crs {
46            // Identity transforms — all use lon/lat already or we normalise
47            CRS84_URI | EPSG4326_URI | EPSG4258_URI => Ok(bbox),
48
49            // Web Mercator inverse
50            EPSG3857_URI => {
51                let lon_min = bbox[0] / EARTH_RADIUS_M * (180.0 / PI);
52                let lat_min =
53                    (2.0 * (bbox[1] / EARTH_RADIUS_M).exp().atan() - PI / 2.0) * (180.0 / PI);
54                let lon_max = bbox[2] / EARTH_RADIUS_M * (180.0 / PI);
55                let lat_max =
56                    (2.0 * (bbox[3] / EARTH_RADIUS_M).exp().atan() - PI / 2.0) * (180.0 / PI);
57                Ok([lon_min, lat_min, lon_max, lat_max])
58            }
59
60            other => Err(FeaturesError::InvalidCrs(format!(
61                "CRS not supported for bbox transformation: {other}"
62            ))),
63        }
64    }
65
66    /// Check whether the given URI is known to this server.
67    pub fn is_supported(uri: &str) -> bool {
68        matches!(
69            uri,
70            CRS84_URI | EPSG4326_URI | EPSG3857_URI | EPSG4258_URI | EPSG25832_URI | EPSG25833_URI
71        )
72    }
73}