Skip to content

Commit

Permalink
add iterator for layers in dataset
Browse files Browse the repository at this point in the history
add iterator for fields in feature
add accessors for additional ogr field types.

FieldValueIterator skip unknown types but does not terminate until field count reached
  • Loading branch information
manimaul committed Dec 6, 2020
1 parent d14da5e commit 8d42f43
Show file tree
Hide file tree
Showing 9 changed files with 354 additions and 10 deletions.
50 changes: 50 additions & 0 deletions fixtures/fixture_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3

from osgeo import ogr, osr
import os.path

c_dir = os.path.dirname(os.path.realpath(__file__))


def create_n_layer_sqlite_ds(name: str, n_layers: int, n_features: int):
driver_name = "SQLite"
driver = ogr.GetDriverByName(driver_name)

file_name = os.path.join(c_dir, name)
if os.path.exists(file_name):
os.remove(file_name)

source = driver.CreateDataSource(file_name)

srs = osr.SpatialReference()
srs.ImportFromEPSG(4326)

for nl in range(0, n_layers):
# create a layer
layer = source.CreateLayer("layer_{}".format(nl), srs, geom_type=ogr.wkbPoint)

# create a field "id" in the layer
id_field = ogr.FieldDefn("id", ogr.OFTInteger)
layer.CreateField(id_field)

for ni in range(0, n_features):
# add a feature to the layer
feature_def = layer.GetLayerDefn()
feature = ogr.Feature(feature_def)

# add a point to the feature
point = ogr.Geometry(ogr.wkbPoint)
point.AddPoint(x=47.0 + nl, y=-122.0 + nl)
feature.SetGeometry(point)

# add the feature to the layer
layer.CreateFeature(feature)

# add a field
feature.SetField("id", nl)

source.Destroy()


if __name__ == '__main__':
create_n_layer_sqlite_ds(name="three_layer_ds.s3db", n_layers=3, n_features=3)
18 changes: 18 additions & 0 deletions fixtures/soundg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"features": [
{
"geometry": {
"coordinates": [ -122.4335161, 47.3136322 ],
"type": "Point"
},
"properties": {
"a_string_list": [ "a", "list", "of", "strings"],
"a_real_list": [ 0.1, 0.2 ],
"an_int_list": [ 1, 2 ],
"a_long_list": [ 5000000000, 6000000000 ]
},
"type": "Feature"
}
],
"type": "FeatureCollection"
}
Binary file added fixtures/three_layer_ds.s3db
Binary file not shown.
42 changes: 42 additions & 0 deletions src/dataset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use libc::{c_double, c_int};
use ptr::null_mut;

use crate::errors::*;
use std::convert::TryInto;

pub type GeoTransform = [c_double; 6];
static START: Once = Once::new();
Expand Down Expand Up @@ -239,6 +240,10 @@ impl Dataset {
Ok(self.child_layer(c_layer))
}

pub fn layers(&self) -> LayerIterator {
return LayerIterator::with_dataset(self);
}

pub fn raster_count(&self) -> isize {
(unsafe { gdal_sys::GDALGetRasterCount(self.c_dataset) }) as isize
}
Expand Down Expand Up @@ -396,6 +401,43 @@ impl Dataset {
}
}

pub struct LayerIterator<'a> {
dataset: &'a Dataset,
idx: isize,
count: isize
}

impl<'a> Iterator for LayerIterator<'a> {
type Item = Layer<'a>;

#[inline]
fn next(&mut self) -> Option<Layer<'a>> {
let idx = self.idx;
if idx < self.count {
self.idx += 1;
let c_layer = unsafe { gdal_sys::OGR_DS_GetLayer(self.dataset.c_dataset, idx as c_int) };
if !c_layer.is_null() {
let layer = unsafe { Layer::from_c_layer(self.dataset, c_layer) };
return Some(layer);
}
}
return None;
}

fn size_hint(&self) -> (usize, Option<usize>) {
match Some(self.count).map(|s| s.try_into().ok()).flatten() {
Some(size) => (size, Some(size)),
None => (0, None),
}
}
}

