Skip to main content

geotiff_core/
crs.rs

1//! Coordinate Reference System extraction from GeoKeys.
2
3use crate::geokeys::{self, GeoKeyDirectory, GeoKeyValue};
4
5/// GeoTIFF model type.
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum ModelType {
8    Projected,
9    Geographic,
10    Geocentric,
11    Unknown(u16),
12}
13
14impl ModelType {
15    pub fn from_code(code: u16) -> Self {
16        match code {
17            1 => Self::Projected,
18            2 => Self::Geographic,
19            3 => Self::Geocentric,
20            other => Self::Unknown(other),
21        }
22    }
23
24    pub fn code(&self) -> u16 {
25        match self {
26            Self::Projected => 1,
27            Self::Geographic => 2,
28            Self::Geocentric => 3,
29            Self::Unknown(v) => *v,
30        }
31    }
32}
33
34/// GeoTIFF raster-space interpretation.
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum RasterType {
37    PixelIsArea,
38    PixelIsPoint,
39    Unknown(u16),
40}
41
42impl RasterType {
43    pub fn from_code(code: u16) -> Self {
44        match code {
45            1 => Self::PixelIsArea,
46            2 => Self::PixelIsPoint,
47            other => Self::Unknown(other),
48        }
49    }
50
51    pub fn code(&self) -> u16 {
52        match self {
53            Self::PixelIsArea => 1,
54            Self::PixelIsPoint => 2,
55            Self::Unknown(v) => *v,
56        }
57    }
58}
59
60/// Extracted CRS information from GeoKeys.
61#[derive(Debug, Clone)]
62pub struct CrsInfo {
63    /// Model type: 1 = Projected, 2 = Geographic, 3 = Geocentric.
64    pub model_type: u16,
65    /// Raster type: 1 = PixelIsArea, 2 = PixelIsPoint.
66    pub raster_type: u16,
67    /// EPSG code for a projected CRS (from ProjectedCSTypeGeoKey).
68    pub projected_epsg: Option<u16>,
69    /// EPSG code for a geographic CRS (from GeographicTypeGeoKey).
70    pub geographic_epsg: Option<u16>,
71    /// Citation string for the projected CRS.
72    pub projection_citation: Option<String>,
73    /// Citation string for the geographic CRS.
74    pub geographic_citation: Option<String>,
75}
76
77impl CrsInfo {
78    /// Extract CRS information from a GeoKey directory.
79    pub fn from_geokeys(geokeys: &GeoKeyDirectory) -> Self {
80        Self {
81            model_type: geokeys.get_short(geokeys::GT_MODEL_TYPE).unwrap_or(0),
82            raster_type: geokeys.get_short(geokeys::GT_RASTER_TYPE).unwrap_or(1),
83            projected_epsg: geokeys.get_short(geokeys::PROJECTED_CS_TYPE),
84            geographic_epsg: geokeys.get_short(geokeys::GEOGRAPHIC_TYPE),
85            projection_citation: geokeys.get_ascii(geokeys::PROJ_CITATION).map(String::from),
86            geographic_citation: geokeys.get_ascii(geokeys::GEOG_CITATION).map(String::from),
87        }
88    }
89
90    /// Returns the most specific EPSG code available.
91    pub fn epsg(&self) -> Option<u32> {
92        self.projected_epsg
93            .or(self.geographic_epsg)
94            .map(|e| e as u32)
95    }
96
97    /// Returns the GeoTIFF raster-space interpretation.
98    pub fn raster_type_enum(&self) -> RasterType {
99        RasterType::from_code(self.raster_type)
100    }
101
102    /// Returns the GeoTIFF model type.
103    pub fn model_type_enum(&self) -> ModelType {
104        ModelType::from_code(self.model_type)
105    }
106
107    /// Populate a GeoKeyDirectory from this CRS info.
108    pub fn apply_to_geokeys(&self, geokeys: &mut GeoKeyDirectory) {
109        geokeys.set(geokeys::GT_MODEL_TYPE, GeoKeyValue::Short(self.model_type));
110        geokeys.set(
111            geokeys::GT_RASTER_TYPE,
112            GeoKeyValue::Short(self.raster_type),
113        );
114        if let Some(epsg) = self.projected_epsg {
115            geokeys.set(geokeys::PROJECTED_CS_TYPE, GeoKeyValue::Short(epsg));
116        }
117        if let Some(epsg) = self.geographic_epsg {
118            geokeys.set(geokeys::GEOGRAPHIC_TYPE, GeoKeyValue::Short(epsg));
119        }
120        if let Some(ref citation) = self.projection_citation {
121            geokeys.set(geokeys::PROJ_CITATION, GeoKeyValue::Ascii(citation.clone()));
122        }
123        if let Some(ref citation) = self.geographic_citation {
124            geokeys.set(geokeys::GEOG_CITATION, GeoKeyValue::Ascii(citation.clone()));
125        }
126    }
127}