From 0cb95fa6d408a3537adc534ce36cea8b828af45d Mon Sep 17 00:00:00 2001 From: "Simeon H.K. Fitch" Date: Tue, 9 May 2023 17:22:02 -0400 Subject: [PATCH] Misc documentation and formatting tweaks. --- CHANGES.md | 6 +++ examples/spatial_reference.rs | 3 +- src/dataset.rs | 4 +- src/spatial_ref/mod.rs | 13 ++++-- src/spatial_ref/srs.rs | 66 +++++++++++++++++++++---------- src/spatial_ref/transform.rs | 56 ++++++++++++++++++++------ src/spatial_ref/transform_opts.rs | 17 ++++---- src/vector/geometry.rs | 2 +- src/vector/layer.rs | 4 +- src/vector/ops/transformations.rs | 6 +-- 10 files changed, 124 insertions(+), 53 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6047c178d..e0c39ca6a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -81,6 +81,12 @@ - +- Split `spatial_ref` module into more feature-specific submodules. + **Breaking** `SpatialRef::to_c_hsrs` is now `unsafe` since it returns a raw C pointer, as is the convention with + `Dataset::c_dataset`, `Geometry::c_geometry`, etc. + + - + ## 0.14 - Added new content to `README.md` and the root docs. diff --git a/examples/spatial_reference.rs b/examples/spatial_reference.rs index b80d0c2f4..95f98e148 100644 --- a/examples/spatial_reference.rs +++ b/examples/spatial_reference.rs @@ -1,6 +1,5 @@ use gdal::errors::Result; -use gdal::spatial_ref::SpatialRef; -use gdal::spatial_ref::CoordTransform; +use gdal::spatial_ref::{CoordTransform, SpatialRef}; use gdal::vector::Geometry; fn run() -> Result<()> { diff --git a/src/dataset.rs b/src/dataset.rs index a67c261a8..00c3989a2 100644 --- a/src/dataset.rs +++ b/src/dataset.rs @@ -438,7 +438,7 @@ impl Dataset { #[cfg(major_ge_3)] /// Set the spatial reference system for this dataset. pub fn set_spatial_ref(&mut self, spatial_ref: &SpatialRef) -> Result<()> { - let rv = unsafe { gdal_sys::GDALSetSpatialRef(self.c_dataset, spatial_ref.c_handle()) }; + let rv = unsafe { gdal_sys::GDALSetSpatialRef(self.c_dataset, spatial_ref.to_c_hsrs()) }; if rv != CPLErr::CE_None { return Err(_last_cpl_err(rv)); } @@ -683,7 +683,7 @@ impl Dataset { let c_layer = unsafe { let c_srs = match options.srs { - Some(srs) => srs.c_handle(), + Some(srs) => srs.to_c_hsrs(), None => null_mut(), }; // The C function takes `char **papszOptions` without mention of `const`, and this is diff --git a/src/spatial_ref/mod.rs b/src/spatial_ref/mod.rs index 04e09346e..5a0aa9755 100644 --- a/src/spatial_ref/mod.rs +++ b/src/spatial_ref/mod.rs @@ -1,11 +1,18 @@ -//! GDAL Spatial Reference System Functions +//! GDAL Spatial Reference System //! -//! +//! See: [Spatial Reference System C API](https://gdal.org/api/ogr_srs_api.html). +//! +//! See also: [OGR Coordinate Reference Systems and Coordinate Transformation Tutorial](https://gdal.org/tutorials/osr_api_tut.html) mod srs; mod transform; mod transform_opts; -pub use srs::{AxisOrientationType, SpatialRef}; +/// Axis orientation options +/// +/// See [`OGRAxisOrientation`](https://gdal.org/api/ogr_srs_api.html#_CPPv418OGRAxisOrientation). +pub type AxisOrientationType = gdal_sys::OGRAxisOrientation::Type; + +pub use srs::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 a53261807..ac05bfa9e 100644 --- a/src/spatial_ref/srs.rs +++ b/src/spatial_ref/srs.rs @@ -1,23 +1,19 @@ use crate::utils::{_last_null_pointer_err, _string}; use gdal_sys::{self, OGRErr}; -use libc::{c_char, c_int}; use std::ffi::{CStr, CString}; use std::ptr::{self}; use std::str::FromStr; use crate::errors::*; -#[derive(Debug, Clone)] -pub struct AreaOfUse { - pub west_lon_degree: f64, - pub south_lat_degree: f64, - pub east_lon_degree: f64, - pub north_lat_degree: f64, - pub name: String, -} - -pub type AxisOrientationType = gdal_sys::OGRAxisOrientation::Type; - +/// A OpenGIS Spatial Reference System definition. +/// +/// Used in geo-referencing raster and vector data, and in coordinate transformations. +/// +/// # Notes +/// * See also: [OGR Coordinate Reference Systems and Coordinate Transformation Tutorial](https://gdal.org/tutorials/osr_api_tut.html) +/// * Consult the [OGC WKT Coordinate System Issues](https://gdal.org/tutorials/wktproblems.html) +/// page for implementation details of WKT in OGR. #[derive(Debug)] pub struct SpatialRef(gdal_sys::OGRSpatialReferenceH); @@ -63,11 +59,21 @@ impl SpatialRef { } } - /// Get the handle to underlying C API data - pub(crate) unsafe fn c_handle(&self) -> gdal_sys::OGRSpatialReferenceH { + /// Returns a C pointer to the allocated [`gdal_sys::OGRSpatialReferenceH`] memory. + /// + /// # Safety + /// This method returns a raw C pointer + pub unsafe fn to_c_hsrs(&self) -> gdal_sys::OGRSpatialReferenceH { self.0 } + /// Set spatial reference from various text formats. + /// + /// This method will examine the provided input, and try to deduce the format, + /// and then use it to initialize the spatial reference system. See the [C++ API docs][CPP] + /// for details on these forms. + /// + /// [CPP]: https://gdal.org/api/ogrspatialref.html#_CPPv4N19OGRSpatialReference16SetFromUserInputEPKc pub fn from_definition(definition: &str) -> Result { let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(ptr::null()) }; if c_obj.is_null() { @@ -96,7 +102,7 @@ impl SpatialRef { pub fn from_epsg(epsg_code: u32) -> Result { let null_ptr = ptr::null_mut(); let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(null_ptr) }; - let rv = unsafe { gdal_sys::OSRImportFromEPSG(c_obj, epsg_code as c_int) }; + let rv = unsafe { gdal_sys::OSRImportFromEPSG(c_obj, epsg_code as libc::c_int) }; if rv != OGRErr::OGRERR_NONE { Err(GdalError::OgrError { err: rv, @@ -124,7 +130,7 @@ impl SpatialRef { pub fn from_esri(esri_wkt: &str) -> Result { let c_str = CString::new(esri_wkt)?; - let mut ptrs = vec![c_str.as_ptr() as *mut c_char, ptr::null_mut()]; + let mut ptrs = vec![c_str.as_ptr() as *mut libc::c_char, ptr::null_mut()]; let null_ptr = ptr::null_mut(); let c_obj = unsafe { gdal_sys::OSRNewSpatialReference(null_ptr) }; let rv = unsafe { gdal_sys::OSRImportFromESRI(c_obj, ptrs.as_mut_ptr()) }; @@ -166,7 +172,8 @@ impl SpatialRef { pub fn to_pretty_wkt(&self) -> Result { let mut c_wkt = ptr::null_mut(); - let rv = unsafe { gdal_sys::OSRExportToPrettyWkt(self.0, &mut c_wkt, false as c_int) }; + let rv = + unsafe { gdal_sys::OSRExportToPrettyWkt(self.0, &mut c_wkt, false as libc::c_int) }; let res = if rv != OGRErr::OGRERR_NONE { Err(GdalError::OgrError { err: rv, @@ -348,13 +355,17 @@ impl SpatialRef { unsafe { gdal_sys::OSRIsVertical(self.0) == 1 } } - pub fn axis_orientation(&self, target_key: &str, axis: i32) -> Result { + pub fn axis_orientation( + &self, + target_key: &str, + axis: i32, + ) -> Result { let mut orientation = gdal_sys::OGRAxisOrientation::OAO_Other; let c_ptr = unsafe { gdal_sys::OSRGetAxis( self.0, CString::new(target_key)?.as_ptr(), - axis as c_int, + axis as libc::c_int, &mut orientation, ) }; @@ -375,7 +386,7 @@ impl SpatialRef { gdal_sys::OSRGetAxis( self.0, CString::new(target_key)?.as_ptr(), - axis as c_int, + axis as libc::c_int, ptr::null_mut(), ) }; @@ -413,6 +424,9 @@ impl SpatialRef { } #[cfg(major_ge_3)] + /// Get the valid use bounding area for this `SpatialRef`. + /// + /// See: [`OSRGetAreaOfUse`](https://gdal.org/api/ogr_srs_api.html#_CPPv415OSRGetAreaOfUse20OGRSpatialReferenceHPdPdPdPdPPKc) pub fn area_of_use(&self) -> Option { let mut c_area_name: *const libc::c_char = ptr::null_mut(); let (mut w_long, mut s_lat, mut e_long, mut n_lat): (f64, f64, f64, f64) = @@ -442,6 +456,18 @@ impl SpatialRef { } } +#[derive(Debug, Clone)] +/// Defines the bounding area of valid use for a [`SpatialRef`]. +/// +/// See [`area_of_use`][SpatialRef::area_of_use]. +pub struct AreaOfUse { + pub west_lon_degree: f64, + pub south_lat_degree: f64, + pub east_lon_degree: f64, + pub north_lat_degree: f64, + pub name: String, +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/spatial_ref/transform.rs b/src/spatial_ref/transform.rs index 0f72aab1d..7b1bbd90e 100644 --- a/src/spatial_ref/transform.rs +++ b/src/spatial_ref/transform.rs @@ -1,13 +1,13 @@ -use gdal_sys::{CPLErr, OGRCoordinateTransformationH}; -use libc::c_int; -use std::ptr::null_mut; use crate::errors; use crate::errors::GdalError; use crate::spatial_ref::{CoordTransformOptions, SpatialRef}; use crate::utils::{_last_cpl_err, _last_null_pointer_err}; +use gdal_sys::{CPLErr, OGRCoordinateTransformationH}; +use libc::c_int; +use std::ptr::null_mut; #[derive(Debug)] -/// Defines a coordinate transformation from one SRS to another. +/// Defines a coordinate transformation from one [`SpatialRef`] to another. pub struct CoordTransform { inner: OGRCoordinateTransformationH, from: String, @@ -21,8 +21,13 @@ impl Drop for CoordTransform { } impl CoordTransform { + /// Constructs a new transformation from `source` to `target`. + /// + /// See: [OCTNewCoordinateTransformation](https://gdal.org/api/ogr_srs_api.html#_CPPv430OCTNewCoordinateTransformation20OGRSpatialReferenceH20OGRSpatialReferenceH) pub fn new(source: &SpatialRef, target: &SpatialRef) -> errors::Result { - let c_obj = unsafe { gdal_sys::OCTNewCoordinateTransformation(source.c_handle(), target.c_handle()) }; + let c_obj = unsafe { + gdal_sys::OCTNewCoordinateTransformation(source.to_c_hsrs(), target.to_c_hsrs()) + }; if c_obj.is_null() { return Err(_last_null_pointer_err("OCTNewCoordinateTransformation")); } @@ -33,13 +38,21 @@ impl CoordTransform { }) } + /// Constructs a new transformation from `source` to `target` with additional extended options + /// defined by `options`: [`CoordTransformOptions`]. + /// + /// See: [OCTNewCoordinateTransformation](https://gdal.org/api/ogr_srs_api.html#_CPPv432OCTNewCoordinateTransformationEx20OGRSpatialReferenceH20OGRSpatialReferenceH35OGRCoordinateTransformationOptionsH) pub fn new_with_options( source: &SpatialRef, target: &SpatialRef, options: &CoordTransformOptions, ) -> errors::Result { let c_obj = unsafe { - gdal_sys::OCTNewCoordinateTransformationEx(source.c_handle(), target.c_handle(), options.c_options()) + gdal_sys::OCTNewCoordinateTransformationEx( + source.to_c_hsrs(), + target.to_c_hsrs(), + options.c_options(), + ) }; if c_obj.is_null() { return Err(_last_null_pointer_err("OCTNewCoordinateTransformation")); @@ -56,15 +69,21 @@ impl CoordTransform { /// /// # Arguments /// * `bounds` - array of [axis0_min, axis1_min, axis0_max, axis1_max], - /// interpreted in the axis order of the source SpatialRef, - /// typically [xmin, ymin, xmax, ymax] + /// interpreted in the axis order of the source SpatialRef, + /// typically [xmin, ymin, xmax, ymax] /// * `densify_pts` - number of points per edge (recommended: 21) /// /// # Returns /// `Ok([f64; 4])` with bounds in axis order of target SpatialRef /// `Err` if there is an error. + /// + /// See: [OCTTransformBounds](https://gdal.org/api/ogr_srs_api.html#_CPPv418OCTTransformBounds28OGRCoordinateTransformationHKdKdKdKdPdPdPdPdKi) #[cfg(all(major_ge_3, minor_ge_4))] - pub fn transform_bounds(&self, bounds: &[f64; 4], densify_pts: i32) -> errors::Result<[f64; 4]> { + pub fn transform_bounds( + &self, + bounds: &[f64; 4], + densify_pts: i32, + ) -> errors::Result<[f64; 4]> { let mut out_xmin: f64 = 0.; let mut out_ymin: f64 = 0.; let mut out_xmax: f64 = 0.; @@ -109,7 +128,14 @@ impl CoordTransform { /// * `x` - slice of x coordinates /// * `y` - slice of y coordinates (must match x in length) /// * `z` - slice of z coordinates, or an empty slice to ignore - pub fn transform_coords(&self, x: &mut [f64], y: &mut [f64], z: &mut [f64]) -> errors::Result<()> { + /// + /// See: [OCTTransform](https://gdal.org/api/ogr_srs_api.html#_CPPv412OCTTransform28OGRCoordinateTransformationHiPdPdPd) + pub fn transform_coords( + &self, + x: &mut [f64], + y: &mut [f64], + z: &mut [f64], + ) -> errors::Result<()> { let nb_coords = x.len(); assert_eq!( nb_coords, @@ -166,16 +192,20 @@ impl CoordTransform { .expect("Coordinate transform failed") } - pub fn to_c_hct(&self) -> OGRCoordinateTransformationH { + /// Returns a C pointer to the allocated [`gdal_sys::OGRCoordinateTransformationH`] memory. + /// + /// # Safety + /// This method returns a raw C pointer + pub unsafe fn to_c_hct(&self) -> OGRCoordinateTransformationH { self.inner } } #[cfg(test)] mod tests { + use super::*; use crate::assert_almost_eq; use crate::vector::Geometry; - use super::*; #[cfg(all(major_ge_3, minor_ge_4))] #[test] @@ -346,4 +376,4 @@ mod tests { panic!("Wrong error type"); } } -} \ No newline at end of file +} diff --git a/src/spatial_ref/transform_opts.rs b/src/spatial_ref/transform_opts.rs index 20df6a700..8977b7198 100644 --- a/src/spatial_ref/transform_opts.rs +++ b/src/spatial_ref/transform_opts.rs @@ -45,8 +45,8 @@ impl CoordTransformOptions { /// The west longitude is generally lower than the east longitude, except for areas of interest /// that go across the anti-meridian. /// - /// For more information, see: - /// + /// For more information, see + /// [Advanced Coordinate Transformation Tutorial](https://gdal.org/tutorials/osr_api_tut.html#advanced-coordinate-transformation). /// /// # Arguments /// @@ -101,8 +101,9 @@ impl CoordTransformOptions { /// Sets whether ballpark transformations are allowed. /// - /// By default, PROJ may generate "ballpark transformations" (see - /// ) when precise datum transformations are missing. For high + /// By default, PROJ may generate "ballpark transformations" + /// (see [Glossary](https://proj.org/glossary.html)) + /// when precise datum transformations are missing. For high /// accuracy use cases, such transformations might not be allowed. /// /// If this option is specified with PROJ < 8, the `OGR_CT_OP_SELECTION` configuration option @@ -131,8 +132,8 @@ impl CoordTransformOptions { /// string starting with `+proj=pipeline`), a WKT2 string describing a `CoordinateOperation`, /// or a `"urn:ogc:def:coordinateOperation:EPSG::XXXX"` URN. /// - /// For more information, see: - /// + /// For more information, see + /// [Advanced Coordinate Transformation Tutorial](https://gdal.org/tutorials/osr_api_tut.html#advanced-coordinate-transformation). /// /// # Arguments /// @@ -173,7 +174,7 @@ mod tests { options.set_ballpark_allowed(false).unwrap(); let trafo = CoordTransform::new_with_options(&ma, &nl, &options); let err = trafo.unwrap_err(); - assert!(matches!(err, GdalError::NullPointer { .. }), "{:?}", err); + assert!(matches!(err, GdalError::NullPointer { .. }), "{err:?}"); } #[test] @@ -189,4 +190,4 @@ mod tests { let trafo = CoordTransform::new_with_options(&nad27, &wgs84, &options); assert!(trafo.is_ok()); } -} \ No newline at end of file +} diff --git a/src/vector/geometry.rs b/src/vector/geometry.rs index 227d7aa8f..49a42f0d1 100644 --- a/src/vector/geometry.rs +++ b/src/vector/geometry.rs @@ -292,7 +292,7 @@ impl Geometry { pub fn set_spatial_ref(&mut self, spatial_ref: SpatialRef) { unsafe { - gdal_sys::OGR_G_AssignSpatialReference(self.c_geometry(), spatial_ref.c_handle()) + gdal_sys::OGR_G_AssignSpatialReference(self.c_geometry(), spatial_ref.to_c_hsrs()) }; } diff --git a/src/vector/layer.rs b/src/vector/layer.rs index 4b1e26ff3..84ba9b381 100644 --- a/src/vector/layer.rs +++ b/src/vector/layer.rs @@ -462,7 +462,9 @@ pub trait LayerAccess: Sized { /// Read batches of columnar [Arrow](https://arrow.apache.org/) data from OGR. /// - /// Extended options are available via [`CslStringList`]. As defined in the OGR documentation for [`GetArrowStream`](https://gdal.org/api/ogrlayer_cpp.html#_CPPv4N8OGRLayer14GetArrowStreamEP16ArrowArrayStream12CSLConstList), the current options are: + /// Extended options are available via [`crate::cpl::CslStringList`]. + /// As defined in the OGR documentation for [`GetArrowStream`](https://gdal.org/api/ogrlayer_cpp.html#_CPPv4N8OGRLayer14GetArrowStreamEP16ArrowArrayStream12CSLConstList), + /// the current options are: /// /// * `INCLUDE_FID=YES/NO`. Whether to include the FID column. Defaults to YES. /// * `MAX_FEATURES_IN_BATCH=integer`. Maximum number of features to retrieve in a ArrowArray batch. Defaults to 65 536. diff --git a/src/vector/ops/transformations.rs b/src/vector/ops/transformations.rs index 25fa6ce95..e41e701aa 100644 --- a/src/vector/ops/transformations.rs +++ b/src/vector/ops/transformations.rs @@ -2,8 +2,8 @@ use gdal_sys::OGRErr; use crate::cpl::CslStringList; use crate::errors::{GdalError, Result}; -use crate::spatial_ref::SpatialRef; use crate::spatial_ref::CoordTransform; +use crate::spatial_ref::SpatialRef; use crate::utils::_last_null_pointer_err; use crate::vector::Geometry; @@ -44,7 +44,7 @@ impl Geometry { /// /// See: [`OGR_G_TransformTo`](https://gdal.org/api/vector_c_api.html#_CPPv417OGR_G_TransformTo12OGRGeometryH20OGRSpatialReferenceH) pub fn transform_to_inplace(&mut self, spatial_ref: &SpatialRef) -> Result<()> { - let rv = unsafe { gdal_sys::OGR_G_TransformTo(self.c_geometry(), spatial_ref.c_handle()) }; + let rv = unsafe { gdal_sys::OGR_G_TransformTo(self.c_geometry(), spatial_ref.to_c_hsrs()) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { err: rv, @@ -59,7 +59,7 @@ impl Geometry { /// See: [`OGR_G_TransformTo`](https://gdal.org/api/vector_c_api.html#_CPPv417OGR_G_TransformTo12OGRGeometryH20OGRSpatialReferenceH) pub fn transform_to(&self, spatial_ref: &SpatialRef) -> Result { let new_c_geom = unsafe { gdal_sys::OGR_G_Clone(self.c_geometry()) }; - let rv = unsafe { gdal_sys::OGR_G_TransformTo(new_c_geom, spatial_ref.c_handle()) }; + let rv = unsafe { gdal_sys::OGR_G_TransformTo(new_c_geom, spatial_ref.to_c_hsrs()) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { err: rv,