From dd0e8f54476ebb437634d1f6e1ed800c2dada291 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 19 Dec 2023 15:15:02 -0500 Subject: [PATCH 1/2] Created `enum AxisMappingStrategy` for `OSRAxisMappingStrategy` ordinals. --- CHANGES.md | 5 +++ src/raster/types.rs | 2 +- src/spatial_ref/mod.rs | 2 +- src/spatial_ref/srs.rs | 80 ++++++++++++++++++++++++++++-------- src/spatial_ref/transform.rs | 37 +++++------------ src/vector/layer.rs | 3 +- 6 files changed, 83 insertions(+), 46 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index b88b21609..396a458fd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -2,6 +2,11 @@ ## Unreleased +- Created `enum AxisMappingStrategy` for `OSRAxisMappingStrategy` ordinals. +- **Breaking**: `SpatialRef::{set_}axis_mapping_strategy` use `AxisMappingStrategy` instead of `gdal_sys::OSRAxisMappingStrategy::Type`. + + - + - Defers the gdal_i.lib missing message until after the pkg-config check and outputs pkg-config metadata in case of a static build. - diff --git a/src/raster/types.rs b/src/raster/types.rs index 1d85ec6d9..23d4e6571 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -283,7 +283,7 @@ impl TryFrom for GdalDataType { "Complex data types are not available".into(), )), o => Err(GdalError::BadArgument(format!( - "unknown GDALDataType ordinal' {o}'" + "unknown GDALDataType ordinal '{o}'" ))), } } diff --git a/src/spatial_ref/mod.rs b/src/spatial_ref/mod.rs index 5a0aa9755..533695374 100644 --- a/src/spatial_ref/mod.rs +++ b/src/spatial_ref/mod.rs @@ -13,6 +13,6 @@ mod transform_opts; /// See [`OGRAxisOrientation`](https://gdal.org/api/ogr_srs_api.html#_CPPv418OGRAxisOrientation). pub type AxisOrientationType = gdal_sys::OGRAxisOrientation::Type; -pub use srs::SpatialRef; +pub use srs::{AxisMappingStrategy, SpatialRef}; pub use transform::CoordTransform; pub use transform_opts::CoordTransformOptions; diff --git a/src/spatial_ref/srs.rs b/src/spatial_ref/srs.rs index 40e7009dc..e211319c3 100644 --- a/src/spatial_ref/srs.rs +++ b/src/spatial_ref/srs.rs @@ -1,5 +1,5 @@ use crate::utils::{_last_null_pointer_err, _string}; -use gdal_sys::{self, OGRErr}; +use gdal_sys::{self, OGRErr, OSRAxisMappingStrategy}; use std::ffi::{CStr, CString}; use std::ptr::{self}; use std::str::FromStr; @@ -403,21 +403,30 @@ impl SpatialRef { } #[cfg(major_ge_3)] - pub fn set_axis_mapping_strategy(&mut self, strategy: gdal_sys::OSRAxisMappingStrategy::Type) { + /// Set the data axis to CRS axis mapping strategy. + /// + /// # Notes + /// + /// Starting with GDAL 3.5, the `OSR_DEFAULT_AXIS_MAPPING_STRATEGY` configuration option can be + /// set to `"TRADITIONAL_GIS_ORDER"` or `"AUTHORITY_COMPLIANT"` (the later being the default + /// value when the option is not set) to control the value of the data axis to CRS axis + /// mapping strategy when a [`SpatialRef`] object is created. + /// Calling [`set_axis_mapping_strategy`][Self::set_axis_mapping_strategy] will override this default value. + /// + /// See: [`OSRSetAxisMappingStrategy`](https://gdal.org/api/ogrspatialref.html#_CPPv4N19OGRSpatialReference22SetAxisMappingStrategyE22OSRAxisMappingStrategy) + pub fn set_axis_mapping_strategy(&mut self, strategy: AxisMappingStrategy) { unsafe { - gdal_sys::OSRSetAxisMappingStrategy(self.0, strategy); + gdal_sys::OSRSetAxisMappingStrategy(self.0, strategy.gdal_ordinal()); } } #[cfg(major_ge_3)] - #[deprecated(note = "use `axis_mapping_strategy` instead")] - pub fn get_axis_mapping_strategy(&self) -> gdal_sys::OSRAxisMappingStrategy::Type { - self.axis_mapping_strategy() - } - - #[cfg(major_ge_3)] - pub fn axis_mapping_strategy(&self) -> gdal_sys::OSRAxisMappingStrategy::Type { - unsafe { gdal_sys::OSRGetAxisMappingStrategy(self.0) } + /// Return the data axis to CRS axis mapping strategy. + /// + /// See: [`OSRGetAxisMappingStrategy`](https://gdal.org/api/ogrspatialref.html#_CPPv4NK19OGRSpatialReference22GetAxisMappingStrategyEv) + pub fn axis_mapping_strategy(&self) -> AxisMappingStrategy { + let id = unsafe { gdal_sys::OSRGetAxisMappingStrategy(self.0) }; + id.try_into().expect("valid enumeration ordinal from GDAL") } #[cfg(major_ge_3)] @@ -606,6 +615,47 @@ pub struct AreaOfUse { pub name: String, } +#[cfg(major_ge_3)] +/// Data axis to CRS axis mapping strategy. +/// +/// See: [`OSRGetAxisMappingStrategy`](https://gdal.org/api/ogrspatialref.html#_CPPv4NK19OGRSpatialReference22GetAxisMappingStrategyEv) +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)] +#[repr(u32)] +pub enum AxisMappingStrategy { + /// For geographic CRS with lat/long order, the data will still be long/lat ordered. + /// Similarly for a projected CRS with northing/easting order, the data will still + /// be easting/northing ordered. + TraditionalGisOrder = OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, + /// The data axis will be identical to the CRS axis. + AuthorityCompliant = OSRAxisMappingStrategy::OAMS_AUTHORITY_COMPLIANT, + /// The data axes are custom-defined via [`gdal_sys::OSRSetDataAxisToSRSAxisMapping`]. + Custom = OSRAxisMappingStrategy::OAMS_CUSTOM, +} + +impl AxisMappingStrategy { + #[inline] + pub(crate) fn gdal_ordinal(&self) -> OSRAxisMappingStrategy::Type { + *self as OSRAxisMappingStrategy::Type + } +} + +impl TryFrom for AxisMappingStrategy { + type Error = GdalError; + + fn try_from(value: u32) -> std::result::Result { + use OSRAxisMappingStrategy::*; + + match value { + OAMS_TRADITIONAL_GIS_ORDER => Ok(Self::TraditionalGisOrder), + OAMS_AUTHORITY_COMPLIANT => Ok(Self::AuthorityCompliant), + OAMS_CUSTOM => Ok(Self::Custom), + o => Err(GdalError::BadArgument(format!( + "unknown OSRAxisMappingStrategy ordinal '{o}'" + ))), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -751,14 +801,12 @@ mod tests { let mut spatial_ref = SpatialRef::from_epsg(4326).unwrap(); assert_eq!( spatial_ref.axis_mapping_strategy(), - gdal_sys::OSRAxisMappingStrategy::OAMS_AUTHORITY_COMPLIANT - ); - spatial_ref.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, + AxisMappingStrategy::AuthorityCompliant ); + spatial_ref.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); assert_eq!( spatial_ref.axis_mapping_strategy(), - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER + AxisMappingStrategy::TraditionalGisOrder ); } diff --git a/src/spatial_ref/transform.rs b/src/spatial_ref/transform.rs index ca898214d..3aa6507d6 100644 --- a/src/spatial_ref/transform.rs +++ b/src/spatial_ref/transform.rs @@ -205,6 +205,7 @@ impl CoordTransform { mod tests { use super::*; use crate::assert_almost_eq; + use crate::spatial_ref::srs::AxisMappingStrategy; use crate::vector::Geometry; #[cfg(all(major_ge_3, minor_ge_4))] @@ -244,9 +245,7 @@ mod tests { assert_almost_eq(out_bounds[3], bounds[3]); // force EPSG:4326 into x,y order to match source SpatialRef - spatial_ref2.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + spatial_ref2.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); transform = CoordTransform::new(&spatial_ref1, &spatial_ref2).unwrap(); out_bounds = transform.transform_bounds(&bounds, 21).unwrap(); assert_almost_eq(out_bounds[0], bounds[0]); @@ -277,13 +276,9 @@ mod tests { // TODO: handle axis order in tests #[cfg(major_ge_3)] - spatial_ref1.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + spatial_ref1.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); #[cfg(major_ge_3)] - spatial_ref2.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + spatial_ref2.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); let transform = CoordTransform::new(&spatial_ref1, &spatial_ref2).unwrap(); let mut xs = [23.43, 23.50]; @@ -314,13 +309,9 @@ mod tests { // TODO: handle axis order in tests #[cfg(major_ge_3)] - spatial_ref1.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + spatial_ref1.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); #[cfg(major_ge_3)] - spatial_ref2.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + spatial_ref2.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); let htransform = CoordTransform::new(&spatial_ref2, &spatial_ref1).unwrap(); geom.transform_inplace(&htransform).unwrap(); @@ -334,13 +325,9 @@ mod tests { // TODO: handle axis order in tests #[cfg(major_ge_3)] - wgs84.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + wgs84.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); #[cfg(major_ge_3)] - dhd_2.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + dhd_2.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); let mut x = [1979105.06, 0.0]; let mut y = [5694052.67, 0.0]; @@ -355,13 +342,9 @@ mod tests { // TODO: handle axis order in tests #[cfg(major_ge_3)] - wgs84.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + wgs84.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); #[cfg(major_ge_3)] - webmercator.set_axis_mapping_strategy( - gdal_sys::OSRAxisMappingStrategy::OAMS_TRADITIONAL_GIS_ORDER, - ); + webmercator.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); let mut x = [1000000.0]; let mut y = [1000000.0]; diff --git a/src/vector/layer.rs b/src/vector/layer.rs index 530bc6d13..07a50cfd9 100644 --- a/src/vector/layer.rs +++ b/src/vector/layer.rs @@ -732,6 +732,7 @@ impl Dataset { mod tests { use super::{LayerCaps::*, *}; use crate::options::DatasetOptions; + use crate::spatial_ref::AxisMappingStrategy; use crate::test_utils::{fixture, open_gpkg_for_update, SuppressGDALErrorLog, TempFixture}; use crate::vector::feature::FeatureIterator; use crate::{assert_almost_eq, Dataset, DriverManager, GdalOpenFlags}; @@ -1470,7 +1471,7 @@ mod tests { let geom_field = layer.defn().geom_fields().next().unwrap(); let mut spatial_ref2 = SpatialRef::from_epsg(4326).unwrap(); #[cfg(major_ge_3)] - spatial_ref2.set_axis_mapping_strategy(0); + spatial_ref2.set_axis_mapping_strategy(AxisMappingStrategy::TraditionalGisOrder); assert_eq!(geom_field.spatial_ref().unwrap(), spatial_ref2); } From 1cbce6503f30e6c0bec7905aeed8904aa4ea1735 Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 19 Dec 2023 16:18:27 -0500 Subject: [PATCH 2/2] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Laurențiu Nicola --- src/raster/types.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raster/types.rs b/src/raster/types.rs index 23d4e6571..26b5003b9 100644 --- a/src/raster/types.rs +++ b/src/raster/types.rs @@ -283,7 +283,7 @@ impl TryFrom for GdalDataType { "Complex data types are not available".into(), )), o => Err(GdalError::BadArgument(format!( - "unknown GDALDataType ordinal '{o}'" + "unknown GDALDataType ordinal `{o}`" ))), } }