impl<'a> LayerIterator<'a> {
pub fn with_dataset(dataset: &'a Dataset) -> LayerIterator<'a> {
LayerIterator { dataset, idx: 0, count: dataset.layer_count() }
}
}

impl MajorObject for Dataset {
unsafe fn gdal_object_ptr(&self) -> GDALMajorObjectH {
self.c_dataset
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ mod utils;
pub mod vector;
pub mod version;

pub use dataset::{Dataset, GeoTransform, Transaction};
pub use dataset::{Dataset, GeoTransform, Transaction, LayerIterator};
pub use driver::Driver;
pub use metadata::Metadata;

Expand Down
21 changes: 21 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,27 @@ pub fn _string(raw_ptr: *const c_char) -> String {
c_str.to_string_lossy().into_owned()
}

pub fn _string_array(raw_ptr: *mut *mut c_char) -> Vec<String> {
let mut ret_val: Vec<String> = vec![];
let mut i = 0;
unsafe {
loop {
let ptr = raw_ptr.add(i);
if ptr.is_null() {
break;
}
let next = ptr.read();
if next.is_null() {
break;
}
let value = _string(next);
i += 1;
ret_val.push(value);
}
}
ret_val
}

// TODO: inspect if this is sane...
pub fn _last_cpl_err(cpl_err_class: CPLErr::Type) -> GdalError {
let last_err_no = unsafe { gdal_sys::CPLGetLastErrorNo() };
Expand Down
133 changes: 125 additions & 8 deletions src/vector/feature.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::{_last_null_pointer_err, _string};
use crate::utils::{_last_null_pointer_err, _string, _string_array};
use crate::vector::geometry::Geometry;
use crate::vector::layer::Layer;
use crate::vector::Defn;
Expand All @@ -12,6 +12,7 @@ use std::ffi::CString;
use chrono::{Date, DateTime, Datelike, FixedOffset, TimeZone, Timelike};

use crate::errors::*;
use std::slice;

/// OGR Feature
pub struct Feature<'a> {
Expand Down Expand Up @@ -70,30 +71,66 @@ impl<'a> Feature<'a> {
let c_name = CString::new(name)?;
let field_id = unsafe { gdal_sys::OGR_F_GetFieldIndex(self.c_feature, c_name.as_ptr()) };
if field_id == -1 {
return Err(GdalError::InvalidFieldName {
Err(GdalError::InvalidFieldName {
field_name: name.to_string(),
method_name: "OGR_F_GetFieldIndex",
});
})
} else {
self.field_from_id(field_id)
}
}

fn field_from_id(&self, field_id: i32) -> Result<FieldValue> {
let field_defn = unsafe { gdal_sys::OGR_F_GetFieldDefnRef(self.c_feature, field_id) };
let field_type = unsafe { gdal_sys::OGR_Fld_GetType(field_defn) };
match field_type {
OGRFieldType::OFTString => {
let rv = unsafe { gdal_sys::OGR_F_GetFieldAsString(self.c_feature, field_id) };
Ok(FieldValue::StringValue(_string(rv)))
}
OGRFieldType::OFTStringList => {
let rv = unsafe {
let ptr = gdal_sys::OGR_F_GetFieldAsStringList(self.c_feature, field_id);
_string_array(ptr)
};
Ok(FieldValue::StringListValue(rv))
}
OGRFieldType::OFTReal => {
let rv = unsafe { gdal_sys::OGR_F_GetFieldAsDouble(self.c_feature, field_id) };
Ok(FieldValue::RealValue(rv as f64))
}
OGRFieldType::OFTRealList => {
let rv = unsafe {
let mut len: i32 = 0;
let ptr = gdal_sys::OGR_F_GetFieldAsDoubleList(self.c_feature, field_id, &mut len);
slice::from_raw_parts(ptr, len as usize).to_vec()
};
Ok(FieldValue::RealListValue(rv))
}
OGRFieldType::OFTInteger => {
let rv = unsafe { gdal_sys::OGR_F_GetFieldAsInteger(self.c_feature, field_id) };
Ok(FieldValue::IntegerValue(rv as i32))
}
OGRFieldType::OFTIntegerList => {
let rv = unsafe {
let mut len: i32 = 0;
let ptr = gdal_sys::OGR_F_GetFieldAsIntegerList(self.c_feature, field_id, &mut len);
slice::from_raw_parts(ptr, len as usize).to_vec()
};
Ok(FieldValue::IntegerListValue(rv))
}
OGRFieldType::OFTInteger64 => {
let rv = unsafe { gdal_sys::OGR_F_GetFieldAsInteger64(self.c_feature, field_id) };
Ok(FieldValue::Integer64Value(rv))
}
OGRFieldType::OFTInteger64List => {
let rv = unsafe {
let mut len: i32 = 0;
let ptr = gdal_sys::OGR_F_GetFieldAsInteger64List(self.c_feature, field_id, &mut len);
slice::from_raw_parts(ptr, len as usize).to_vec()
};
Ok(FieldValue::Integer64ListValue(rv))
}
#[cfg(feature = "datetime")]
OGRFieldType::OFTDateTime => Ok(FieldValue::DateTimeValue(
self.get_field_datetime(field_id)?,
Expand Down Expand Up @@ -300,19 +337,23 @@ impl<'a> Feature<'a> {
}

pub fn set_field(&self, field_name: &str, value: &FieldValue) -> Result<()> {
match *value {
FieldValue::RealValue(value) => self.set_field_double(field_name, value),
match value {
FieldValue::RealValue(value) => self.set_field_double(field_name, *value),
FieldValue::StringValue(ref value) => self.set_field_string(field_name, value.as_str()),
FieldValue::IntegerValue(value) => self.set_field_integer(field_name, value),
FieldValue::Integer64Value(value) => self.set_field_integer64(field_name, value),
FieldValue::IntegerValue(value) => self.set_field_integer(field_name, *value),
FieldValue::Integer64Value(value) => self.set_field_integer64(field_name, *value),

#[cfg(feature = "datetime")]
FieldValue::DateTimeValue(value) => self.set_field_datetime(field_name, value),
FieldValue::DateTimeValue(value) => self.set_field_datetime(field_name, value.clone()),

#[cfg(feature = "datetime")]
FieldValue::DateValue(value) => {
self.set_field_datetime(field_name, value.and_hms(0, 0, 0))
}
_ => Err(GdalError::UnhandledFieldType {
field_type: value.ogr_field_type(),
method_name: "OGR_Fld_GetType",
})
}
}

Expand All @@ -327,6 +368,58 @@ impl<'a> Feature<'a> {
self.geometry[0] = geom;
Ok(())
}
pub fn field_count(&self) -> i32 {
let count = unsafe { gdal_sys::OGR_F_GetFieldCount(self.c_feature) };
count
}

pub fn fields(&self) -> FieldValueIterator {
FieldValueIterator::with_feature(self)
}
}

pub struct FieldValueIterator<'a> {
feature: &'a Feature<'a>,
idx: i32,
count: i32,
}

impl<'a> FieldValueIterator<'a> {
pub fn with_feature(feature: &'a Feature<'a>) -> Self {
FieldValueIterator { feature, idx: 0, count: feature.field_count() }
}
}

impl<'a> Iterator for FieldValueIterator<'a> {
type Item = (String, FieldValue);

#[inline]
fn next(&mut self) -> Option<(String, FieldValue)> {
let idx = self.idx;
if idx < self.count {
self.idx += 1;
let field_defn = unsafe { gdal_sys::OGR_F_GetFieldDefnRef(self.feature.c_feature, idx) };
let field_name = unsafe { gdal_sys::OGR_Fld_GetNameRef(field_defn) };
let name = _string(field_name);
let fv: Option<(String, FieldValue)> = self.feature.field_from_id(idx).ok()
.map(|field_value| (name, field_value));
if let None = fv { //skip unknown types
if self.idx < self.count {
return self.next();
}
}
fv
} else {
None
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
match Some(self.count).map(|s| s.try_into().ok()).flatten() {
Some(size) => (size, Some(size)),
None => (0, None),
}
}
}

impl<'a> Drop for Feature<'a> {
Expand All @@ -337,11 +430,16 @@ impl<'a> Drop for Feature<'a> {
}
}

#[derive(Debug, PartialEq)]
pub enum FieldValue {
IntegerValue(i32),
IntegerListValue(Vec<i32>),
Integer64Value(i64),
Integer64ListValue(Vec<i64>),
StringValue(String),
StringListValue(Vec<String>),
RealValue(f64),
RealListValue(Vec<f64>),

#[cfg(feature = "datetime")]
DateValue(Date<FixedOffset>),
Expand Down Expand Up @@ -403,4 +501,23 @@ impl FieldValue {
_ => None,
}
}

pub fn ogr_field_type(&self) -> OGRFieldType::Type {
match self {
FieldValue::IntegerValue(_) => OGRFieldType::OFTInteger,
FieldValue::IntegerListValue(_) => OGRFieldType::OFTIntegerList,
FieldValue::Integer64Value(_) => OGRFieldType::OFTInteger64,
FieldValue::Integer64ListValue(_) => OGRFieldType::OFTInteger64List,
FieldValue::StringValue(_) => OGRFieldType::OFTString,
FieldValue::StringListValue(_) => OGRFieldType::OFTStringList,
FieldValue::RealValue(_) => OGRFieldType::OFTReal,
FieldValue::RealListValue(_) => OGRFieldType::OFTRealList,

#[cfg(feature = "datetime")]
FieldValue::DateValue(_) => OGRFieldType::OFTDate,

#[cfg(feature = "datetime")]
FieldValue::DateTimeValue(_) => OGRFieldType::OFTDateTime
}
}
}
2 changes: 1 addition & 1 deletion src/vector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ mod layer;
mod ops;

pub use defn::{Defn, Field, FieldIterator};
pub use feature::{Feature, FieldValue};
pub use feature::{Feature, FieldValue, FieldValueIterator};
pub use gdal_sys::{OGRFieldType, OGRwkbGeometryType};
pub use geometry::Geometry;
pub use layer::{FeatureIterator, FieldDefn, Layer};
Expand Down
Loading

0 comments on commit 8d42f43

Please sign in to comment.