diff --git a/CHANGES.md b/CHANGES.md index ae3861f9d..e3f6851d1 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -11,6 +11,10 @@ - +- **Breaking**: Add `gdal::vector::OwnedLayer`, `gdal::vector::LayerAccess` and `gdal::vector::layer::OwnedFeatureIterator`. This requires importing `gdal::vector::LayerAccess` for using most vector layer methods. + + - https://github.com/georust/gdal/pull/238 + ## 0.12 - Bump Rust edition to 2021 diff --git a/examples/read_write_ogr_datetime.rs b/examples/read_write_ogr_datetime.rs index 4bc7e10ef..626565942 100644 --- a/examples/read_write_ogr_datetime.rs +++ b/examples/read_write_ogr_datetime.rs @@ -1,3 +1,5 @@ +use gdal::vector::LayerAccess; + fn run() -> gdal::errors::Result<()> { use chrono::Duration; use gdal::vector::{Defn, Feature, FieldDefn, FieldValue}; diff --git a/examples/write_ogr.rs b/examples/write_ogr.rs index 7f0a9a898..60c15f73b 100644 --- a/examples/write_ogr.rs +++ b/examples/write_ogr.rs @@ -1,5 +1,5 @@ use gdal::errors::Result; -use gdal::vector::{Defn, Feature, FieldDefn, FieldValue, Geometry, OGRFieldType}; +use gdal::vector::{Defn, Feature, FieldDefn, FieldValue, Geometry, LayerAccess, OGRFieldType}; use gdal::Driver; use std::fs; diff --git a/src/dataset.rs b/src/dataset.rs index d10ae0971..75b4e1d15 100644 --- a/src/dataset.rs +++ b/src/dataset.rs @@ -12,8 +12,7 @@ use crate::cpl::CslStringList; use crate::errors::*; use crate::raster::RasterCreationOption; use crate::utils::{_last_cpl_err, _last_null_pointer_err, _path_to_c_string, _string}; -use crate::vector::sql; -use crate::vector::Geometry; +use crate::vector::{sql, Geometry, OwnedLayer}; use crate::{ gdal_major_object::MajorObject, raster::RasterBand, spatial_ref::SpatialRef, vector::Layer, Driver, Metadata, @@ -453,6 +452,10 @@ impl Dataset { unsafe { Layer::from_c_layer(self, c_layer) } } + fn into_child_layer(self, c_layer: OGRLayerH) -> OwnedLayer { + unsafe { OwnedLayer::from_c_layer(self, c_layer) } + } + /// Get the number of layers in this dataset. pub fn layer_count(&self) -> isize { (unsafe { gdal_sys::OGR_DS_GetLayerCount(self.c_dataset) }) as isize @@ -470,6 +473,18 @@ impl Dataset { Ok(self.child_layer(c_layer)) } + /// Fetch a layer by index. + /// + /// Applies to vector datasets, and fetches by the given + /// _0-based_ index. + pub fn into_layer(self, idx: isize) -> Result { + let c_layer = unsafe { gdal_sys::OGR_DS_GetLayer(self.c_dataset, idx as c_int) }; + if c_layer.is_null() { + return Err(_last_null_pointer_err("OGR_DS_GetLayer")); + } + Ok(self.into_child_layer(c_layer)) + } + /// Fetch a layer by name. pub fn layer_by_name(&self, name: &str) -> Result { let c_name = CString::new(name)?; @@ -480,6 +495,16 @@ impl Dataset { Ok(self.child_layer(c_layer)) } + /// Fetch a layer by name. + pub fn into_layer_by_name(self, name: &str) -> Result { + let c_name = CString::new(name)?; + let c_layer = unsafe { gdal_sys::OGR_DS_GetLayerByName(self.c_dataset(), c_name.as_ptr()) }; + if c_layer.is_null() { + return Err(_last_null_pointer_err("OGR_DS_GetLayerByName")); + } + Ok(self.into_child_layer(c_layer)) + } + /// Returns an iterator over the layers of the dataset. pub fn layers(&self) -> LayerIterator { LayerIterator::with_dataset(self) @@ -648,6 +673,7 @@ impl Dataset { /// /// ``` /// # use gdal::{Dataset, LayerOptions}; + /// # use gdal::vector::LayerAccess; /// # /// fn create_point_grid(dataset: &mut Dataset) -> gdal::errors::Result<()> { /// use gdal::vector::Geometry; @@ -713,6 +739,7 @@ impl Dataset { /// # use gdal::Dataset; /// # use std::path::Path; /// use gdal::vector::sql; + /// use gdal::vector::LayerAccess; /// /// let ds = Dataset::open(Path::new("fixtures/roads.geojson")).unwrap(); /// let query = "SELECT kind, is_bridge, highway FROM roads WHERE highway = 'pedestrian'"; @@ -933,7 +960,7 @@ impl<'a> Drop for Transaction<'a> { #[cfg(test)] mod tests { use super::*; - use crate::vector::Geometry; + use crate::vector::{Geometry, LayerAccess}; use tempfile::TempPath; macro_rules! fixture { diff --git a/src/lib.rs b/src/lib.rs index e01ab40ff..5fc0decd7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,7 @@ //! ``` //! use std::path::Path; //! use gdal::Dataset; +//! use gdal::vector::LayerAccess; //! //! let dataset = Dataset::open(Path::new("fixtures/roads.geojson")).unwrap(); //! let mut layer = dataset.layer(0).unwrap(); diff --git a/src/vector/defn.rs b/src/vector/defn.rs index 797e86d46..5aca09da7 100644 --- a/src/vector/defn.rs +++ b/src/vector/defn.rs @@ -1,6 +1,6 @@ use crate::spatial_ref::SpatialRef; use crate::utils::{_last_null_pointer_err, _string}; -use crate::vector::layer::Layer; +use crate::vector::LayerAccess; use gdal_sys::{ self, OGRFeatureDefnH, OGRFieldDefnH, OGRFieldType, OGRGeomFieldDefnH, OGRwkbGeometryType, }; @@ -55,7 +55,7 @@ impl Defn { } } - pub fn from_layer(lyr: &Layer) -> Defn { + pub fn from_layer(lyr: &L) -> Defn { let c_defn = unsafe { gdal_sys::OGR_L_GetLayerDefn(lyr.c_layer()) }; Defn { c_defn } } diff --git a/src/vector/feature.rs b/src/vector/feature.rs index 1df2e44bb..e71d19d3e 100644 --- a/src/vector/feature.rs +++ b/src/vector/feature.rs @@ -1,7 +1,6 @@ use crate::utils::{_last_null_pointer_err, _string, _string_array}; use crate::vector::geometry::Geometry; -use crate::vector::layer::Layer; -use crate::vector::Defn; +use crate::vector::{Defn, LayerAccess}; use gdal_sys::{self, OGRErr, OGRFeatureH, OGRFieldType}; use libc::c_longlong; use libc::{c_double, c_int}; @@ -487,7 +486,7 @@ impl<'a> Feature<'a> { Ok(&self.geometry[idx]) } - pub fn create(&self, lyr: &Layer) -> Result<()> { + pub fn create(&self, lyr: &L) -> Result<()> { let rv = unsafe { gdal_sys::OGR_L_CreateFeature(lyr.c_layer(), self.c_feature) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { diff --git a/src/vector/layer.rs b/src/vector/layer.rs index cf162b5ea..28137a3e9 100644 --- a/src/vector/layer.rs +++ b/src/vector/layer.rs @@ -86,6 +86,7 @@ impl LayerCaps { /// ``` /// use std::path::Path; /// use gdal::Dataset; +/// use gdal::vector::LayerAccess; /// /// let dataset = Dataset::open(Path::new("fixtures/roads.geojson")).unwrap(); /// let mut layer = dataset.layer(0).unwrap(); @@ -108,28 +109,120 @@ impl<'a> MajorObject for Layer<'a> { impl<'a> Metadata for Layer<'a> {} +impl<'a> LayerAccess for Layer<'a> { + unsafe fn c_layer(&self) -> OGRLayerH { + self.c_layer + } + + fn defn(&self) -> &Defn { + &self.defn + } +} + impl<'a> Layer<'a> { /// Creates a new Layer from a GDAL layer pointer /// /// # Safety /// This method operates on a raw C pointer - pub unsafe fn from_c_layer(_: &'a Dataset, c_layer: OGRLayerH) -> Layer<'a> { + pub(crate) unsafe fn from_c_layer(_: &'a Dataset, c_layer: OGRLayerH) -> Self { let c_defn = gdal_sys::OGR_L_GetLayerDefn(c_layer); let defn = Defn::from_c_defn(c_defn); - Layer { + Self { c_layer, defn, phantom: PhantomData, } } +} + +/// Layer in a vector dataset +/// +/// ``` +/// use std::path::Path; +/// use gdal::Dataset; +/// use gdal::vector::LayerAccess; +/// +/// let dataset = Dataset::open(Path::new("fixtures/roads.geojson")).unwrap(); +/// let mut layer = dataset.into_layer(0).unwrap(); +/// for feature in layer.features() { +/// // do something with each feature +/// } +/// ``` +#[derive(Debug)] +pub struct OwnedLayer { + c_layer: OGRLayerH, + defn: Defn, + // we store the dataset to prevent dropping (i.e. closing) it + _dataset: Dataset, +} + +impl MajorObject for OwnedLayer { + unsafe fn gdal_object_ptr(&self) -> GDALMajorObjectH { + self.c_layer + } +} + +impl Metadata for OwnedLayer {} + +impl LayerAccess for OwnedLayer { + unsafe fn c_layer(&self) -> OGRLayerH { + self.c_layer + } + + fn defn(&self) -> &Defn { + &self.defn + } +} + +impl OwnedLayer { + /// Creates a new Layer from a GDAL layer pointer + /// + /// # Safety + /// This method operates on a raw C pointer + pub(crate) unsafe fn from_c_layer(dataset: Dataset, c_layer: OGRLayerH) -> Self { + let c_defn = gdal_sys::OGR_L_GetLayerDefn(c_layer); + let defn = Defn::from_c_defn(c_defn); + Self { + c_layer, + defn, + _dataset: dataset, + } + } + /// Returns iterator over the features in this layer. + /// + /// **Note.** This method resets the current index to + /// the beginning before iteration. It also borrows the + /// layer mutably, preventing any overlapping borrows. + pub fn owned_features(mut self) -> OwnedFeatureIterator { + self.reset_feature_reading(); + OwnedFeatureIterator::_with_layer(self) + } + + /// Returns the `Dataset` this layer belongs to and consumes this layer. + pub fn into_dataset(self) -> Dataset { + self._dataset + } +} + +/// As long we have a 1:1 mapping between a dataset and a layer, it is `Send`. +/// We cannot allow a layer to be send, when two or more access (and modify) the same `Dataset`. +unsafe impl Send for OwnedLayer {} + +impl From for Dataset { + fn from(owned_layer: OwnedLayer) -> Self { + owned_layer.into_dataset() + } +} + +pub trait LayerAccess: Sized { /// Returns the C wrapped pointer /// /// # Safety /// This method returns a raw C pointer - pub unsafe fn c_layer(&self) -> OGRLayerH { - self.c_layer - } + unsafe fn c_layer(&self) -> OGRLayerH; + + fn defn(&self) -> &Defn; /// Returns the feature with the given feature id `fid`, or `None` if not found. /// @@ -138,8 +231,8 @@ impl<'a> Layer<'a> { /// Not all drivers support this efficiently; however, the call should always work if the /// feature exists, as a fallback implementation just scans all the features in the layer /// looking for the desired feature. - pub fn feature(&self, fid: u64) -> Option { - let c_feature = unsafe { gdal_sys::OGR_L_GetFeature(self.c_layer, fid as i64) }; + fn feature(&self, fid: u64) -> Option { + let c_feature = unsafe { gdal_sys::OGR_L_GetFeature(self.c_layer(), fid as i64) }; if c_feature.is_null() { None } else { @@ -152,7 +245,7 @@ impl<'a> Layer<'a> { /// **Note.** This method resets the current index to /// the beginning before iteration. It also borrows the /// layer mutably, preventing any overlapping borrows. - pub fn features(&mut self) -> FeatureIterator { + fn features(&mut self) -> FeatureIterator { self.reset_feature_reading(); FeatureIterator::_with_layer(self) } @@ -160,45 +253,41 @@ impl<'a> Layer<'a> { /// Set a spatial filter on this layer. /// /// Refer [OGR_L_SetSpatialFilter](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) - pub fn set_spatial_filter(&mut self, geometry: &Geometry) { - unsafe { gdal_sys::OGR_L_SetSpatialFilter(self.c_layer, geometry.c_geometry()) }; + fn set_spatial_filter(&mut self, geometry: &Geometry) { + unsafe { gdal_sys::OGR_L_SetSpatialFilter(self.c_layer(), geometry.c_geometry()) }; } /// Set a spatial rectangle filter on this layer by specifying the bounds of a rectangle. - pub fn set_spatial_filter_rect(&mut self, min_x: f64, min_y: f64, max_x: f64, max_y: f64) { - unsafe { gdal_sys::OGR_L_SetSpatialFilterRect(self.c_layer, min_x, min_y, max_x, max_y) }; + fn set_spatial_filter_rect(&mut self, min_x: f64, min_y: f64, max_x: f64, max_y: f64) { + unsafe { gdal_sys::OGR_L_SetSpatialFilterRect(self.c_layer(), min_x, min_y, max_x, max_y) }; } /// Clear spatial filters set on this layer. - pub fn clear_spatial_filter(&mut self) { - unsafe { gdal_sys::OGR_L_SetSpatialFilter(self.c_layer, null_mut()) }; + fn clear_spatial_filter(&mut self) { + unsafe { gdal_sys::OGR_L_SetSpatialFilter(self.c_layer(), null_mut()) }; } /// Get the name of this layer. - pub fn name(&self) -> String { - let rv = unsafe { gdal_sys::OGR_L_GetName(self.c_layer) }; + fn name(&self) -> String { + let rv = unsafe { gdal_sys::OGR_L_GetName(self.c_layer()) }; _string(rv) } - pub fn has_capability(&self, capability: LayerCaps) -> bool { + fn has_capability(&self, capability: LayerCaps) -> bool { unsafe { - gdal_sys::OGR_L_TestCapability(self.c_layer, capability.into_cstring().as_ptr()) == 1 + gdal_sys::OGR_L_TestCapability(self.c_layer(), capability.into_cstring().as_ptr()) == 1 } } - pub fn defn(&self) -> &Defn { - &self.defn - } - - pub fn create_defn_fields(&self, fields_def: &[(&str, OGRFieldType::Type)]) -> Result<()> { + fn create_defn_fields(&self, fields_def: &[(&str, OGRFieldType::Type)]) -> Result<()> { for fd in fields_def { let fdefn = FieldDefn::new(fd.0, fd.1)?; fdefn.add_to_layer(self)?; } Ok(()) } - pub fn create_feature(&mut self, geometry: Geometry) -> Result<()> { - let feature = Feature::new(&self.defn)?; + fn create_feature(&mut self, geometry: Geometry) -> Result<()> { + let feature = Feature::new(self.defn())?; let c_geometry = unsafe { geometry.into_c_geometry() }; let rv = unsafe { gdal_sys::OGR_F_SetGeometryDirectly(feature.c_feature(), c_geometry) }; @@ -208,7 +297,7 @@ impl<'a> Layer<'a> { method_name: "OGR_F_SetGeometryDirectly", }); } - let rv = unsafe { gdal_sys::OGR_L_CreateFeature(self.c_layer, feature.c_feature()) }; + let rv = unsafe { gdal_sys::OGR_L_CreateFeature(self.c_layer(), feature.c_feature()) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { err: rv, @@ -218,13 +307,13 @@ impl<'a> Layer<'a> { Ok(()) } - pub fn create_feature_fields( + fn create_feature_fields( &mut self, geometry: Geometry, field_names: &[&str], values: &[FieldValue], ) -> Result<()> { - let mut ft = Feature::new(&self.defn)?; + let mut ft = Feature::new(self.defn())?; ft.set_geometry(geometry)?; for (fd, val) in field_names.iter().zip(values.iter()) { ft.set_field(fd, val)?; @@ -239,8 +328,8 @@ impl<'a> Layer<'a> { /// /// The returned count takes the [spatial filter](`Layer::set_spatial_filter`) into account. /// For dynamic databases the count may not be exact. - pub fn feature_count(&self) -> u64 { - (unsafe { gdal_sys::OGR_L_GetFeatureCount(self.c_layer, 1) }) as u64 + fn feature_count(&self) -> u64 { + (unsafe { gdal_sys::OGR_L_GetFeatureCount(self.c_layer(), 1) }) as u64 } /// Returns the number of features in this layer, if it is possible to compute this @@ -251,8 +340,8 @@ impl<'a> Layer<'a> { /// /// The returned count takes the [spatial filter](`Layer::set_spatial_filter`) into account. /// For dynamic databases the count may not be exact. - pub fn try_feature_count(&self) -> Option { - let rv = unsafe { gdal_sys::OGR_L_GetFeatureCount(self.c_layer, 0) }; + fn try_feature_count(&self) -> Option { + let rv = unsafe { gdal_sys::OGR_L_GetFeatureCount(self.c_layer(), 0) }; if rv < 0 { None } else { @@ -271,7 +360,7 @@ impl<'a> Layer<'a> { /// /// Layers without any geometry may return [`OGRErr::OGRERR_FAILURE`] to indicate that no /// meaningful extents could be collected. - pub fn get_extent(&self) -> Result { + fn get_extent(&self) -> Result { let mut envelope = OGREnvelope { MinX: 0.0, MaxX: 0.0, @@ -279,7 +368,7 @@ impl<'a> Layer<'a> { MaxY: 0.0, }; let force = 1; - let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer, &mut envelope, force) }; + let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), &mut envelope, force) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { err: rv, @@ -298,7 +387,7 @@ impl<'a> Layer<'a> { /// Depending on the driver, the returned extent may or may not take the [spatial /// filter](`Layer::set_spatial_filter`) into account. So it is safer to call `try_get_extent` /// without setting a spatial filter. - pub fn try_get_extent(&self) -> Result> { + fn try_get_extent(&self) -> Result> { let mut envelope = OGREnvelope { MinX: 0.0, MaxX: 0.0, @@ -306,7 +395,7 @@ impl<'a> Layer<'a> { MaxY: 0.0, }; let force = 0; - let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer, &mut envelope, force) }; + let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), &mut envelope, force) }; if rv == OGRErr::OGRERR_FAILURE { Ok(None) } else { @@ -323,8 +412,8 @@ impl<'a> Layer<'a> { /// Fetch the spatial reference system for this layer. /// /// Refer [OGR_L_GetSpatialRef](https://gdal.org/doxygen/classOGRLayer.html#a75c06b4993f8eb76b569f37365cd19ab) - pub fn spatial_ref(&self) -> Result { - let c_obj = unsafe { gdal_sys::OGR_L_GetSpatialRef(self.c_layer) }; + fn spatial_ref(&self) -> Result { + let c_obj = unsafe { gdal_sys::OGR_L_GetSpatialRef(self.c_layer()) }; if c_obj.is_null() { return Err(_last_null_pointer_err("OGR_L_GetSpatialRef")); } @@ -333,7 +422,7 @@ impl<'a> Layer<'a> { fn reset_feature_reading(&mut self) { unsafe { - gdal_sys::OGR_L_ResetReading(self.c_layer); + gdal_sys::OGR_L_ResetReading(self.c_layer()); } } @@ -344,9 +433,9 @@ impl<'a> Layer<'a> { /// Parameters: /// - `query` in restricted SQL WHERE format /// - pub fn set_attribute_filter(&mut self, query: &str) -> Result<()> { + fn set_attribute_filter(&mut self, query: &str) -> Result<()> { let c_str = CString::new(query)?; - let rv = unsafe { gdal_sys::OGR_L_SetAttributeFilter(self.c_layer, c_str.as_ptr()) }; + let rv = unsafe { gdal_sys::OGR_L_SetAttributeFilter(self.c_layer(), c_str.as_ptr()) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { @@ -362,9 +451,9 @@ impl<'a> Layer<'a> { /// /// From the GDAL docs: Note that installing a query string will generally result in resetting the current reading position /// - pub fn clear_attribute_filter(&mut self) { + fn clear_attribute_filter(&mut self) { unsafe { - gdal_sys::OGR_L_SetAttributeFilter(self.c_layer, null_mut()); + gdal_sys::OGR_L_SetAttributeFilter(self.c_layer(), null_mut()); } } } @@ -397,20 +486,81 @@ impl<'a> Iterator for FeatureIterator<'a> { } impl<'a> FeatureIterator<'a> { - pub fn _with_layer(layer: &'a Layer) -> FeatureIterator<'a> { + pub(crate) fn _with_layer(layer: &'a L) -> Self { let defn = layer.defn(); let size_hint = layer .try_feature_count() .map(|s| s.try_into().ok()) .flatten(); - FeatureIterator { - c_layer: layer.c_layer, + Self { + c_layer: unsafe { layer.c_layer() }, size_hint, defn, } } } +pub struct OwnedFeatureIterator { + pub(crate) layer: OwnedLayer, + size_hint: Option, +} + +impl<'a> Iterator for &'a mut OwnedFeatureIterator +where + Self: 'a, +{ + type Item = Feature<'a>; + + #[inline] + fn next(&mut self) -> Option> { + let c_feature = unsafe { gdal_sys::OGR_L_GetNextFeature(self.layer.c_layer()) }; + + if c_feature.is_null() { + return None; + } + + Some(unsafe { + // We have to convince the compiler that our `Defn` adheres to our iterator lifetime `<'a>` + let defn: &'a Defn = std::mem::transmute::<&'_ _, &'a _>(self.layer.defn()); + + Feature::from_c_feature(defn, c_feature) + }) + } + + fn size_hint(&self) -> (usize, Option) { + match self.size_hint { + Some(size) => (size, Some(size)), + None => (0, None), + } + } +} + +impl OwnedFeatureIterator { + pub(crate) fn _with_layer(layer: OwnedLayer) -> Self { + let size_hint = layer + .try_feature_count() + .map(|s| s.try_into().ok()) + .flatten(); + Self { layer, size_hint } + } + + pub fn into_layer(self) -> OwnedLayer { + self.layer + } +} + +impl AsMut for OwnedFeatureIterator { + fn as_mut(&mut self) -> &mut Self { + self + } +} + +impl From for OwnedLayer { + fn from(feature_iterator: OwnedFeatureIterator) -> Self { + feature_iterator.into_layer() + } +} + pub struct FieldDefn { c_obj: OGRFieldDefnH, } @@ -442,7 +592,7 @@ impl FieldDefn { pub fn set_precision(&self, precision: i32) { unsafe { gdal_sys::OGR_Fld_SetPrecision(self.c_obj, precision as c_int) }; } - pub fn add_to_layer(&self, layer: &Layer) -> Result<()> { + pub fn add_to_layer(&self, layer: &L) -> Result<()> { let rv = unsafe { gdal_sys::OGR_L_CreateField(layer.c_layer(), self.c_obj, 1) }; if rv != OGRErr::OGRERR_NONE { return Err(GdalError::OgrError { diff --git a/src/vector/mod.rs b/src/vector/mod.rs index be94a79ca..6e27fdd70 100644 --- a/src/vector/mod.rs +++ b/src/vector/mod.rs @@ -5,6 +5,7 @@ //! ``` //! use std::path::Path; //! use gdal::Dataset; +//! use gdal::vector::LayerAccess; //! //! let dataset = Dataset::open(Path::new("fixtures/roads.geojson")).unwrap(); //! let mut layer = dataset.layer(0).unwrap(); @@ -28,7 +29,9 @@ pub use defn::{Defn, Field, FieldIterator}; pub use feature::{Feature, FieldValue, FieldValueIterator}; pub use gdal_sys::{OGRFieldType, OGRwkbGeometryType}; pub use geometry::{geometry_type_to_name, Geometry}; -pub use layer::{FeatureIterator, FieldDefn, Layer, LayerCaps}; +pub use layer::{ + FeatureIterator, FieldDefn, Layer, LayerAccess, LayerCaps, OwnedFeatureIterator, OwnedLayer, +}; pub use ops::GeometryIntersection; use crate::errors::Result; diff --git a/src/vector/sql.rs b/src/vector/sql.rs index ef7d717dd..0d5351444 100644 --- a/src/vector/sql.rs +++ b/src/vector/sql.rs @@ -2,7 +2,7 @@ use std::ops::{Deref, DerefMut}; use gdal_sys::GDALDatasetH; -use crate::vector::Layer; +use crate::vector::{Layer, LayerAccess}; /// The result of a SQL query executed by /// [`Dataset::execute_sql()`](crate::Dataset::execute_sql()). It is just a thin wrapper around a diff --git a/src/vector/vector_tests/mod.rs b/src/vector/vector_tests/mod.rs index 669bbaa28..2b7ed67b2 100644 --- a/src/vector/vector_tests/mod.rs +++ b/src/vector/vector_tests/mod.rs @@ -1,6 +1,6 @@ use super::{ - Feature, FeatureIterator, FieldValue, Geometry, Layer, LayerCaps::*, OGRFieldType, - OGRwkbGeometryType, + Feature, FeatureIterator, FieldValue, Geometry, Layer, LayerAccess, LayerCaps::*, OGRFieldType, + OGRwkbGeometryType, OwnedLayer, }; use crate::spatial_ref::SpatialRef; use crate::{assert_almost_eq, Dataset, Driver}; @@ -95,6 +95,15 @@ where f(layer); } +fn with_owned_layer(name: &str, f: F) +where + F: Fn(OwnedLayer), +{ + let ds = Dataset::open(fixture!(name)).unwrap(); + let layer = ds.into_layer(0).unwrap(); + f(layer); +} + fn with_features(name: &str, f: F) where F: Fn(FeatureIterator), @@ -161,6 +170,40 @@ mod tests { assert_eq!(layers.count(), 3); } + #[test] + fn test_owned_layers() { + let ds = Dataset::open(fixture!("three_layer_ds.s3db")).unwrap(); + + assert_eq!(ds.layer_count(), 3); + + let mut layer = ds.into_layer(0).unwrap(); + + { + let feature = layer.features().next().unwrap(); + assert_eq!(feature.field("id").unwrap(), None); + } + + // convert back to dataset + + let ds = layer.into_dataset(); + assert_eq!(ds.layer_count(), 3); + } + + #[test] + fn test_iterate_owned_features() { + with_owned_layer("roads.geojson", |layer| { + let mut features = layer.owned_features(); + + assert_eq!(features.as_mut().size_hint(), (21, Some(21))); + assert_eq!(features.count(), 21); + + // get back layer + + let layer = features.into_layer(); + assert_eq!(layer.name(), "roads"); + }); + } + #[test] fn test_fid() { with_feature("roads.geojson", 236194095, |feature| { diff --git a/src/vector/vector_tests/sql.rs b/src/vector/vector_tests/sql.rs index 34e0d1989..2655e49e5 100644 --- a/src/vector/vector_tests/sql.rs +++ b/src/vector/vector_tests/sql.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use crate::{ fixture, - vector::{sql, Geometry}, + vector::{sql, Geometry, LayerAccess}, Dataset, }; diff --git a/tests/compile-tests/01-features-aliasing-errors.rs b/tests/compile-tests/01-features-aliasing-errors.rs index eb531f124..fc4394d00 100644 --- a/tests/compile-tests/01-features-aliasing-errors.rs +++ b/tests/compile-tests/01-features-aliasing-errors.rs @@ -1,5 +1,6 @@ mod utils; +use gdal::vector::LayerAccess; use gdal::Dataset; fn main() { diff --git a/tests/compile-tests/01-features-aliasing-errors.stderr b/tests/compile-tests/01-features-aliasing-errors.stderr index 929ab8a8c..3e16db9e7 100644 --- a/tests/compile-tests/01-features-aliasing-errors.stderr +++ b/tests/compile-tests/01-features-aliasing-errors.stderr @@ -1,10 +1,10 @@ error[E0502]: cannot borrow `layer` as immutable because it is also borrowed as mutable - --> $DIR/01-features-aliasing-errors.rs:9:17 - | -8 | for _ in layer.features() { - | ---------------- - | | - | mutable borrow occurs here - | mutable borrow later used here -9 | let _ = layer.defn(); - | ^^^^^^^^^^^^ immutable borrow occurs here + --> $DIR/01-features-aliasing-errors.rs:10:17 + | +9 | for _ in layer.features() { + | ---------------- + | | + | mutable borrow occurs here + | mutable borrow later used here +10 | let _ = layer.defn(); + | ^^^^^^^^^^^^ immutable borrow occurs here