use crate::utils::{_last_null_pointer_err, _string, _string_array};
use crate::vector::geometry::Geometry;
use crate::vector::{Defn, LayerAccess};
use gdal_sys::{self, OGRErr, OGRFeatureH, OGRFieldType};
use libc::{c_char, c_double, c_int, c_longlong};
use std::convert::TryInto;
use std::ffi::{CString, NulError};
use std::ptr;
use chrono::{Date, DateTime, Datelike, FixedOffset, TimeZone, Timelike};
use crate::errors::*;
use std::slice;
#[derive(Debug)]
pub struct Feature<'a> {
_defn: &'a Defn,
c_feature: OGRFeatureH,
geometry: Vec<Geometry>,
}
impl<'a> Feature<'a> {
pub fn new(defn: &'a Defn) -> Result<Feature> {
let c_feature = unsafe { gdal_sys::OGR_F_Create(defn.c_defn()) };
if c_feature.is_null() {
return Err(_last_null_pointer_err("OGR_F_Create"));
};
Ok(Feature {
_defn: defn,
c_feature,
geometry: Feature::_lazy_feature_geometries(defn),
})
}
pub unsafe fn from_c_feature(defn: &'a Defn, c_feature: OGRFeatureH) -> Feature {
Feature {
_defn: defn,
c_feature,
geometry: Feature::_lazy_feature_geometries(defn),
}
}
pub unsafe fn c_feature(&self) -> OGRFeatureH {
self.c_feature
}
pub fn _lazy_feature_geometries(defn: &'a Defn) -> Vec<Geometry> {
let geom_field_count =
unsafe { gdal_sys::OGR_FD_GetGeomFieldCount(defn.c_defn()) } as isize;
(0..geom_field_count)
.map(|_| unsafe { Geometry::lazy_feature_geometry() })
.collect()
}
pub fn fid(&self) -> Option<u64> {
let fid = unsafe { gdal_sys::OGR_F_GetFID(self.c_feature) };
if fid < 0 {
None
} else {
Some(fid as u64)
}
}
pub fn field<S: AsRef<str>>(&self, name: S) -> Result<Option<FieldValue>> {
let idx = self.field_idx_from_name(name)?;
self.field_from_id(idx)
}
fn field_from_id(&self, field_id: i32) -> Result<Option<FieldValue>> {
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_id) } != 0 {
return Ok(None);
}
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(Some(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(Some(FieldValue::StringListValue(rv)))
}
OGRFieldType::OFTReal => {
let rv = unsafe { gdal_sys::OGR_F_GetFieldAsDouble(self.c_feature, field_id) };
Ok(Some(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(Some(FieldValue::RealListValue(rv)))
}
OGRFieldType::OFTInteger => {
let rv = unsafe { gdal_sys::OGR_F_GetFieldAsInteger(self.c_feature, field_id) };
Ok(Some(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(Some(FieldValue::IntegerListValue(rv)))
}
OGRFieldType::OFTInteger64 => {
let rv = unsafe { gdal_sys::OGR_F_GetFieldAsInteger64(self.c_feature, field_id) };
Ok(Some(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(Some(FieldValue::Integer64ListValue(rv)))
}
OGRFieldType::OFTDateTime => Ok(Some(FieldValue::DateTimeValue(
self._field_as_datetime(field_id)?,
))),
OGRFieldType::OFTDate => Ok(Some(FieldValue::DateValue(
self._field_as_datetime(field_id)?.date(),
))),
_ => Err(GdalError::UnhandledFieldType {
field_type,
method_name: "OGR_Fld_GetType",
}),
}
}
fn field_idx_from_name<S: AsRef<str>>(&self, field_name: S) -> Result<i32> {
let c_str_field_name = CString::new(field_name.as_ref())?;
let field_id =
unsafe { gdal_sys::OGR_F_GetFieldIndex(self.c_feature, c_str_field_name.as_ptr()) };
if field_id == -1 {
return Err(GdalError::InvalidFieldName {
field_name: field_name.as_ref().to_string(),
method_name: "OGR_F_GetFieldIndex",
});
}
Ok(field_id)
}
pub fn field_as_integer(&self, field_idx: i32) -> Result<Option<i32>> {
if field_idx >= self.field_count() {
return Err(GdalError::InvalidFieldIndex {
index: field_idx as usize,
method_name: "field_as_integer",
});
}
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = unsafe { gdal_sys::OGR_F_GetFieldAsInteger(self.c_feature, field_idx) };
Ok(Some(value))
}
pub fn field_as_integer_by_name(&self, field_name: &str) -> Result<Option<i32>> {
let field_idx = self.field_idx_from_name(field_name)?;
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = unsafe { gdal_sys::OGR_F_GetFieldAsInteger(self.c_feature, field_idx) };
Ok(Some(value))
}
pub fn field_as_integer64_by_name(&self, field_name: &str) -> Result<Option<i64>> {
let field_idx = self.field_idx_from_name(field_name)?;
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = unsafe { gdal_sys::OGR_F_GetFieldAsInteger64(self.c_feature, field_idx) };
Ok(Some(value))
}
pub fn field_as_integer64(&self, field_idx: i32) -> Result<Option<i64>> {
if field_idx >= self.field_count() {
return Err(GdalError::InvalidFieldIndex {
index: field_idx as usize,
method_name: "field_as_integer64",
});
}
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = unsafe { gdal_sys::OGR_F_GetFieldAsInteger64(self.c_feature, field_idx) };
Ok(Some(value))
}
pub fn field_as_double_by_name(&self, field_name: &str) -> Result<Option<f64>> {
let field_idx = self.field_idx_from_name(field_name)?;
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = unsafe { gdal_sys::OGR_F_GetFieldAsDouble(self.c_feature, field_idx) };
Ok(Some(value))
}
pub fn field_as_double(&self, field_idx: i32) -> Result<Option<f64>> {
if field_idx >= self.field_count() {
return Err(GdalError::InvalidFieldIndex {
index: field_idx as usize,
method_name: "field_as_double",
});
}
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = unsafe { gdal_sys::OGR_F_GetFieldAsDouble(self.c_feature, field_idx) };
Ok(Some(value))
}
pub fn field_as_string_by_name(&self, field_name: &str) -> Result<Option<String>> {
let field_idx = self.field_idx_from_name(field_name)?;
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = _string(unsafe { gdal_sys::OGR_F_GetFieldAsString(self.c_feature, field_idx) });
Ok(Some(value))
}
pub fn field_as_string(&self, field_idx: i32) -> Result<Option<String>> {
if field_idx >= self.field_count() {
return Err(GdalError::InvalidFieldIndex {
index: field_idx as usize,
method_name: "field_as_string",
});
}
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = _string(unsafe { gdal_sys::OGR_F_GetFieldAsString(self.c_feature, field_idx) });
Ok(Some(value))
}
pub fn field_as_datetime_by_name(
&self,
field_name: &str,
) -> Result<Option<DateTime<FixedOffset>>> {
let field_idx = self.field_idx_from_name(field_name)?;
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = self._field_as_datetime(field_idx)?;
Ok(Some(value))
}
pub fn field_as_datetime(&self, field_idx: i32) -> Result<Option<DateTime<FixedOffset>>> {
if field_idx >= self.field_count() {
return Err(GdalError::InvalidFieldIndex {
index: field_idx as usize,
method_name: "field_as_datetime",
});
}
if unsafe { gdal_sys::OGR_F_IsFieldNull(self.c_feature, field_idx) } != 0 {
return Ok(None);
}
let value = self._field_as_datetime(field_idx)?;
Ok(Some(value))
}
fn _field_as_datetime(&self, field_id: c_int) -> Result<DateTime<FixedOffset>> {
let mut year: c_int = 0;
let mut month: c_int = 0;
let mut day: c_int = 0;
let mut hour: c_int = 0;
let mut minute: c_int = 0;
let mut second: c_int = 0;
let mut tzflag: c_int = 0;
let success = unsafe {
gdal_sys::OGR_F_GetFieldAsDateTime(
self.c_feature,
field_id,
&mut year,
&mut month,
&mut day,
&mut hour,
&mut minute,
&mut second,
&mut tzflag,
)
};
if success == 0 {
return Err(GdalError::OgrError {
err: OGRErr::OGRERR_FAILURE,
method_name: "OGR_F_GetFieldAsDateTime",
});
}
let tzoffset_secs = if tzflag == 0 || tzflag == 100 {
0
} else {
(tzflag as i32 - 100) * 15 * 60
};
let rv = FixedOffset::east(tzoffset_secs)
.ymd(year as i32, month as u32, day as u32)
.and_hms(hour as u32, minute as u32, second as u32);
Ok(rv)
}
pub fn geometry(&self) -> &Geometry {
if !self.geometry[0].has_gdal_ptr() {
let c_geom = unsafe { gdal_sys::OGR_F_GetGeometryRef(self.c_feature) };
unsafe { self.geometry[0].set_c_geometry(c_geom) };
}
&self.geometry[0]
}
pub fn geometry_by_name(&self, field_name: &str) -> Result<&Geometry> {
let c_str_field_name = CString::new(field_name)?;
let idx =
unsafe { gdal_sys::OGR_F_GetGeomFieldIndex(self.c_feature, c_str_field_name.as_ptr()) };
if idx == -1 {
Err(GdalError::InvalidFieldName {
field_name: field_name.to_string(),
method_name: "geometry_by_name",
})
} else {
self.geometry_by_index(idx as usize)
}
}
pub fn geometry_by_index(&self, idx: usize) -> Result<&Geometry> {
if idx >= self.geometry.len() {
return Err(GdalError::InvalidFieldIndex {
index: idx,
method_name: "geometry_by_name",
});
}
if !self.geometry[idx].has_gdal_ptr() {
let c_geom = unsafe { gdal_sys::OGR_F_GetGeomFieldRef(self.c_feature, idx as i32) };
if c_geom.is_null() {
return Err(_last_null_pointer_err("OGR_F_GetGeomFieldRef"));
}
unsafe { self.geometry[idx].set_c_geometry(c_geom) };
}
Ok(&self.geometry[idx])
}
pub fn create<L: LayerAccess>(&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 {
err: rv,
method_name: "OGR_L_CreateFeature",
});
}
Ok(())
}
pub fn set_field_string(&self, field_name: &str, value: &str) -> Result<()> {
let c_str_value = CString::new(value)?;
let idx = self.field_idx_from_name(field_name)?;
unsafe { gdal_sys::OGR_F_SetFieldString(self.c_feature, idx, c_str_value.as_ptr()) };
Ok(())
}
pub fn set_field_string_list(&self, field_name: &str, value: &[&str]) -> Result<()> {
let c_strings = value
.iter()
.map(|&value| CString::new(value))
.collect::<std::result::Result<Vec<CString>, NulError>>()?;
let c_str_ptrs = c_strings
.iter()
.map(|s| s.as_ptr())
.chain(std::iter::once(ptr::null()))
.collect::<Vec<*const c_char>>();
let c_value = c_str_ptrs.as_ptr() as *mut *mut c_char;
let idx = self.field_idx_from_name(field_name)?;
unsafe { gdal_sys::OGR_F_SetFieldStringList(self.c_feature, idx, c_value) };
Ok(())
}
pub fn set_field_double(&self, field_name: &str, value: f64) -> Result<()> {
let idx = self.field_idx_from_name(field_name)?;
unsafe { gdal_sys::OGR_F_SetFieldDouble(self.c_feature, idx, value as c_double) };
Ok(())
}
pub fn set_field_double_list(&self, field_name: &str, value: &[f64]) -> Result<()> {
let idx = self.field_idx_from_name(field_name)?;
unsafe {
gdal_sys::OGR_F_SetFieldDoubleList(
self.c_feature,
idx,
value.len() as c_int,
value.as_ptr(),
)
};
Ok(())
}
pub fn set_field_integer(&self, field_name: &str, value: i32) -> Result<()> {
let idx = self.field_idx_from_name(field_name)?;
unsafe { gdal_sys::OGR_F_SetFieldInteger(self.c_feature, idx, value as c_int) };
Ok(())
}
pub fn set_field_integer_list(&self, field_name: &str, value: &[i32]) -> Result<()> {
let idx = self.field_idx_from_name(field_name)?;
unsafe {
gdal_sys::OGR_F_SetFieldIntegerList(
self.c_feature,
idx,
value.len() as c_int,
value.as_ptr(),
)
};
Ok(())
}
pub fn set_field_integer64(&self, field_name: &str, value: i64) -> Result<()> {
let idx = self.field_idx_from_name(field_name)?;
unsafe { gdal_sys::OGR_F_SetFieldInteger64(self.c_feature, idx, value as c_longlong) };
Ok(())
}
pub fn set_field_integer64_list(&self, field_name: &str, value: &[i64]) -> Result<()> {
let idx = self.field_idx_from_name(field_name)?;
unsafe {
gdal_sys::OGR_F_SetFieldInteger64List(
self.c_feature,
idx,
value.len() as c_int,
value.as_ptr(),
)
};
Ok(())
}
pub fn set_field_datetime(&self, field_name: &str, value: DateTime<FixedOffset>) -> Result<()> {
let idx = self.field_idx_from_name(field_name)?;
let year = value.year() as c_int;
let month = value.month() as c_int;
let day = value.day() as c_int;
let hour = value.hour() as c_int;
let minute = value.minute() as c_int;
let second = value.second() as c_int;
let tzflag: c_int = if value.offset().local_minus_utc() == 0 {
0
} else {
100 + (value.offset().local_minus_utc() / (15 * 60))
};
unsafe {
gdal_sys::OGR_F_SetFieldDateTime(
self.c_feature,
idx,
year,
month,
day,
hour,
minute,
second,
tzflag,
)
};
Ok(())
}
pub fn set_field(&self, field_name: &str, value: &FieldValue) -> Result<()> {
match value {
FieldValue::IntegerValue(value) => self.set_field_integer(field_name, *value),
FieldValue::IntegerListValue(value) => self.set_field_integer_list(field_name, value),
FieldValue::Integer64Value(value) => self.set_field_integer64(field_name, *value),
FieldValue::Integer64ListValue(value) => {
self.set_field_integer64_list(field_name, value)
}
FieldValue::StringValue(ref value) => self.set_field_string(field_name, value.as_str()),
FieldValue::StringListValue(ref value) => {
let strs = value.iter().map(String::as_str).collect::<Vec<&str>>();
self.set_field_string_list(field_name, &strs)
}
FieldValue::RealValue(value) => self.set_field_double(field_name, *value),
FieldValue::RealListValue(value) => self.set_field_double_list(field_name, value),
FieldValue::DateValue(value) => {
self.set_field_datetime(field_name, value.and_hms(0, 0, 0))
}
FieldValue::DateTimeValue(value) => self.set_field_datetime(field_name, *value),
}
}
pub fn set_geometry(&mut self, geom: Geometry) -> Result<()> {
let rv = unsafe { gdal_sys::OGR_F_SetGeometry(self.c_feature, geom.c_geometry()) };
if rv != OGRErr::OGRERR_NONE {
return Err(GdalError::OgrError {
err: rv,
method_name: "OGR_F_SetGeometry",
});
}
self.geometry[0] = geom;
Ok(())
}
pub fn field_count(&self) -> i32 {
unsafe { gdal_sys::OGR_F_GetFieldCount(self.c_feature) }
}
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, Option<FieldValue>);
#[inline]
fn next(&mut self) -> Option<(String, Option<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, Option<FieldValue>)> = self
.feature
.field_from_id(idx)
.ok()
.map(|field_value| (name, field_value));
if fv.is_none() {
return self.next();
}
fv
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match Some(self.count).and_then(|s| s.try_into().ok()) {
Some(size) => (size, Some(size)),
None => (0, None),
}
}
}
impl<'a> Drop for Feature<'a> {
fn drop(&mut self) {
unsafe {
gdal_sys::OGR_F_Destroy(self.c_feature);
}
}
}
#[derive(Clone, 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>),
DateValue(Date<FixedOffset>),
DateTimeValue(DateTime<FixedOffset>),
}
impl FieldValue {
pub fn into_string(self) -> Option<String> {
match self {
FieldValue::StringValue(rv) => Some(rv),
_ => None,
}
}
pub fn into_real(self) -> Option<f64> {
match self {
FieldValue::RealValue(rv) => Some(rv),
_ => None,
}
}
pub fn into_int(self) -> Option<i32> {
match self {
FieldValue::IntegerValue(rv) => Some(rv),
FieldValue::Integer64Value(rv) => rv.try_into().ok(),
_ => None,
}
}
pub fn into_int64(self) -> Option<i64> {
match self {
FieldValue::IntegerValue(rv) => Some(rv as i64),
FieldValue::Integer64Value(rv) => Some(rv),
_ => None,
}
}
pub fn into_date(self) -> Option<Date<FixedOffset>> {
match self {
FieldValue::DateValue(rv) => Some(rv),
FieldValue::DateTimeValue(rv) => Some(rv.date()),
_ => None,
}
}
pub fn into_datetime(self) -> Option<DateTime<FixedOffset>> {
match self {
FieldValue::DateTimeValue(rv) => Some(rv),
_ => 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,
FieldValue::DateValue(_) => OGRFieldType::OFTDate,
FieldValue::DateTimeValue(_) => OGRFieldType::OFTDateTime,
}
}
}
pub fn field_type_to_name(ty: OGRFieldType::Type) -> String {
let rv = unsafe { gdal_sys::OGR_GetFieldTypeName(ty) };
_string(rv)
}
#[test]
pub fn test_field_type_to_name() {
assert_eq!(field_type_to_name(OGRFieldType::OFTReal), "Real");
field_type_to_name(4372521);
}