ogcapi_types/common/
crs.rs1use std::{fmt, str};
2
3use serde::{Deserialize, Serialize};
4
5pub const OGC_CRS84: &str = "http://www.opengis.net/def/crs/OGC/1.3/CRS84";
7
8pub const OGC_CRS84H: &str = "http://www.opengis.net/def/crs/OGC/0/CRS84h";
10
11#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)]
13pub struct Crs {
14 pub authority: Authority,
15 pub version: String,
16 pub code: String,
17}
18
19impl Crs {
20 pub fn new(authority: Authority, version: impl ToString, code: impl ToString) -> Crs {
21 Crs {
22 authority,
23 version: version.to_string(),
24 code: code.to_string(),
25 }
26 }
27
28 pub fn from_epsg(code: i32) -> Self {
29 Crs::new(Authority::EPSG, "0", code)
30 }
31
32 pub fn from_srid(code: i32) -> Self {
33 if code == 4326 {
34 Crs::default()
35 } else {
36 Crs::new(Authority::EPSG, "0", code)
37 }
38 }
39
40 pub fn to_urn(&self) -> String {
41 format!(
42 "urn:ogc:def:crs:{authority}:{version}:{code}",
43 authority = self.authority,
44 version = self.version,
45 code = self.code
46 )
47 }
48
49 pub fn to_epsg(&self) -> Option<Crs> {
50 match self.authority {
51 Authority::OGC => match self.code.as_str() {
52 "CRS84h" => Some(Crs::new(Authority::EPSG, "0", "4979")),
53 _ => None,
54 },
55 Authority::EPSG => Some(self.to_owned()),
56 }
57 }
58
59 pub fn as_epsg(&self) -> Option<i32> {
60 match self.authority {
61 Authority::OGC => match self.code.as_str() {
62 "CRS84h" => Some(4979),
63 _ => panic!("Unable to extract epsg code from `{}`", self),
64 },
65 Authority::EPSG => self.code.parse().ok(),
66 }
67 }
68
69 pub fn as_srid(&self) -> i32 {
70 match self.authority {
71 Authority::OGC => match self.code.as_str() {
72 "CRS84" => 4326,
73 "CRS84h" => 4979,
74 _ => panic!("Unable to extract epsg code from `{}`", self),
75 },
76 Authority::EPSG => self.code.parse().unwrap(),
77 }
78 }
79
80 pub fn as_known_crs(&self) -> String {
82 format!("{}:{}", self.authority, self.code)
83 }
84}
85
86impl fmt::Display for Crs {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 write!(
89 f,
90 "http://www.opengis.net/def/crs/{authority}/{version}/{code}",
91 authority = self.authority,
92 version = self.version,
93 code = self.code
94 )
95 }
96}
97
98impl str::FromStr for Crs {
99 type Err = String;
100
101 fn from_str(s: &str) -> Result<Self, Self::Err> {
102 let parts: Vec<&str> = if s.starts_with("urn") {
103 s.trim_start_matches("urn:ogc:def:crs:")
104 .split(':')
105 .collect()
106 } else {
107 s.trim_start_matches("http://www.opengis.net/def/crs/")
108 .split('/')
109 .collect()
110 };
111 match parts.len() {
112 3 => Ok(Crs::new(Authority::from_str(parts[0])?, parts[1], parts[2])),
113 _ => Err(format!("Unable to parse CRS from `{s}`!")),
114 }
115 }
116}
117
118impl Default for Crs {
119 fn default() -> Crs {
120 Crs {
121 authority: Authority::OGC,
122 version: "1.3".to_string(),
123 code: "CRS84".to_string(),
124 }
125 }
126}
127
128#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash)]
130pub enum Authority {
131 OGC,
132 EPSG,
133}
134
135impl fmt::Display for Authority {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 match self {
138 Authority::OGC => write!(f, "OGC"),
139 Authority::EPSG => write!(f, "EPSG"),
140 }
141 }
142}
143
144impl str::FromStr for Authority {
145 type Err = &'static str;
146
147 fn from_str(s: &str) -> Result<Self, Self::Err> {
148 match s {
149 "OGC" => Ok(Authority::OGC),
150 "EPSG" => Ok(Authority::EPSG),
151 _ => Err("Unknown crs authority!"),
152 }
153 }
154}
155
156#[cfg(test)]
157mod tests {
158 use std::str::FromStr;
159
160 use crate::common::{Crs, OGC_CRS84};
161
162 #[test]
163 fn parse_crs() {
164 let crs = Crs::from_str(OGC_CRS84).unwrap();
165 assert_eq!(format!("{:#}", crs), OGC_CRS84)
166 }
167
168 #[test]
169 fn from_epsg() {
170 let code = 4979;
171 let crs = Crs::from_epsg(code);
172 assert_eq!(
173 crs.to_string(),
174 "http://www.opengis.net/def/crs/EPSG/0/4979".to_string()
175 );
176
177 let epsg = crs.as_epsg();
178 assert_eq!(epsg, Some(code));
179 }
180
181 #[test]
182 fn into_epsg() {
183 let crs = Crs::from_epsg(4979);
184 assert_eq!(
185 crs.to_string(),
186 "http://www.opengis.net/def/crs/EPSG/0/4979".to_string()
187 )
188 }
189
190 #[test]
191 fn to_epsg() {
192 let crs = Crs::from_str("http://www.opengis.net/def/crs/EPSG/0/4979").unwrap();
193 assert_eq!(crs.to_epsg(), Some(Crs::from_epsg(4979)))
194 }
195}