Skip to main content

proj_core/
crs.rs

1use crate::datum::Datum;
2
3/// A Coordinate Reference System definition.
4#[derive(Debug, Clone, Copy)]
5pub enum CrsDef {
6    /// Geographic CRS (lon/lat in degrees).
7    Geographic(GeographicCrsDef),
8    /// Projected CRS (easting/northing in meters).
9    Projected(ProjectedCrsDef),
10}
11
12impl CrsDef {
13    /// Get the datum for this CRS.
14    pub fn datum(&self) -> &Datum {
15        match self {
16            CrsDef::Geographic(g) => &g.datum,
17            CrsDef::Projected(p) => &p.datum,
18        }
19    }
20
21    /// Get the EPSG code for this CRS.
22    pub fn epsg(&self) -> u32 {
23        match self {
24            CrsDef::Geographic(g) => g.epsg,
25            CrsDef::Projected(p) => p.epsg,
26        }
27    }
28
29    /// Get the CRS name.
30    pub fn name(&self) -> &str {
31        match self {
32            CrsDef::Geographic(g) => g.name,
33            CrsDef::Projected(p) => p.name,
34        }
35    }
36
37    /// Returns true if this is a geographic CRS.
38    pub fn is_geographic(&self) -> bool {
39        matches!(self, CrsDef::Geographic(_))
40    }
41
42    /// Returns true if this is a projected CRS.
43    pub fn is_projected(&self) -> bool {
44        matches!(self, CrsDef::Projected(_))
45    }
46}
47
48/// Definition of a geographic CRS (longitude, latitude in degrees).
49#[derive(Debug, Clone, Copy)]
50pub struct GeographicCrsDef {
51    /// EPSG code.
52    pub epsg: u32,
53    /// Geodetic datum.
54    pub datum: Datum,
55    /// Human-readable name.
56    pub name: &'static str,
57}
58
59/// Definition of a projected CRS (easting, northing in meters).
60#[derive(Debug, Clone, Copy)]
61pub struct ProjectedCrsDef {
62    /// EPSG code.
63    pub epsg: u32,
64    /// Geodetic datum.
65    pub datum: Datum,
66    /// Projection method and parameters.
67    pub method: ProjectionMethod,
68    /// Human-readable name.
69    pub name: &'static str,
70}
71
72/// All supported projection methods with their parameters.
73///
74/// Angle parameters are stored in **degrees**. Conversion to radians happens
75/// at projection construction time (once), not per-transform.
76#[derive(Debug, Clone, Copy)]
77pub enum ProjectionMethod {
78    /// Web Mercator (EPSG:3857) — spherical Mercator on WGS84 semi-major axis.
79    WebMercator,
80
81    /// Transverse Mercator (includes UTM zones).
82    TransverseMercator {
83        /// Central meridian (degrees).
84        lon0: f64,
85        /// Latitude of origin (degrees).
86        lat0: f64,
87        /// Scale factor on central meridian.
88        k0: f64,
89        /// False easting (meters).
90        false_easting: f64,
91        /// False northing (meters).
92        false_northing: f64,
93    },
94
95    /// Polar Stereographic.
96    PolarStereographic {
97        /// Central meridian / straight vertical longitude (degrees).
98        lon0: f64,
99        /// Latitude of true scale (degrees). Determines the hemisphere.
100        lat_ts: f64,
101        /// Scale factor (used when lat_ts = ±90°, otherwise derived from lat_ts).
102        k0: f64,
103        /// False easting (meters).
104        false_easting: f64,
105        /// False northing (meters).
106        false_northing: f64,
107    },
108
109    /// Lambert Conformal Conic (1SP or 2SP).
110    LambertConformalConic {
111        /// Central meridian (degrees).
112        lon0: f64,
113        /// Latitude of origin (degrees).
114        lat0: f64,
115        /// First standard parallel (degrees).
116        lat1: f64,
117        /// Second standard parallel (degrees). Set equal to lat1 for 1SP variant.
118        lat2: f64,
119        /// False easting (meters).
120        false_easting: f64,
121        /// False northing (meters).
122        false_northing: f64,
123    },
124
125    /// Albers Equal Area Conic.
126    AlbersEqualArea {
127        /// Central meridian (degrees).
128        lon0: f64,
129        /// Latitude of origin (degrees).
130        lat0: f64,
131        /// First standard parallel (degrees).
132        lat1: f64,
133        /// Second standard parallel (degrees).
134        lat2: f64,
135        /// False easting (meters).
136        false_easting: f64,
137        /// False northing (meters).
138        false_northing: f64,
139    },
140
141    /// Standard Mercator (ellipsoidal, distinct from Web Mercator).
142    Mercator {
143        /// Central meridian (degrees).
144        lon0: f64,
145        /// Latitude of true scale (degrees). 0 for 1SP variant.
146        lat_ts: f64,
147        /// Scale factor (for 1SP when lat_ts = 0).
148        k0: f64,
149        /// False easting (meters).
150        false_easting: f64,
151        /// False northing (meters).
152        false_northing: f64,
153    },
154
155    /// Equidistant Cylindrical / Plate Carrée.
156    EquidistantCylindrical {
157        /// Central meridian (degrees).
158        lon0: f64,
159        /// Latitude of true scale (degrees).
160        lat_ts: f64,
161        /// False easting (meters).
162        false_easting: f64,
163        /// False northing (meters).
164        false_northing: f64,
165    },
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use crate::datum;
172
173    #[test]
174    fn geographic_crs_is_geographic() {
175        let crs = CrsDef::Geographic(GeographicCrsDef {
176            epsg: 4326,
177            datum: datum::WGS84,
178            name: "WGS 84",
179        });
180        assert!(crs.is_geographic());
181        assert!(!crs.is_projected());
182        assert_eq!(crs.epsg(), 4326);
183    }
184
185    #[test]
186    fn projected_crs_is_projected() {
187        let crs = CrsDef::Projected(ProjectedCrsDef {
188            epsg: 3857,
189            datum: datum::WGS84,
190            method: ProjectionMethod::WebMercator,
191            name: "WGS 84 / Pseudo-Mercator",
192        });
193        assert!(crs.is_projected());
194        assert!(!crs.is_geographic());
195        assert_eq!(crs.epsg(), 3857);
196    }
197}