async_tiff/geo/
geo_key_directory.rs

1#![allow(dead_code)]
2#![allow(missing_docs)]
3
4use std::collections::HashMap;
5
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7
8use crate::tiff::Value;
9use crate::tiff::{TiffError, TiffResult};
10
11/// Geospatial TIFF tag variants
12#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive, IntoPrimitive, Eq, Hash)]
13#[repr(u16)]
14pub(crate) enum GeoKeyTag {
15    // GeoTIFF configuration keys
16    ModelType = 1024,
17    RasterType = 1025,
18    Citation = 1026,
19
20    // Geodetic CRS Parameter Keys
21    GeographicType = 2048,
22    GeogCitation = 2049,
23    GeogGeodeticDatum = 2050,
24    GeogPrimeMeridian = 2051,
25    GeogLinearUnits = 2052,
26    GeogLinearUnitSize = 2053,
27    GeogAngularUnits = 2054,
28    GeogAngularUnitSize = 2055,
29    GeogEllipsoid = 2056,
30    GeogSemiMajorAxis = 2057,
31    GeogSemiMinorAxis = 2058,
32    GeogInvFlattening = 2059,
33    GeogAzimuthUnits = 2060,
34    GeogPrimeMeridianLong = 2061,
35
36    // Projected CRS Parameter Keys
37    ProjectedType = 3072,
38    ProjCitation = 3073,
39    Projection = 3074,
40    ProjCoordTrans = 3075,
41    ProjLinearUnits = 3076,
42    ProjLinearUnitSize = 3077,
43    ProjStdParallel1 = 3078,
44    ProjStdParallel2 = 3079,
45    ProjNatOriginLong = 3080,
46    ProjNatOriginLat = 3081,
47    ProjFalseEasting = 3082,
48    ProjFalseNorthing = 3083,
49    ProjFalseOriginLong = 3084,
50    ProjFalseOriginLat = 3085,
51    ProjFalseOriginEasting = 3086,
52    ProjFalseOriginNorthing = 3087,
53    ProjCenterLong = 3088,
54    ProjCenterLat = 3089,
55    ProjCenterEasting = 3090,
56    ProjCenterNorthing = 3091,
57    ProjScaleAtNatOrigin = 3092,
58    ProjScaleAtCenter = 3093,
59    ProjAzimuthAngle = 3094,
60    ProjStraightVertPoleLong = 3095,
61
62    // Vertical CRS Parameter Keys (4096-5119)
63    Vertical = 4096,
64    VerticalCitation = 4097,
65    VerticalDatum = 4098,
66    VerticalUnits = 4099,
67}
68
69/// Metadata defined by the GeoTIFF standard.
70///
71/// <http://docs.opengeospatial.org/is/19-008r4/19-008r4.html#_requirements_class_geokeydirectorytag>
72#[derive(Debug, Clone)]
73pub struct GeoKeyDirectory {
74    pub model_type: Option<u16>,
75    pub raster_type: Option<u16>,
76    pub citation: Option<String>,
77
78    pub geographic_type: Option<u16>,
79    pub geog_citation: Option<String>,
80    pub geog_geodetic_datum: Option<u16>,
81
82    /// This key is used to specify a Prime Meridian from the GeoTIFF CRS register or to indicate
83    /// that the Prime Meridian is user-defined. The default is Greenwich, England.
84    /// <https://docs.ogc.org/is/19-008r4/19-008r4.html#_requirements_class_primemeridiangeokey>
85    pub geog_prime_meridian: Option<u16>,
86
87    pub geog_linear_units: Option<u16>,
88    pub geog_linear_unit_size: Option<f64>,
89    pub geog_angular_units: Option<u16>,
90    pub geog_angular_unit_size: Option<f64>,
91
92    /// This key is provided to specify an ellipsoid (or sphere) from the GeoTIFF CRS register or
93    /// to indicate that the ellipsoid (or sphere) is user-defined.
94    pub geog_ellipsoid: Option<u16>,
95    pub geog_semi_major_axis: Option<f64>,
96    pub geog_semi_minor_axis: Option<f64>,
97    pub geog_inv_flattening: Option<f64>,
98    pub geog_azimuth_units: Option<u16>,
99
100    /// This key allows definition of a user-defined Prime Meridian, the location of which is
101    /// defined by its longitude relative to the international reference meridian (for the earth
102    /// this is Greenwich).
103    pub geog_prime_meridian_long: Option<f64>,
104
105    pub projected_type: Option<u16>,
106    pub proj_citation: Option<String>,
107    pub projection: Option<u16>,
108    pub proj_coord_trans: Option<u16>,
109    pub proj_linear_units: Option<u16>,
110    pub proj_linear_unit_size: Option<f64>,
111    pub proj_std_parallel1: Option<f64>,
112    pub proj_std_parallel2: Option<f64>,
113    pub proj_nat_origin_long: Option<f64>,
114    pub proj_nat_origin_lat: Option<f64>,
115    pub proj_false_easting: Option<f64>,
116    pub proj_false_northing: Option<f64>,
117    pub proj_false_origin_long: Option<f64>,
118    pub proj_false_origin_lat: Option<f64>,
119    pub proj_false_origin_easting: Option<f64>,
120    pub proj_false_origin_northing: Option<f64>,
121    pub proj_center_long: Option<f64>,
122    pub proj_center_lat: Option<f64>,
123    pub proj_center_easting: Option<f64>,
124    pub proj_center_northing: Option<f64>,
125    pub proj_scale_at_nat_origin: Option<f64>,
126    pub proj_scale_at_center: Option<f64>,
127    pub proj_azimuth_angle: Option<f64>,
128    pub proj_straight_vert_pole_long: Option<f64>,
129
130    pub vertical: Option<u16>,
131    pub vertical_citation: Option<String>,
132    pub vertical_datum: Option<u16>,
133    pub vertical_units: Option<u16>,
134}
135
136impl GeoKeyDirectory {
137    /// Construct a new [`GeoKeyDirectory`] from tag values.
138    pub(crate) fn from_tags(mut tag_data: HashMap<GeoKeyTag, Value>) -> TiffResult<Self> {
139        let mut model_type = None;
140        let mut raster_type = None;
141        let mut citation = None;
142
143        let mut geographic_type = None;
144        let mut geog_citation = None;
145        let mut geog_geodetic_datum = None;
146        let mut geog_prime_meridian = None;
147        let mut geog_linear_units = None;
148        let mut geog_linear_unit_size = None;
149        let mut geog_angular_units = None;
150        let mut geog_angular_unit_size = None;
151        let mut geog_ellipsoid = None;
152        let mut geog_semi_major_axis = None;
153        let mut geog_semi_minor_axis = None;
154        let mut geog_inv_flattening = None;
155        let mut geog_azimuth_units = None;
156        let mut geog_prime_meridian_long = None;
157
158        let mut projected_type = None;
159        let mut proj_citation = None;
160        let mut projection = None;
161        let mut proj_coord_trans = None;
162        let mut proj_linear_units = None;
163        let mut proj_linear_unit_size = None;
164        let mut proj_std_parallel1 = None;
165        let mut proj_std_parallel2 = None;
166        let mut proj_nat_origin_long = None;
167        let mut proj_nat_origin_lat = None;
168        let mut proj_false_easting = None;
169        let mut proj_false_northing = None;
170        let mut proj_false_origin_long = None;
171        let mut proj_false_origin_lat = None;
172        let mut proj_false_origin_easting = None;
173        let mut proj_false_origin_northing = None;
174        let mut proj_center_long = None;
175        let mut proj_center_lat = None;
176        let mut proj_center_easting = None;
177        let mut proj_center_northing = None;
178        let mut proj_scale_at_nat_origin = None;
179        let mut proj_scale_at_center = None;
180        let mut proj_azimuth_angle = None;
181        let mut proj_straight_vert_pole_long = None;
182
183        let mut vertical = None;
184        let mut vertical_citation = None;
185        let mut vertical_datum = None;
186        let mut vertical_units = None;
187
188        tag_data.drain().try_for_each(|(tag, value)| {
189            match tag {
190                GeoKeyTag::ModelType => model_type = Some(value.into_u16()?),
191                GeoKeyTag::RasterType => raster_type = Some(value.into_u16()?),
192                GeoKeyTag::Citation => citation = Some(value.into_string()?),
193                GeoKeyTag::GeographicType => geographic_type = Some(value.into_u16()?),
194                GeoKeyTag::GeogCitation => geog_citation = Some(value.into_string()?),
195                GeoKeyTag::GeogGeodeticDatum => geog_geodetic_datum = Some(value.into_u16()?),
196                GeoKeyTag::GeogPrimeMeridian => geog_prime_meridian = Some(value.into_u16()?),
197                GeoKeyTag::GeogLinearUnits => geog_linear_units = Some(value.into_u16()?),
198                GeoKeyTag::GeogLinearUnitSize => geog_linear_unit_size = Some(value.into_f64()?),
199                GeoKeyTag::GeogAngularUnits => geog_angular_units = Some(value.into_u16()?),
200                GeoKeyTag::GeogAngularUnitSize => geog_angular_unit_size = Some(value.into_f64()?),
201                GeoKeyTag::GeogEllipsoid => geog_ellipsoid = Some(value.into_u16()?),
202                GeoKeyTag::GeogSemiMajorAxis => geog_semi_major_axis = Some(value.into_f64()?),
203                GeoKeyTag::GeogSemiMinorAxis => geog_semi_minor_axis = Some(value.into_f64()?),
204                GeoKeyTag::GeogInvFlattening => geog_inv_flattening = Some(value.into_f64()?),
205                GeoKeyTag::GeogAzimuthUnits => geog_azimuth_units = Some(value.into_u16()?),
206                GeoKeyTag::GeogPrimeMeridianLong => {
207                    geog_prime_meridian_long = Some(value.into_f64()?)
208                }
209                GeoKeyTag::ProjectedType => projected_type = Some(value.into_u16()?),
210                GeoKeyTag::ProjCitation => proj_citation = Some(value.into_string()?),
211                GeoKeyTag::Projection => projection = Some(value.into_u16()?),
212                GeoKeyTag::ProjCoordTrans => proj_coord_trans = Some(value.into_u16()?),
213                GeoKeyTag::ProjLinearUnits => proj_linear_units = Some(value.into_u16()?),
214                GeoKeyTag::ProjLinearUnitSize => proj_linear_unit_size = Some(value.into_f64()?),
215                GeoKeyTag::ProjStdParallel1 => proj_std_parallel1 = Some(value.into_f64()?),
216                GeoKeyTag::ProjStdParallel2 => proj_std_parallel2 = Some(value.into_f64()?),
217                GeoKeyTag::ProjNatOriginLong => proj_nat_origin_long = Some(value.into_f64()?),
218                GeoKeyTag::ProjNatOriginLat => proj_nat_origin_lat = Some(value.into_f64()?),
219                GeoKeyTag::ProjFalseEasting => proj_false_easting = Some(value.into_f64()?),
220                GeoKeyTag::ProjFalseNorthing => proj_false_northing = Some(value.into_f64()?),
221                GeoKeyTag::ProjFalseOriginLong => proj_false_origin_long = Some(value.into_f64()?),
222                GeoKeyTag::ProjFalseOriginLat => proj_false_origin_lat = Some(value.into_f64()?),
223                GeoKeyTag::ProjFalseOriginEasting => {
224                    proj_false_origin_easting = Some(value.into_f64()?)
225                }
226                GeoKeyTag::ProjFalseOriginNorthing => {
227                    proj_false_origin_northing = Some(value.into_f64()?)
228                }
229                GeoKeyTag::ProjCenterLong => proj_center_long = Some(value.into_f64()?),
230                GeoKeyTag::ProjCenterLat => proj_center_lat = Some(value.into_f64()?),
231                GeoKeyTag::ProjCenterEasting => proj_center_easting = Some(value.into_f64()?),
232                GeoKeyTag::ProjCenterNorthing => proj_center_northing = Some(value.into_f64()?),
233                GeoKeyTag::ProjScaleAtNatOrigin => {
234                    proj_scale_at_nat_origin = Some(value.into_f64()?)
235                }
236                GeoKeyTag::ProjScaleAtCenter => proj_scale_at_center = Some(value.into_f64()?),
237                GeoKeyTag::ProjAzimuthAngle => proj_azimuth_angle = Some(value.into_f64()?),
238                GeoKeyTag::ProjStraightVertPoleLong => {
239                    proj_straight_vert_pole_long = Some(value.into_f64()?)
240                }
241                GeoKeyTag::Vertical => vertical = Some(value.into_u16()?),
242                GeoKeyTag::VerticalCitation => vertical_citation = Some(value.into_string()?),
243                GeoKeyTag::VerticalDatum => vertical_datum = Some(value.into_u16()?),
244                GeoKeyTag::VerticalUnits => vertical_units = Some(value.into_u16()?),
245            };
246            Ok::<_, TiffError>(())
247        })?;
248
249        Ok(Self {
250            model_type,
251            raster_type,
252            citation,
253
254            geographic_type,
255            geog_citation,
256            geog_geodetic_datum,
257            geog_prime_meridian,
258            geog_linear_units,
259            geog_linear_unit_size,
260            geog_angular_units,
261            geog_angular_unit_size,
262            geog_ellipsoid,
263            geog_semi_major_axis,
264            geog_semi_minor_axis,
265            geog_inv_flattening,
266            geog_azimuth_units,
267            geog_prime_meridian_long,
268
269            projected_type,
270            proj_citation,
271            projection,
272            proj_coord_trans,
273            proj_linear_units,
274            proj_linear_unit_size,
275            proj_std_parallel1,
276            proj_std_parallel2,
277            proj_nat_origin_long,
278            proj_nat_origin_lat,
279            proj_false_easting,
280            proj_false_northing,
281            proj_false_origin_long,
282            proj_false_origin_lat,
283            proj_false_origin_easting,
284            proj_false_origin_northing,
285            proj_center_long,
286            proj_center_lat,
287            proj_center_easting,
288            proj_center_northing,
289            proj_scale_at_nat_origin,
290            proj_scale_at_center,
291            proj_azimuth_angle,
292            proj_straight_vert_pole_long,
293
294            vertical,
295            vertical_citation,
296            vertical_datum,
297            vertical_units,
298        })
299    }
300
301    /// Return the EPSG code representing the crs of the image
302    ///
303    /// This will return either [`GeoKeyDirectory::projected_type`] or
304    /// [`GeoKeyDirectory::geographic_type`].
305    pub fn epsg_code(&self) -> Option<u16> {
306        if let Some(projected_type) = self.projected_type {
307            Some(projected_type)
308        } else {
309            self.geographic_type
310        }
311    }
312}