Skip to main content

epsg_utils/
crs.rs

1/// A top-level coordinate reference system.
2///
3/// This enum dispatches over the different CRS types that this parser can handle.
4#[derive(Debug, Clone, PartialEq)]
5pub enum Crs {
6    /// A projected CRS (WKT2 keyword: `PROJCRS`).
7    ProjectedCrs(Box<ProjectedCrs>),
8    /// A geographic CRS (WKT2 keyword: `GEOGCRS`).
9    GeogCrs(Box<GeogCrs>),
10    /// A geodetic CRS (WKT2 keyword: `GEODCRS`).
11    GeodCrs(Box<GeodCrs>),
12    /// A vertical CRS (WKT2 keyword: `VERTCRS`).
13    VertCrs(Box<VertCrs>),
14    /// A compound CRS (WKT2 keyword: `COMPOUNDCRS`).
15    CompoundCrs(Box<CompoundCrs>),
16}
17
18impl Crs {
19    /// Serialize this CRS to a WKT2 string.
20    ///
21    /// This is an alias for [`ToString::to_string()`] provided for discoverability.
22    pub fn to_wkt2(&self) -> String {
23        self.to_string()
24    }
25
26    /// Extract the EPSG code from this CRS's identifiers, if present.
27    pub fn to_epsg(&self) -> Option<i32> {
28        match self {
29            Crs::ProjectedCrs(crs) => crs.to_epsg(),
30            Crs::GeogCrs(crs) => crs.to_epsg(),
31            Crs::GeodCrs(crs) => crs.to_epsg(),
32            Crs::VertCrs(crs) => crs.to_epsg(),
33            Crs::CompoundCrs(crs) => crs.to_epsg(),
34        }
35    }
36}
37
38/// A geographic coordinate reference system (GEOGCRS).
39///
40/// A geographic CRS uses an ellipsoidal coordinate system with latitude and longitude.
41///
42/// WKT2 keywords: `GEOGCRS` (preferred), `GEOGRAPHICCRS`.
43#[derive(Debug, Clone, PartialEq)]
44pub struct GeogCrs {
45    /// The name of the geographic CRS (e.g. "WGS 84").
46    pub name: String,
47    /// Present only if the CRS is dynamic (has a time-varying reference frame).
48    pub dynamic: Option<DynamicCrs>,
49    /// The datum: either a geodetic reference frame or a datum ensemble.
50    pub datum: Datum,
51    /// The coordinate system describing axes, their directions, and units.
52    pub coordinate_system: CoordinateSystem,
53    /// Zero or more scope-extent pairings describing the applicability of this CRS.
54    pub usages: Vec<Usage>,
55    /// Zero or more external identifiers referencing this CRS.
56    pub identifiers: Vec<Identifier>,
57    /// An optional free-text remark about this CRS.
58    pub remark: Option<String>,
59}
60
61impl GeogCrs {
62    /// Extract the EPSG code from this CRS's identifiers, if present.
63    pub fn to_epsg(&self) -> Option<i32> {
64        self.identifiers.iter().find_map(|id| {
65            if id.authority_name == "EPSG"
66                && let AuthorityId::Number(n) = id.authority_unique_id
67            {
68                return Some(n as i32);
69            }
70            None
71        })
72    }
73}
74
75/// A geodetic coordinate reference system (GEODCRS).
76///
77/// A geodetic CRS uses a Cartesian or spherical coordinate system.
78///
79/// WKT2 keywords: `GEODCRS` (preferred), `GEODETICCRS`.
80#[derive(Debug, Clone, PartialEq)]
81pub struct GeodCrs {
82    /// The name of the geodetic CRS (e.g. "WGS 84").
83    pub name: String,
84    /// Present only if the CRS is dynamic (has a time-varying reference frame).
85    pub dynamic: Option<DynamicCrs>,
86    /// The datum: either a geodetic reference frame or a datum ensemble.
87    pub datum: Datum,
88    /// The coordinate system describing axes, their directions, and units.
89    pub coordinate_system: CoordinateSystem,
90    /// Zero or more scope-extent pairings describing the applicability of this CRS.
91    pub usages: Vec<Usage>,
92    /// Zero or more external identifiers referencing this CRS.
93    pub identifiers: Vec<Identifier>,
94    /// An optional free-text remark about this CRS.
95    pub remark: Option<String>,
96}
97
98impl GeodCrs {
99    /// Extract the EPSG code from this CRS's identifiers, if present.
100    pub fn to_epsg(&self) -> Option<i32> {
101        self.identifiers.iter().find_map(|id| {
102            if id.authority_name == "EPSG"
103                && let AuthorityId::Number(n) = id.authority_unique_id
104            {
105                return Some(n as i32);
106            }
107            None
108        })
109    }
110}
111
112/// A vertical coordinate reference system (VERTCRS).
113///
114/// A vertical CRS uses a vertical coordinate system (height or depth).
115/// It may be a standalone CRS (with a datum) or a derived CRS (with a base
116/// vertical CRS and a deriving conversion).
117///
118/// WKT2 keywords: `VERTCRS` (preferred), `VERTICALCRS`.
119#[derive(Debug, Clone, PartialEq)]
120pub struct VertCrs {
121    /// The name of the vertical CRS (e.g. "NAVD88").
122    pub name: String,
123    /// The source of this CRS: either a datum or a base vertical CRS with a
124    /// deriving conversion.
125    pub source: VertCrsSource,
126    /// The coordinate system describing axes, their directions, and units.
127    pub coordinate_system: CoordinateSystem,
128    /// Zero or more geoid model references.
129    pub geoid_models: Vec<GeoidModel>,
130    /// Zero or more scope-extent pairings describing the applicability of this CRS.
131    pub usages: Vec<Usage>,
132    /// Zero or more external identifiers referencing this CRS.
133    pub identifiers: Vec<Identifier>,
134    /// An optional free-text remark about this CRS.
135    pub remark: Option<String>,
136}
137
138/// Whether a vertical CRS is standalone (datum-based) or derived from a base
139/// vertical CRS.
140#[derive(Debug, Clone, PartialEq)]
141pub enum VertCrsSource {
142    /// A standalone vertical CRS with a datum.
143    Datum {
144        /// Present only if the CRS is dynamic.
145        dynamic: Option<DynamicCrs>,
146        /// The datum: either a vertical reference frame or a datum ensemble.
147        datum: VerticalDatum,
148    },
149    /// A derived vertical CRS with a base CRS and a deriving conversion.
150    Derived {
151        /// The base vertical CRS from which this CRS is derived.
152        base_vert_crs: BaseVertCrs,
153        /// The conversion applied to the base CRS.
154        deriving_conversion: MapProjection,
155    },
156}
157
158/// The base vertical CRS of a derived vertical CRS.
159///
160/// WKT2 keyword: `BASEVERTCRS`.
161#[derive(Debug, Clone, PartialEq)]
162pub struct BaseVertCrs {
163    /// The name of the base CRS.
164    pub name: String,
165    /// Present only if the base CRS is dynamic.
166    pub dynamic: Option<DynamicCrs>,
167    /// The datum: either a vertical reference frame or a datum ensemble.
168    pub datum: VerticalDatum,
169    /// Identifiers for this base CRS.
170    pub identifiers: Vec<Identifier>,
171}
172
173impl VertCrs {
174    /// Extract the EPSG code from this CRS's identifiers, if present.
175    pub fn to_epsg(&self) -> Option<i32> {
176        self.identifiers.iter().find_map(|id| {
177            if id.authority_name == "EPSG"
178                && let AuthorityId::Number(n) = id.authority_unique_id
179            {
180                return Some(n as i32);
181            }
182            None
183        })
184    }
185}
186
187/// A vertical datum is either a vertical reference frame or a datum ensemble.
188#[derive(Debug, Clone, PartialEq)]
189pub enum VerticalDatum {
190    /// A single vertical reference frame.
191    ReferenceFrame(VerticalReferenceFrame),
192    /// An ensemble of vertical reference frames.
193    Ensemble(Box<DatumEnsemble>),
194}
195
196/// A vertical reference frame (vertical datum).
197///
198/// WKT2 keywords: `VDATUM` (preferred), `VRF`, `VERTICALDATUM`.
199#[derive(Debug, Clone, PartialEq)]
200pub struct VerticalReferenceFrame {
201    /// The datum name (e.g. "North American Vertical Datum 1988").
202    pub name: String,
203    /// A textual description of the datum anchor point.
204    pub anchor: Option<String>,
205    /// The epoch at which a derived static reference frame is aligned to its parent
206    /// dynamic frame.
207    pub anchor_epoch: Option<f64>,
208    /// Identifiers for this datum.
209    pub identifiers: Vec<Identifier>,
210}
211
212/// A reference to a geoid model associated with a vertical CRS.
213///
214/// WKT2 keyword: `GEOIDMODEL`.
215#[derive(Debug, Clone, PartialEq)]
216pub struct GeoidModel {
217    /// The name of the geoid model.
218    pub name: String,
219    /// Identifiers for this geoid model.
220    pub identifiers: Vec<Identifier>,
221}
222
223/// A compound coordinate reference system (COMPOUNDCRS).
224///
225/// A compound CRS is a non-repeating sequence of two or more independent CRSs.
226///
227/// WKT2 keyword: `COMPOUNDCRS`.
228#[derive(Debug, Clone, PartialEq)]
229pub struct CompoundCrs {
230    /// The name of the compound CRS (e.g. "NAD83 + NAVD88").
231    pub name: String,
232    /// The constituent single CRSs (at least two).
233    pub components: Vec<SingleCrs>,
234    /// Zero or more scope-extent pairings describing the applicability of this CRS.
235    pub usages: Vec<Usage>,
236    /// Zero or more external identifiers referencing this CRS.
237    pub identifiers: Vec<Identifier>,
238    /// An optional free-text remark about this CRS.
239    pub remark: Option<String>,
240}
241
242impl CompoundCrs {
243    /// Extract the EPSG code from this CRS's identifiers, if present.
244    pub fn to_epsg(&self) -> Option<i32> {
245        self.identifiers.iter().find_map(|id| {
246            if id.authority_name == "EPSG"
247                && let AuthorityId::Number(n) = id.authority_unique_id
248            {
249                return Some(n as i32);
250            }
251            None
252        })
253    }
254}
255
256/// A single (non-compound) CRS, used as a component of a compound CRS.
257#[derive(Debug, Clone, PartialEq)]
258pub enum SingleCrs {
259    /// A projected CRS.
260    ProjectedCrs(Box<ProjectedCrs>),
261    /// A geographic CRS.
262    GeogCrs(Box<GeogCrs>),
263    /// A geodetic CRS.
264    GeodCrs(Box<GeodCrs>),
265    /// A vertical CRS.
266    VertCrs(Box<VertCrs>),
267    /// An unsupported CRS type, stored as raw WKT text.
268    Other(String),
269}
270
271/// A projected coordinate reference system (PROJCRS).
272///
273/// A projected CRS is derived from a geographic CRS by applying a map projection.
274/// It uses Cartesian coordinates (easting/northing) rather than angular coordinates.
275///
276/// WKT2 keywords: `PROJCRS` (preferred), `PROJECTEDCRS` (not supported by this parser).
277#[derive(Debug, Clone, PartialEq)]
278pub struct ProjectedCrs {
279    /// The name of the projected CRS (e.g. "WGS 84 / UTM zone 31N").
280    pub name: String,
281    /// The base geodetic or geographic CRS from which this CRS is derived.
282    pub base_geodetic_crs: BaseGeodeticCrs,
283    /// The map projection (conversion) applied to the base CRS.
284    pub map_projection: MapProjection,
285    /// The coordinate system describing axes, their directions, and units.
286    pub coordinate_system: CoordinateSystem,
287    /// Zero or more scope-extent pairings describing the applicability of this CRS.
288    pub usages: Vec<Usage>,
289    /// Zero or more external identifiers referencing this CRS.
290    pub identifiers: Vec<Identifier>,
291    /// An optional free-text remark about this CRS.
292    pub remark: Option<String>,
293}
294
295impl ProjectedCrs {
296    /// Extract the EPSG code from this CRS's identifiers, if present.
297    ///
298    /// Returns `Some(code)` if the CRS has an identifier with authority "EPSG"
299    /// and a numeric code, or `None` otherwise.
300    pub fn to_epsg(&self) -> Option<i32> {
301        self.identifiers.iter().find_map(|id| {
302            if id.authority_name == "EPSG"
303                && let AuthorityId::Number(n) = id.authority_unique_id
304            {
305                return Some(n as i32);
306            }
307            None
308        })
309    }
310}
311
312/// A coordinate system definition, consisting of a type, dimension, axes, and an optional
313/// shared unit.
314///
315/// WKT2 keyword: `CS`.
316#[derive(Debug, Clone, PartialEq)]
317pub struct CoordinateSystem {
318    /// The type of coordinate system (e.g. Cartesian, ellipsoidal).
319    pub cs_type: CsType,
320    /// The number of dimensions (1, 2, or 3).
321    pub dimension: u8,
322    /// Identifiers for the coordinate system itself.
323    pub identifiers: Vec<Identifier>,
324    /// The axes of the coordinate system. These appear as siblings after the `CS[...]` node.
325    pub axes: Vec<Axis>,
326    /// An optional unit shared by all axes. If absent, each axis specifies its own unit.
327    pub cs_unit: Option<Unit>,
328}
329
330/// The type of a coordinate system.
331#[derive(Debug, Clone, PartialEq)]
332pub enum CsType {
333    /// An affine coordinate system.
334    Affine,
335    /// A Cartesian coordinate system with orthogonal straight axes.
336    Cartesian,
337    /// A 3D coordinate system with a polar and a longitudinal axis, plus a straight axis.
338    Cylindrical,
339    /// A coordinate system on the surface of an ellipsoid using latitude and longitude.
340    Ellipsoidal,
341    /// A 1D coordinate system along a straight line.
342    Linear,
343    /// A coordinate system for parametric values (e.g. pressure levels).
344    Parametric,
345    /// A 2D coordinate system with a straight radial axis and an angular axis.
346    Polar,
347    /// A 3D coordinate system on a sphere using two angular coordinates plus radius.
348    Spherical,
349    /// A 1D coordinate system for height or depth.
350    Vertical,
351    /// A temporal coordinate system using integer counts of a time unit.
352    TemporalCount,
353    /// A temporal coordinate system using real-valued measurements of a time unit.
354    TemporalMeasure,
355    /// A coordinate system using discrete ordinal values (ranks).
356    Ordinal,
357    /// A temporal coordinate system using date-time values.
358    TemporalDateTime,
359}
360
361/// A single axis of a coordinate system.
362///
363/// WKT2 keyword: `AXIS`.
364#[derive(Debug, Clone, PartialEq)]
365pub struct Axis {
366    /// The axis name and/or abbreviation (e.g. "easting (E)").
367    pub name_abbrev: String,
368    /// The axis direction (e.g. "north", "east", "up", "clockwise").
369    pub direction: String,
370    /// The meridian from which the axis direction is measured. Only used with
371    /// certain directions like "north" or "south".
372    pub meridian: Option<Meridian>,
373    /// The bearing angle for "clockwise" or "counterClockwise" directions, in degrees.
374    pub bearing: Option<f64>,
375    /// The ordering of this axis within the coordinate system (1-based).
376    pub order: Option<u32>,
377    /// The unit for values along this axis. If absent, the coordinate system's shared unit applies.
378    pub unit: Option<Unit>,
379    /// The minimum value for this axis.
380    pub axis_min_value: Option<f64>,
381    /// The maximum value for this axis.
382    pub axis_max_value: Option<f64>,
383    /// Whether the axis range is exact or wraps around (e.g. longitude 0-360).
384    pub range_meaning: Option<RangeMeaning>,
385    /// Identifiers for this axis.
386    pub identifiers: Vec<Identifier>,
387}
388
389/// A meridian from which an axis direction is measured.
390///
391/// WKT2 keyword: `MERIDIAN`.
392#[derive(Debug, Clone, PartialEq)]
393pub struct Meridian {
394    /// The meridian value (typically longitude in the given angle unit).
395    pub value: f64,
396    /// The angle unit for the meridian value.
397    pub unit: Unit,
398}
399
400/// How the axis range between min and max values is interpreted.
401///
402/// WKT2 keyword: `RANGEMEANING`.
403#[derive(Debug, Clone, Copy, PartialEq)]
404pub enum RangeMeaning {
405    /// The range boundaries are exact limits.
406    Exact,
407    /// The range wraps around (e.g. longitude 0 to 360 degrees).
408    Wraparound,
409}
410
411/// A map projection (coordinate conversion) applied to a base CRS to produce a projected CRS.
412///
413/// WKT2 keyword: `CONVERSION`.
414#[derive(Debug, Clone, PartialEq)]
415pub struct MapProjection {
416    /// The name of the map projection (e.g. "UTM zone 31N").
417    pub name: String,
418    /// The projection method (e.g. "Transverse Mercator").
419    pub method: MapProjectionMethod,
420    /// The parameters of the projection (e.g. central meridian, scale factor).
421    pub parameters: Vec<MapProjectionParameter>,
422    /// Identifiers for this conversion as a whole.
423    pub identifiers: Vec<Identifier>,
424}
425
426/// The method (algorithm) used by a map projection.
427///
428/// WKT2 keywords: `METHOD` (preferred), `PROJECTION` (backward compatibility).
429#[derive(Debug, Clone, PartialEq)]
430pub struct MapProjectionMethod {
431    /// The name of the method (e.g. "Transverse Mercator").
432    pub name: String,
433    /// Identifiers for this method.
434    pub identifiers: Vec<Identifier>,
435}
436
437/// A single parameter of a map projection.
438///
439/// WKT2 keyword: `PARAMETER`.
440#[derive(Debug, Clone, PartialEq)]
441pub struct MapProjectionParameter {
442    /// The parameter name (e.g. "Latitude of natural origin").
443    pub name: String,
444    /// The numeric value of the parameter.
445    pub value: f64,
446    /// The unit for the parameter value (angle, length, or scale).
447    pub unit: Option<Unit>,
448    /// Identifiers for this parameter.
449    pub identifiers: Vec<Identifier>,
450}
451
452/// The keyword used for a base geodetic CRS within a projected CRS.
453#[derive(Debug, Clone, PartialEq)]
454pub enum BaseGeodeticCrsKeyword {
455    /// `BASEGEODCRS` -- a geodetic (geocentric) base CRS.
456    BaseGeodCrs,
457    /// `BASEGEOGCRS` -- a geographic base CRS (the more common form).
458    BaseGeogCrs,
459}
460
461/// The base geodetic or geographic CRS from which a projected CRS is derived.
462///
463/// WKT2 keywords: `BASEGEOGCRS` (preferred), `BASEGEODCRS`.
464#[derive(Debug, Clone, PartialEq)]
465pub struct BaseGeodeticCrs {
466    /// Which keyword was used in the WKT.
467    pub keyword: BaseGeodeticCrsKeyword,
468    /// The name of the base CRS.
469    pub name: String,
470    /// Present only if the CRS is dynamic (has a time-varying reference frame).
471    pub dynamic: Option<DynamicCrs>,
472    /// The datum: either a geodetic reference frame or a datum ensemble.
473    pub datum: Datum,
474    /// An optional ellipsoidal coordinate system unit for the base CRS.
475    pub ellipsoidal_cs_unit: Option<Unit>,
476    /// Identifiers for this base CRS.
477    pub identifiers: Vec<Identifier>,
478}
479
480/// Dynamic CRS attributes for a CRS with a time-varying reference frame.
481///
482/// In a dynamic CRS, coordinate values of a point change with time.
483///
484/// WKT2 keyword: `DYNAMIC`.
485#[derive(Debug, Clone, PartialEq)]
486pub struct DynamicCrs {
487    /// The epoch at which the reference frame is defined (e.g. 2010.0).
488    pub frame_reference_epoch: f64,
489    /// An optional reference to a deformation model or velocity grid.
490    pub deformation_model: Option<DeformationModel>,
491}
492
493/// A reference to a deformation model or velocity grid associated with a dynamic CRS.
494///
495/// WKT2 keywords: `MODEL` (preferred), `VELOCITYGRID`.
496#[derive(Debug, Clone, PartialEq)]
497pub struct DeformationModel {
498    /// The name of the deformation model.
499    pub name: String,
500    /// Identifiers for this deformation model.
501    pub identifiers: Vec<Identifier>,
502}
503
504/// A datum is either a geodetic reference frame or a datum ensemble.
505#[derive(Debug, Clone, PartialEq)]
506pub enum Datum {
507    /// A single geodetic reference frame (classical datum or modern terrestrial reference frame).
508    ReferenceFrame(GeodeticReferenceFrame),
509    /// An ensemble of reference frames that are considered approximately equivalent.
510    Ensemble(DatumEnsemble),
511}
512
513/// A geodetic reference frame (datum), defining the relationship between a coordinate
514/// system and the Earth.
515///
516/// WKT2 keywords: `DATUM` (preferred), `TRF`, `GEODETICDATUM`.
517#[derive(Debug, Clone, PartialEq)]
518pub struct GeodeticReferenceFrame {
519    /// The datum name (e.g. "World Geodetic System 1984").
520    pub name: String,
521    /// The reference ellipsoid.
522    pub ellipsoid: Ellipsoid,
523    /// A textual description of the datum anchor point.
524    pub anchor: Option<String>,
525    /// The epoch at which a derived static reference frame is aligned to its parent
526    /// dynamic frame.
527    pub anchor_epoch: Option<f64>,
528    /// Identifiers for this datum.
529    pub identifiers: Vec<Identifier>,
530    /// The prime meridian. Appears as a sibling after the DATUM node in WKT2.
531    /// If absent, the international reference meridian (Greenwich) is assumed.
532    pub prime_meridian: Option<PrimeMeridian>,
533}
534
535/// The prime meridian defining zero longitude.
536///
537/// WKT2 keywords: `PRIMEM` (preferred), `PRIMEMERIDIAN`.
538#[derive(Debug, Clone, PartialEq)]
539pub struct PrimeMeridian {
540    /// The name of the prime meridian (e.g. "Greenwich", "Paris").
541    pub name: String,
542    /// The longitude of this meridian measured from the international reference meridian,
543    /// positive eastward.
544    pub irm_longitude: f64,
545    /// The angle unit for the longitude value. If absent, the value is in the CRS's
546    /// angular unit (if available), otherwise in decimal degrees.
547    pub unit: Option<Unit>,
548    /// Identifiers for this prime meridian.
549    pub identifiers: Vec<Identifier>,
550}
551
552/// A reference ellipsoid (the mathematical figure of the Earth).
553///
554/// WKT2 keywords: `ELLIPSOID` (preferred), `SPHEROID` (backward compatibility).
555#[derive(Debug, Clone, PartialEq)]
556pub struct Ellipsoid {
557    /// The ellipsoid name (e.g. "WGS 84", "GRS 1980").
558    pub name: String,
559    /// The semi-major axis length.
560    pub semi_major_axis: f64,
561    /// The inverse flattening (0 for a sphere).
562    pub inverse_flattening: f64,
563    /// The length unit for the semi-major axis. If absent, metres are assumed.
564    pub unit: Option<Unit>,
565    /// Identifiers for this ellipsoid.
566    pub identifiers: Vec<Identifier>,
567}
568
569/// A datum ensemble: a collection of reference frames considered approximately equivalent.
570///
571/// Use of datum ensembles comes with a caveat: it will not be possible to identify which
572/// member the data is most accurately referenced to.
573///
574/// WKT2 keyword: `ENSEMBLE`.
575#[derive(Debug, Clone, PartialEq)]
576pub struct DatumEnsemble {
577    /// The ensemble name (e.g. "WGS 84 ensemble").
578    pub name: String,
579    /// The member reference frames of this ensemble.
580    pub members: Vec<EnsembleMember>,
581    /// The reference ellipsoid. Present for geodetic datum ensembles, absent for vertical.
582    pub ellipsoid: Option<Ellipsoid>,
583    /// The positional accuracy of the ensemble in metres, indicating the difference in
584    /// coordinate values between members.
585    pub accuracy: f64,
586    /// Identifiers for this ensemble.
587    pub identifiers: Vec<Identifier>,
588    /// The prime meridian. Present for geodetic datum ensembles, appears as a sibling
589    /// after the ENSEMBLE node.
590    pub prime_meridian: Option<PrimeMeridian>,
591}
592
593/// A member of a datum ensemble.
594///
595/// WKT2 keyword: `MEMBER`.
596#[derive(Debug, Clone, PartialEq)]
597pub struct EnsembleMember {
598    /// The member name (e.g. "WGS 84 (G730)").
599    pub name: String,
600    /// Identifiers for this member.
601    pub identifiers: Vec<Identifier>,
602}
603
604/// An external identifier referencing an authority's definition of an object.
605///
606/// WKT2 keyword: `ID`.
607#[derive(Debug, Clone, PartialEq)]
608pub struct Identifier {
609    /// The name of the authority (e.g. "EPSG").
610    pub authority_name: String,
611    /// The unique identifier within the authority (e.g. 4326 or "Abcd_Ef").
612    pub authority_unique_id: AuthorityId,
613    /// The version of the cited object or repository.
614    pub version: Option<AuthorityId>,
615    /// A citation giving further details of the authority.
616    pub citation: Option<String>,
617    /// A URI referencing an online resource.
618    pub uri: Option<String>,
619}
620
621/// An authority identifier value, which can be either numeric or textual.
622#[derive(Debug, Clone, PartialEq)]
623pub enum AuthorityId {
624    /// A numeric identifier (e.g. `4326`).
625    Number(f64),
626    /// A textual identifier (e.g. `"Abcd_Ef"`).
627    Text(String),
628}
629
630/// The keyword used for a unit.
631#[derive(Debug, Clone, PartialEq)]
632pub enum UnitKeyword {
633    /// `ANGLEUNIT` -- for angular measurements.
634    AngleUnit,
635    /// `LENGTHUNIT` -- for linear measurements.
636    LengthUnit,
637    /// `PARAMETRICUNIT` -- for parametric values (e.g. pressure).
638    ParametricUnit,
639    /// `SCALEUNIT` -- for dimensionless scale factors.
640    ScaleUnit,
641    /// `TIMEUNIT` (or `TEMPORALQUANTITY`) -- for temporal measurements.
642    TimeUnit,
643    /// `UNIT` -- the generic backward-compatible keyword.
644    Unit,
645}
646
647/// A scope-extent pairing describing the applicability of a CRS or coordinate operation.
648///
649/// WKT2 keyword: `USAGE`.
650#[derive(Debug, Clone, PartialEq)]
651pub struct Usage {
652    /// A textual description of the scope (purpose) of the CRS.
653    pub scope: String,
654    /// A textual description of the geographic area of applicability.
655    pub area: Option<String>,
656    /// A geographic bounding box describing the area of applicability.
657    pub bbox: Option<BBox>,
658    /// A vertical height range of applicability.
659    pub vertical_extent: Option<VerticalExtent>,
660    /// A temporal range of applicability.
661    pub temporal_extent: Option<TemporalExtent>,
662}
663
664/// A geographic bounding box in decimal degrees relative to the international reference meridian.
665///
666/// WKT2 keyword: `BBOX`.
667#[derive(Debug, Clone, PartialEq)]
668pub struct BBox {
669    /// The southern latitude boundary (-90 to +90).
670    pub lower_left_latitude: f64,
671    /// The western longitude boundary (-180 to +180).
672    pub lower_left_longitude: f64,
673    /// The northern latitude boundary (-90 to +90).
674    pub upper_right_latitude: f64,
675    /// The eastern longitude boundary (-180 to +180). May be less than
676    /// `lower_left_longitude` when the area crosses the 180 degree meridian.
677    pub upper_right_longitude: f64,
678}
679
680/// A vertical height range of applicability. Depths have negative height values.
681///
682/// WKT2 keyword: `VERTICALEXTENT`.
683#[derive(Debug, Clone, PartialEq)]
684pub struct VerticalExtent {
685    /// The minimum height (most negative = deepest).
686    pub minimum_height: f64,
687    /// The maximum height.
688    pub maximum_height: f64,
689    /// The unit for the height values. If absent, metres are assumed.
690    pub unit: Option<Unit>,
691}
692
693/// A temporal range of applicability.
694///
695/// WKT2 keyword: `TIMEEXTENT`.
696#[derive(Debug, Clone, PartialEq)]
697pub struct TemporalExtent {
698    /// The start of the temporal range. Either a date/time string (e.g. "2013-01-01")
699    /// or descriptive text (e.g. "Jurassic").
700    pub start: String,
701    /// The end of the temporal range.
702    pub end: String,
703}
704
705/// A unit of measurement with an optional conversion factor to the SI base unit.
706///
707/// WKT2 keywords: `ANGLEUNIT`, `LENGTHUNIT`, `SCALEUNIT`, `PARAMETRICUNIT`,
708/// `TIMEUNIT`, `TEMPORALQUANTITY`, `UNIT`.
709#[derive(Debug, Clone, PartialEq)]
710pub struct Unit {
711    /// Which keyword was used in the WKT.
712    pub keyword: UnitKeyword,
713    /// The unit name (e.g. "metre", "degree").
714    pub name: String,
715    /// The conversion factor to the SI base unit (metres for length, radians for angle,
716    /// seconds for time, unity for scale). Optional only for `TIMEUNIT` when the
717    /// conversion is not a simple scaling (e.g. "calendar month").
718    pub conversion_factor: Option<f64>,
719    /// Identifiers for this unit.
720    pub identifiers: Vec<Identifier>,
721}