use crate::metadata::Metadata;
use crate::spatial_ref::SpatialRef;
use crate::utils::{_last_null_pointer_err, _string};
use crate::vector::defn::Defn;
use crate::vector::{Envelope, Feature, FieldValue, Geometry};
use crate::{dataset::Dataset, gdal_major_object::MajorObject};
use gdal_sys::{self, GDALMajorObjectH, OGRErr, OGRFieldDefnH, OGRFieldType, OGRLayerH};
use libc::c_int;
use std::mem::MaybeUninit;
use std::ptr::null_mut;
use std::{convert::TryInto, ffi::CString, marker::PhantomData};
use crate::errors::*;
#[allow(clippy::upper_case_acronyms)]
pub enum LayerCaps {
OLCRandomRead,
OLCSequentialWrite,
OLCRandomWrite,
OLCFastSpatialFilter,
OLCFastFeatureCount,
OLCFastGetExtent,
OLCCreateField,
OLCDeleteField,
OLCReorderFields,
OLCAlterFieldDefn,
OLCTransactions,
OLCDeleteFeature,
OLCFastSetNextByIndex,
OLCStringsAsUTF8,
OLCIgnoreFields,
OLCCreateGeomField,
OLCCurveGeometries,
OLCMeasuredGeometries,
OLCFastGetArrowStream,
}
impl LayerCaps {
fn into_cstring(self) -> CString {
CString::new(match self {
Self::OLCRandomRead => "RandomRead",
Self::OLCSequentialWrite => "SequentialWrite",
Self::OLCRandomWrite => "RandomWrite",
Self::OLCFastSpatialFilter => "FastSpatialFilter",
Self::OLCFastFeatureCount => "FastFeatureCount",
Self::OLCFastGetExtent => "FastGetExtent",
Self::OLCCreateField => "CreateField",
Self::OLCDeleteField => "DeleteField",
Self::OLCReorderFields => "ReorderFields",
Self::OLCAlterFieldDefn => "AlterFieldDefn",
Self::OLCTransactions => "Transactions",
Self::OLCDeleteFeature => "DeleteFeature",
Self::OLCFastSetNextByIndex => "FastSetNextByIndex",
Self::OLCStringsAsUTF8 => "StringsAsUTF8",
Self::OLCIgnoreFields => "IgnoreFields",
Self::OLCCreateGeomField => "CreateGeomField",
Self::OLCCurveGeometries => "CurveGeometries",
Self::OLCMeasuredGeometries => "MeasuredGeometries",
Self::OLCFastGetArrowStream => "FastGetArrowStream",
})
.unwrap()
}
}
#[derive(Debug)]
pub struct Layer<'a> {
c_layer: OGRLayerH,
defn: Defn,
phantom: PhantomData<&'a Dataset>,
}
impl<'a> MajorObject for Layer<'a> {
unsafe fn gdal_object_ptr(&self) -> GDALMajorObjectH {
self.c_layer
}
}
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> {
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);
Self {
c_layer,
defn,
phantom: PhantomData,
}
}
}
#[derive(Debug)]
pub struct OwnedLayer {
c_layer: OGRLayerH,
defn: Defn,
_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 {
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,
}
}
pub fn owned_features(mut self) -> OwnedFeatureIterator {
self.reset_feature_reading();
OwnedFeatureIterator::_with_layer(self)
}
pub fn into_dataset(self) -> Dataset {
self._dataset
}
}
unsafe impl Send for OwnedLayer {}
impl From<OwnedLayer> for Dataset {
fn from(owned_layer: OwnedLayer) -> Self {
owned_layer.into_dataset()
}
}
pub trait LayerAccess: Sized {
unsafe fn c_layer(&self) -> OGRLayerH;
fn defn(&self) -> &Defn;
fn feature(&self, fid: u64) -> Option<Feature> {
let c_feature = unsafe { gdal_sys::OGR_L_GetFeature(self.c_layer(), fid as i64) };
if c_feature.is_null() {
None
} else {
Some(unsafe { Feature::from_c_feature(self.defn(), c_feature) })
}
}
fn features(&mut self) -> FeatureIterator {
self.reset_feature_reading();
FeatureIterator::_with_layer(self)
}
fn set_feature(&self, feature: Feature) -> Result<()> {
unsafe { gdal_sys::OGR_L_SetFeature(self.c_layer(), feature.c_feature()) };
Ok(())
}
fn set_spatial_filter(&mut self, geometry: &Geometry) {
unsafe { gdal_sys::OGR_L_SetSpatialFilter(self.c_layer(), geometry.c_geometry()) };
}
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 clear_spatial_filter(&mut self) {
unsafe { gdal_sys::OGR_L_SetSpatialFilter(self.c_layer(), null_mut()) };
}
fn name(&self) -> String {
let rv = unsafe { gdal_sys::OGR_L_GetName(self.c_layer()) };
_string(rv)
}
fn has_capability(&self, capability: LayerCaps) -> bool {
unsafe {
gdal_sys::OGR_L_TestCapability(self.c_layer(), capability.into_cstring().as_ptr()) == 1
}
}
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(())
}
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) };
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "OGR_F_SetGeometryDirectly",
});
}
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,
method_name: "OGR_L_CreateFeature",
});
}
Ok(())
}
fn create_feature_fields(
&mut self,
geometry: Geometry,
field_names: &[&str],
values: &[FieldValue],
) -> Result<()> {
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)?;
}
ft.create(self)?;
Ok(())
}
fn feature_count(&self) -> u64 {
(unsafe { gdal_sys::OGR_L_GetFeatureCount(self.c_layer(), 1) }) as u64
}
fn try_feature_count(&self) -> Option<u64> {
let rv = unsafe { gdal_sys::OGR_L_GetFeatureCount(self.c_layer(), 0) };
if rv < 0 {
None
} else {
Some(rv as u64)
}
}
fn get_extent(&self) -> Result<Envelope> {
let mut envelope = MaybeUninit::uninit();
let force = 1;
let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), envelope.as_mut_ptr(), force) };
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "OGR_L_GetExtent",
});
}
Ok(unsafe { envelope.assume_init() })
}
fn try_get_extent(&self) -> Result<Option<Envelope>> {
let mut envelope = MaybeUninit::uninit();
let force = 0;
let rv = unsafe { gdal_sys::OGR_L_GetExtent(self.c_layer(), envelope.as_mut_ptr(), force) };
if rv == OGRErr::OGRERR_FAILURE {
Ok(None)
} else {
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "OGR_L_GetExtent",
});
}
Ok(Some(unsafe { envelope.assume_init() }))
}
}
fn spatial_ref(&self) -> Option<SpatialRef> {
let c_obj = unsafe { gdal_sys::OGR_L_GetSpatialRef(self.c_layer()) };
if c_obj.is_null() {
None
} else {
unsafe { SpatialRef::from_c_obj(c_obj) }.ok()
}
}
fn reset_feature_reading(&mut self) {
unsafe {
gdal_sys::OGR_L_ResetReading(self.c_layer());
}
}
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()) };
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "OGR_L_SetAttributeFilter",
});
}
Ok(())
}
fn clear_attribute_filter(&mut self) {
unsafe {
gdal_sys::OGR_L_SetAttributeFilter(self.c_layer(), null_mut());
}
}
#[cfg(any(major_ge_4, all(major_is_3, minor_ge_6)))]
unsafe fn read_arrow_stream(
&mut self,
out_stream: *mut gdal_sys::ArrowArrayStream,
options: &crate::cpl::CslStringList,
) -> Result<()> {
self.reset_feature_reading();
unsafe {
let success =
gdal_sys::OGR_L_GetArrowStream(self.c_layer(), out_stream, options.as_ptr());
if !success {
return Err(GdalError::OgrError {
err: 1,
method_name: "OGR_L_GetArrowStream",
});
}
}
Ok(())
}
}
pub struct FeatureIterator<'a> {
defn: &'a Defn,
c_layer: OGRLayerH,
size_hint: Option<usize>,
}
impl<'a> Iterator for FeatureIterator<'a> {
type Item = Feature<'a>;
#[inline]
fn next(&mut self) -> Option<Feature<'a>> {
let c_feature = unsafe { gdal_sys::OGR_L_GetNextFeature(self.c_layer) };
if c_feature.is_null() {
None
} else {
Some(unsafe { Feature::from_c_feature(self.defn, c_feature) })
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.size_hint {
Some(size) => (size, Some(size)),
None => (0, None),
}
}
}
impl<'a> FeatureIterator<'a> {
pub(crate) fn _with_layer<L: LayerAccess>(layer: &'a L) -> Self {
let defn = layer.defn();
let size_hint = layer.try_feature_count().and_then(|s| s.try_into().ok());
Self {
c_layer: unsafe { layer.c_layer() },
size_hint,
defn,
}
}
}
pub struct OwnedFeatureIterator {
pub(crate) layer: OwnedLayer,
size_hint: Option<usize>,
}
impl<'a> Iterator for &'a mut OwnedFeatureIterator
where
Self: 'a,
{
type Item = Feature<'a>;
#[inline]
fn next(&mut self) -> Option<Feature<'a>> {
let c_feature = unsafe { gdal_sys::OGR_L_GetNextFeature(self.layer.c_layer()) };
if c_feature.is_null() {
return None;
}
Some(unsafe {
let defn: &'a Defn = std::mem::transmute::<&'_ _, &'a _>(self.layer.defn());
Feature::from_c_feature(defn, c_feature)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
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().and_then(|s| s.try_into().ok());
Self { layer, size_hint }
}
pub fn into_layer(self) -> OwnedLayer {
self.layer
}
}
impl AsMut<OwnedFeatureIterator> for OwnedFeatureIterator {
fn as_mut(&mut self) -> &mut Self {
self
}
}
impl From<OwnedFeatureIterator> for OwnedLayer {
fn from(feature_iterator: OwnedFeatureIterator) -> Self {
feature_iterator.into_layer()
}
}
pub struct FieldDefn {
c_obj: OGRFieldDefnH,
}
impl Drop for FieldDefn {
fn drop(&mut self) {
unsafe { gdal_sys::OGR_Fld_Destroy(self.c_obj) };
}
}
impl MajorObject for FieldDefn {
unsafe fn gdal_object_ptr(&self) -> GDALMajorObjectH {
self.c_obj
}
}
impl FieldDefn {
pub fn new(name: &str, field_type: OGRFieldType::Type) -> Result<FieldDefn> {
let c_str = CString::new(name)?;
let c_obj = unsafe { gdal_sys::OGR_Fld_Create(c_str.as_ptr(), field_type) };
if c_obj.is_null() {
return Err(_last_null_pointer_err("OGR_Fld_Create"));
};
Ok(FieldDefn { c_obj })
}
pub fn set_width(&self, width: i32) {
unsafe { gdal_sys::OGR_Fld_SetWidth(self.c_obj, width as c_int) };
}
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<L: LayerAccess>(&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 {
err: rv,
method_name: "OGR_L_CreateFeature",
});
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{LayerCaps::*, *};
use crate::test_utils::{fixture, SuppressGDALErrorLog, TempFixture};
use crate::{assert_almost_eq, Dataset, DatasetOptions, DriverManager, GdalOpenFlags};
use gdal_sys::OGRwkbGeometryType;
fn ds_with_layer<F>(ds_name: &str, layer_name: &str, f: F)
where
F: Fn(Layer),
{
let ds = Dataset::open(fixture(ds_name)).unwrap();
let layer = ds.layer_by_name(layer_name).unwrap();
f(layer);
}
fn with_layer<F>(name: &str, f: F)
where
F: Fn(Layer),
{
let ds = Dataset::open(fixture(name)).unwrap();
let layer = ds.layer(0).unwrap();
f(layer);
}
fn with_owned_layer<F>(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<F>(name: &str, f: F)
where
F: Fn(FeatureIterator),
{
with_layer(name, |mut layer| f(layer.features()));
}
fn with_feature<F>(name: &str, fid: u64, f: F)
where
F: Fn(Feature),
{
with_layer(name, |layer| f(layer.feature(fid).unwrap()));
}
#[test]
fn test_layer_count() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
assert_eq!(ds.layer_count(), 1);
}
#[test]
fn test_many_layer_count() {
let ds = Dataset::open(fixture("three_layer_ds.s3db")).unwrap();
assert_eq!(ds.layer_count(), 3);
}
#[test]
fn test_layer_get_extent() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
let layer = ds.layer(0).unwrap();
let extent = layer.get_extent().unwrap();
assert_almost_eq(extent.MinX, 26.100768);
assert_almost_eq(extent.MaxX, 26.103515);
assert_almost_eq(extent.MinY, 44.429858);
assert_almost_eq(extent.MaxY, 44.431818);
}
#[test]
fn test_layer_try_get_extent() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
let layer = ds.layer(0).unwrap();
assert!(layer.try_get_extent().unwrap().is_none());
}
#[test]
fn test_layer_spatial_ref() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
let layer = ds.layer(0).unwrap();
let srs = layer.spatial_ref().unwrap();
assert_eq!(srs.auth_code().unwrap(), 4326);
}
#[test]
fn test_layer_capabilities() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
let layer = ds.layer(0).unwrap();
assert!(!layer.has_capability(OLCFastSpatialFilter));
assert!(layer.has_capability(OLCFastFeatureCount));
assert!(!layer.has_capability(OLCFastGetExtent));
assert!(layer.has_capability(OLCRandomRead));
assert!(layer.has_capability(OLCStringsAsUTF8));
}
#[test]
fn test_feature_count() {
with_layer("roads.geojson", |layer| {
assert_eq!(layer.feature_count(), 21);
});
}
#[test]
fn test_many_feature_count() {
ds_with_layer("three_layer_ds.s3db", "layer_0", |layer| {
assert_eq!(layer.feature_count(), 3);
});
}
#[test]
fn test_try_feature_count() {
with_layer("roads.geojson", |layer| {
assert_eq!(layer.try_feature_count(), Some(21));
});
}
#[test]
fn test_feature() {
with_layer("roads.geojson", |layer| {
assert!(layer.feature(236194095).is_some());
assert!(layer.feature(23489660).is_some());
assert!(layer.feature(0).is_none());
assert!(layer.feature(404).is_none());
});
}
#[test]
fn test_iterate_features() {
with_features("roads.geojson", |features| {
assert_eq!(features.size_hint(), (21, Some(21)));
assert_eq!(features.count(), 21);
});
}
#[test]
fn test_iterate_layers() {
let ds = Dataset::open(fixture("three_layer_ds.s3db")).unwrap();
let layers = ds.layers();
assert_eq!(layers.size_hint(), (3, Some(3)));
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);
}
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);
let layer = features.into_layer();
assert_eq!(layer.name(), "roads");
});
}
#[test]
fn test_fid() {
with_feature("roads.geojson", 236194095, |feature| {
assert_eq!(feature.fid(), Some(236194095));
});
}
#[test]
fn test_string_field() {
with_feature("roads.geojson", 236194095, |feature| {
assert_eq!(
feature.field("highway").unwrap().unwrap().into_string(),
Some("footway".to_string())
);
});
with_features("roads.geojson", |features| {
assert_eq!(
features
.filter(|field| {
let highway = field.field("highway").unwrap().unwrap().into_string();
highway == Some("residential".to_string())
})
.count(),
2
);
});
}
#[test]
fn test_null_field() {
with_features("null_feature_fields.geojson", |mut features| {
let feature = features.next().unwrap();
assert_eq!(
feature.field("some_int").unwrap(),
Some(FieldValue::IntegerValue(0))
);
assert_eq!(feature.field("some_string").unwrap(), None);
});
}
#[test]
fn test_string_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
assert_eq!(
feature.field("a_string_list").unwrap().unwrap(),
FieldValue::StringListValue(vec![
String::from("a"),
String::from("list"),
String::from("of"),
String::from("strings")
])
);
});
}
#[test]
fn test_set_string_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
let value = FieldValue::StringListValue(vec![
String::from("the"),
String::from("new"),
String::from("strings"),
]);
feature.set_field("a_string_list", &value).unwrap();
assert_eq!(feature.field("a_string_list").unwrap().unwrap(), value);
});
}
#[test]
#[allow(clippy::float_cmp)]
fn test_get_field_as_x_by_name() {
with_features("roads.geojson", |mut features| {
let feature = features.next().unwrap();
assert_eq!(
feature.field_as_string_by_name("highway").unwrap(),
Some("footway".to_owned())
);
assert_eq!(
feature.field_as_string_by_name("sort_key").unwrap(),
Some("-9".to_owned())
);
assert_eq!(
feature.field_as_integer_by_name("sort_key").unwrap(),
Some(-9)
);
assert_eq!(
feature.field_as_integer64_by_name("sort_key").unwrap(),
Some(-9)
);
assert_eq!(
feature.field_as_double_by_name("sort_key").unwrap(),
Some(-9.)
);
assert_eq!(
feature.field_as_integer_by_name("highway").unwrap(),
Some(0)
);
assert_eq!(
feature.field_as_integer64_by_name("highway").unwrap(),
Some(0)
);
assert_eq!(
feature.field_as_double_by_name("highway").unwrap(),
Some(0.)
);
assert_eq!(feature.field_as_string_by_name("railway").unwrap(), None);
assert_eq!(feature.field_as_integer_by_name("railway").unwrap(), None);
assert_eq!(feature.field_as_integer64_by_name("railway").unwrap(), None);
assert_eq!(feature.field_as_double_by_name("railway").unwrap(), None);
assert!(matches!(
feature.field_as_string_by_name("not_a_field").unwrap_err(),
GdalError::InvalidFieldName {
field_name,
method_name: "OGR_F_GetFieldIndex",
}
if field_name == "not_a_field"
));
});
}
#[test]
#[allow(clippy::float_cmp)]
fn test_get_field_as_x() {
with_features("roads.geojson", |mut features| {
let feature = features.next().unwrap();
let highway_field = 6;
let railway_field = 5;
let sort_key_field = 1;
assert_eq!(
feature.field_as_string(highway_field).unwrap(),
Some("footway".to_owned())
);
assert_eq!(
feature.field_as_string(sort_key_field).unwrap(),
Some("-9".to_owned())
);
assert_eq!(feature.field_as_integer(sort_key_field).unwrap(), Some(-9));
assert_eq!(
feature.field_as_integer64(sort_key_field).unwrap(),
Some(-9)
);
assert_eq!(feature.field_as_double(sort_key_field).unwrap(), Some(-9.));
assert_eq!(feature.field_as_integer(highway_field).unwrap(), Some(0));
assert_eq!(feature.field_as_integer64(highway_field).unwrap(), Some(0));
assert_eq!(feature.field_as_double(highway_field).unwrap(), Some(0.));
assert_eq!(feature.field_as_string(railway_field).unwrap(), None);
assert_eq!(feature.field_as_integer(railway_field).unwrap(), None);
assert_eq!(feature.field_as_integer64(railway_field).unwrap(), None);
assert_eq!(feature.field_as_double(railway_field).unwrap(), None);
assert!(matches!(
feature.field_as_string(23).unwrap_err(),
GdalError::InvalidFieldIndex {
index: 23,
method_name: "field_as_string",
}
));
});
}
#[test]
fn test_get_field_as_datetime() {
use chrono::{FixedOffset, TimeZone};
let hour_secs = 3600;
with_features("points_with_datetime.json", |mut features| {
let feature = features.next().unwrap();
let dt = FixedOffset::east_opt(-5 * hour_secs)
.unwrap()
.with_ymd_and_hms(2011, 7, 14, 19, 43, 37)
.unwrap();
let d = FixedOffset::east_opt(0)
.unwrap()
.with_ymd_and_hms(2018, 1, 4, 0, 0, 0)
.unwrap();
assert_eq!(feature.field_as_datetime_by_name("dt").unwrap(), Some(dt));
assert_eq!(feature.field_as_datetime(0).unwrap(), Some(dt));
assert_eq!(feature.field_as_datetime_by_name("d").unwrap(), Some(d));
assert_eq!(feature.field_as_datetime(1).unwrap(), Some(d));
});
with_features("roads.geojson", |mut features| {
let feature = features.next().unwrap();
let railway_field = 5;
assert_eq!(feature.field_as_datetime_by_name("railway").unwrap(), None);
assert_eq!(feature.field_as_datetime(railway_field).unwrap(), None);
assert!(matches!(
feature
.field_as_datetime_by_name("not_a_field")
.unwrap_err(),
GdalError::InvalidFieldName {
field_name,
method_name: "OGR_F_GetFieldIndex",
} if field_name == "not_a_field"
));
assert!(matches!(
feature.field_as_datetime(23).unwrap_err(),
GdalError::InvalidFieldIndex {
index: 23,
method_name: "field_as_datetime",
}
));
});
}
#[test]
fn test_field_in_layer() {
ds_with_layer("three_layer_ds.s3db", "layer_0", |mut layer| {
let feature = layer.features().next().unwrap();
assert_eq!(feature.field("id").unwrap(), None);
});
}
#[test]
fn test_int_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
assert_eq!(
feature.field("an_int_list").unwrap().unwrap(),
FieldValue::IntegerListValue(vec![1, 2])
);
});
}
#[test]
fn test_set_int_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
let value = FieldValue::IntegerListValue(vec![3, 4, 5]);
feature.set_field("an_int_list", &value).unwrap();
assert_eq!(feature.field("an_int_list").unwrap().unwrap(), value);
});
}
#[test]
fn test_real_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
assert_eq!(
feature.field("a_real_list").unwrap().unwrap(),
FieldValue::RealListValue(vec![0.1, 0.2])
);
});
}
#[test]
fn test_set_real_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
let value = FieldValue::RealListValue(vec![2.5, 3.0, 4.75]);
feature.set_field("a_real_list", &value).unwrap();
assert_eq!(feature.field("a_real_list").unwrap().unwrap(), value);
});
}
#[test]
fn test_long_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
assert_eq!(
feature.field("a_long_list").unwrap().unwrap(),
FieldValue::Integer64ListValue(vec![5000000000, 6000000000])
);
});
}
#[test]
fn test_set_long_list_field() {
with_features("soundg.json", |mut features| {
let feature = features.next().unwrap();
let value = FieldValue::Integer64ListValue(vec![7000000000, 8000000000]);
feature.set_field("a_long_list", &value).unwrap();
assert_eq!(feature.field("a_long_list").unwrap().unwrap(), value);
});
}
#[test]
fn test_float_field() {
with_feature("roads.geojson", 236194095, |feature| {
assert_almost_eq(
feature
.field("sort_key")
.unwrap()
.unwrap()
.into_real()
.unwrap(),
-9.0,
);
});
}
#[test]
fn test_missing_field() {
with_feature("roads.geojson", 236194095, |feature| {
assert!(feature.field("no such field").is_err());
});
}
#[test]
fn test_geom_accessors() {
with_feature("roads.geojson", 236194095, |feature| {
let geom = feature.geometry().unwrap();
assert_eq!(geom.geometry_type(), OGRwkbGeometryType::wkbLineString);
let coords = geom.get_point_vec();
assert_eq!(
coords,
[
(26.1019276, 44.4302748, 0.0),
(26.1019382, 44.4303191, 0.0),
(26.1020002, 44.4304202, 0.0)
]
);
assert_eq!(geom.geometry_count(), 0);
let geom = feature.geometry_by_index(0).unwrap();
assert_eq!(geom.geometry_type(), OGRwkbGeometryType::wkbLineString);
assert!(feature.geometry_by_index(1).is_err());
let geom = feature.geometry_by_name("");
assert!(geom.is_ok());
let geom = feature.geometry_by_name("").unwrap();
assert_eq!(geom.geometry_type(), OGRwkbGeometryType::wkbLineString);
assert!(feature.geometry_by_name("FOO").is_err());
});
}
#[test]
fn test_feature_wkt() {
with_feature("roads.geojson", 236194095, |feature| {
let wkt = feature.geometry().unwrap().wkt().unwrap();
let wkt_ok = format!(
"{}{}",
"LINESTRING (26.1019276 44.4302748,",
"26.1019382 44.4303191,26.1020002 44.4304202)"
);
assert_eq!(wkt, wkt_ok);
});
}
#[test]
fn test_feature_json() {
with_feature("roads.geojson", 236194095, |feature| {
let json = feature.geometry().unwrap().json();
let json_ok = format!(
"{}{}{}{}",
"{ \"type\": \"LineString\", \"coordinates\": [ ",
"[ 26.1019276, 44.4302748 ], ",
"[ 26.1019382, 44.4303191 ], ",
"[ 26.1020002, 44.4304202 ] ] }"
);
assert_eq!(json.unwrap(), json_ok);
});
}
#[test]
fn test_write_features() {
use std::fs;
{
let driver = DriverManager::get_driver_by_name("GeoJSON").unwrap();
let mut ds = driver
.create_vector_only(fixture("output.geojson"))
.unwrap();
let mut layer = ds.create_layer(Default::default()).unwrap();
layer
.create_defn_fields(&[
("Name", OGRFieldType::OFTString),
("Value", OGRFieldType::OFTReal),
("Int_value", OGRFieldType::OFTInteger),
])
.unwrap();
layer
.create_feature_fields(
Geometry::from_wkt("POINT (1 2)").unwrap(),
&["Name", "Value", "Int_value"],
&[
FieldValue::StringValue("Feature 1".to_string()),
FieldValue::RealValue(45.78),
FieldValue::IntegerValue(1),
],
)
.unwrap();
}
{
let ds = Dataset::open(fixture("output.geojson")).unwrap();
let mut layer = ds.layer(0).unwrap();
let ft = layer.features().next().unwrap();
assert_eq!(ft.geometry().unwrap().wkt().unwrap(), "POINT (1 2)");
assert_eq!(
ft.field("Name").unwrap().unwrap().into_string(),
Some("Feature 1".to_string())
);
assert_eq!(ft.field("Value").unwrap().unwrap().into_real(), Some(45.78));
assert_eq!(ft.field("Int_value").unwrap().unwrap().into_int(), Some(1));
}
fs::remove_file(fixture("output.geojson")).unwrap();
}
#[test]
fn test_features_reset() {
with_layer("roads.geojson", |mut layer| {
assert_eq!(layer.features().count(), layer.features().count(),);
});
}
#[test]
fn test_set_attribute_filter() {
with_layer("roads.geojson", |mut layer| {
assert_eq!(layer.features().count(), 21);
layer.clear_attribute_filter();
assert_eq!(layer.features().count(), 21);
layer.set_attribute_filter("highway = 'primary'").unwrap();
assert_eq!(layer.features().count(), 1);
assert_eq!(
layer
.features()
.next()
.unwrap()
.field_as_string_by_name("highway")
.unwrap()
.unwrap(),
"primary"
);
layer.clear_attribute_filter();
assert_eq!(layer.features().count(), 21);
{
let _nolog = SuppressGDALErrorLog::new();
assert!(matches!(
layer.set_attribute_filter("foo = bar").unwrap_err(),
GdalError::OgrError {
err: gdal_sys::OGRErr::OGRERR_CORRUPT_DATA,
method_name: "OGR_L_SetAttributeFilter",
}
));
}
});
}
#[test]
fn test_set_feature() {
let ds_options = DatasetOptions {
open_flags: GdalOpenFlags::GDAL_OF_UPDATE,
..DatasetOptions::default()
};
let tmp_file = TempFixture::empty("test.s3db");
std::fs::copy(fixture("three_layer_ds.s3db"), &tmp_file).unwrap();
let ds = Dataset::open_ex(&tmp_file, ds_options).unwrap();
let mut layer = ds.layer(0).unwrap();
let fids: Vec<u64> = layer.features().map(|f| f.fid().unwrap()).collect();
let feature = layer.feature(fids[0]).unwrap();
feature.set_field_integer("id", 1).ok();
layer.set_feature(feature).ok();
let ds = Dataset::open(&tmp_file).unwrap();
let layer = ds.layer(0).unwrap();
let feature = layer.feature(fids[0]).unwrap();
let value = feature.field("id").unwrap().unwrap().into_int().unwrap();
assert_eq!(value, 1);
}
#[test]
fn test_schema() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
let layer = ds.layer(0).unwrap();
assert!(layer.name() == "OGRGeoJSON" || layer.name() == "roads");
let name_list = layer
.defn()
.fields()
.map(|f| (f.name(), f.field_type()))
.collect::<Vec<_>>();
let ok_names_types = vec![
("kind", OGRFieldType::OFTString),
("sort_key", OGRFieldType::OFTReal),
("is_link", OGRFieldType::OFTString),
("is_tunnel", OGRFieldType::OFTString),
("is_bridge", OGRFieldType::OFTString),
("railway", OGRFieldType::OFTString),
("highway", OGRFieldType::OFTString),
]
.iter()
.map(|s| (s.0.to_string(), s.1))
.collect::<Vec<_>>();
assert_eq!(name_list, ok_names_types);
}
#[test]
fn test_geom_fields() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
let layer = ds.layer(0).unwrap();
let name_list = layer
.defn()
.geom_fields()
.map(|f| (f.name(), f.field_type()))
.collect::<Vec<_>>();
let ok_names_types = vec![("", OGRwkbGeometryType::wkbLineString)]
.iter()
.map(|s| (s.0.to_string(), s.1))
.collect::<Vec<_>>();
assert_eq!(name_list, ok_names_types);
let geom_field = layer.defn().geom_fields().next().unwrap();
let spatial_ref2 = SpatialRef::from_epsg(4326).unwrap();
#[cfg(major_ge_3)]
spatial_ref2.set_axis_mapping_strategy(0);
assert_eq!(geom_field.spatial_ref().unwrap(), spatial_ref2);
}
#[test]
fn test_get_layer_by_name() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
if let Ok(layer) = ds.layer_by_name("OGRGeoJSON") {
assert_eq!(layer.name(), "OGRGeoJSON");
}
if let Ok(layer) = ds.layer_by_name("roads") {
assert_eq!(layer.name(), "roads");
}
}
#[test]
fn test_spatial_filter() {
let ds = Dataset::open(fixture("roads.geojson")).unwrap();
let mut layer = ds.layer(0).unwrap();
assert_eq!(layer.features().count(), 21);
let bbox = Geometry::bbox(26.1017, 44.4297, 26.1025, 44.4303).unwrap();
layer.set_spatial_filter(&bbox);
assert_eq!(layer.features().count(), 7);
layer.clear_spatial_filter();
assert_eq!(layer.features().count(), 21);
layer.set_spatial_filter_rect(26.1017, 44.4297, 26.1025, 44.4303);
assert_eq!(layer.features().count(), 7);
}
}