use super::super::types::*;
use super::feature::{FeatureHandle, FfiFeature, FieldValue};
use super::geometry::{FfiGeometry, merge_bounds};
use crate::{check_null, deref_ptr, deref_ptr_mut};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::ptr;
pub struct LayerHandle {
name: String,
geom_type: String,
srs_epsg: i32,
features: Vec<FfiFeature>,
cursor: usize,
spatial_filter: Option<OxiGdalBbox>,
extent_cache: Option<OxiGdalBbox>,
}
impl LayerHandle {
#[must_use]
pub fn new(name: String, geom_type: String, srs_epsg: i32) -> Self {
Self {
name,
geom_type,
srs_epsg,
features: Vec::new(),
cursor: 0,
spatial_filter: None,
extent_cache: None,
}
}
#[must_use]
pub fn feature_count(&self) -> i64 {
self.features.len() as i64
}
#[must_use]
pub fn calculate_extent(&self) -> OxiGdalBbox {
if let Some(cached) = &self.extent_cache {
return *cached;
}
let bounds = merge_bounds(self.features.iter().filter_map(FfiFeature::bounds));
if let Some((min_x, min_y, max_x, max_y)) = bounds {
OxiGdalBbox {
min_x,
min_y,
max_x,
max_y,
}
} else {
OxiGdalBbox {
min_x: 0.0,
min_y: 0.0,
max_x: 0.0,
max_y: 0.0,
}
}
}
pub fn reset_cursor(&mut self) {
self.cursor = 0;
}
pub fn next_feature(&mut self) -> Option<&FfiFeature> {
while self.cursor < self.features.len() {
let feature = &self.features[self.cursor];
self.cursor += 1;
if let Some(ref filter) = self.spatial_filter {
if !feature.intersects_bbox(filter) {
continue;
}
}
return Some(feature);
}
None
}
#[must_use]
pub fn get_feature_by_fid(&self, fid: i64) -> Option<&FfiFeature> {
self.features.iter().find(|f| f.fid == fid)
}
pub fn add_feature(&mut self, feature: FfiFeature) {
self.features.push(feature);
self.extent_cache = None; }
pub fn set_spatial_filter(&mut self, bbox: OxiGdalBbox) {
self.spatial_filter = Some(bbox);
}
pub fn clear_spatial_filter(&mut self) {
self.spatial_filter = None;
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn geom_type(&self) -> &str {
&self.geom_type
}
#[must_use]
pub fn srs_epsg(&self) -> i32 {
self.srs_epsg
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_dataset_open_layer(
dataset: *const OxiGdalDataset,
layer_name: *const c_char,
out_layer: *mut *mut OxiGdalLayer,
) -> OxiGdalErrorCode {
check_null!(dataset, "dataset");
check_null!(out_layer, "out_layer");
let name = if layer_name.is_null() {
String::new()
} else {
unsafe {
match CStr::from_ptr(layer_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => {
crate::ffi::error::set_last_error("Invalid UTF-8 in layer name".to_string());
return OxiGdalErrorCode::InvalidUtf8;
}
}
}
};
let handle = Box::new(LayerHandle::new(name, "Unknown".to_string(), 0));
unsafe {
*out_layer = Box::into_raw(handle) as *mut OxiGdalLayer;
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_close(layer: *mut OxiGdalLayer) -> OxiGdalErrorCode {
check_null!(layer, "layer");
unsafe {
drop(Box::from_raw(layer as *mut LayerHandle));
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_get_feature_count(
layer: *const OxiGdalLayer,
out_count: *mut i64,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
check_null!(out_count, "out_count");
unsafe {
let handle = deref_ptr!(layer, LayerHandle, "layer");
*out_count = handle.feature_count();
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_get_extent(
layer: *const OxiGdalLayer,
out_bbox: *mut OxiGdalBbox,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
check_null!(out_bbox, "out_bbox");
unsafe {
let handle = deref_ptr!(layer, LayerHandle, "layer");
*out_bbox = handle.calculate_extent();
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_reset_reading(layer: *mut OxiGdalLayer) -> OxiGdalErrorCode {
check_null!(layer, "layer");
unsafe {
let handle = &mut *(layer as *mut LayerHandle);
handle.reset_cursor();
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_get_next_feature(
layer: *mut OxiGdalLayer,
out_feature: *mut *mut OxiGdalFeature,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
check_null!(out_feature, "out_feature");
unsafe {
let handle = &mut *(layer as *mut LayerHandle);
if let Some(feature) = handle.next_feature() {
let feature_handle = Box::new(FeatureHandle::new(feature.clone()));
*out_feature = Box::into_raw(feature_handle) as *mut OxiGdalFeature;
} else {
*out_feature = ptr::null_mut();
}
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_get_feature(
layer: *const OxiGdalLayer,
fid: i64,
out_feature: *mut *mut OxiGdalFeature,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
check_null!(out_feature, "out_feature");
if fid < 0 {
crate::ffi::error::set_last_error("Invalid feature ID".to_string());
return OxiGdalErrorCode::InvalidArgument;
}
unsafe {
let handle = deref_ptr!(layer, LayerHandle, "layer");
if let Some(feature) = handle.get_feature_by_fid(fid) {
let feature_handle = Box::new(FeatureHandle::new(feature.clone()));
*out_feature = Box::into_raw(feature_handle) as *mut OxiGdalFeature;
} else {
*out_feature = ptr::null_mut();
}
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_feature_free(feature: *mut OxiGdalFeature) -> OxiGdalErrorCode {
check_null!(feature, "feature");
unsafe {
drop(Box::from_raw(feature as *mut FeatureHandle));
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_feature_get_fid(
feature: *const OxiGdalFeature,
out_fid: *mut i64,
) -> OxiGdalErrorCode {
check_null!(feature, "feature");
check_null!(out_fid, "out_fid");
unsafe {
let handle = deref_ptr!(feature, FeatureHandle, "feature");
*out_fid = handle.inner().fid;
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_feature_get_geometry_wkt(
feature: *const OxiGdalFeature,
) -> *mut c_char {
if feature.is_null() {
crate::ffi::error::set_last_error("Null feature pointer".to_string());
return ptr::null_mut();
}
unsafe {
let handle = &*(feature as *const FeatureHandle);
let wkt = match &handle.inner().geometry {
Some(geom) => geom.to_wkt(),
None => {
"GEOMETRYCOLLECTION EMPTY".to_string()
}
};
match CString::new(wkt) {
Ok(s) => s.into_raw(),
Err(_) => {
crate::ffi::error::set_last_error("Failed to create WKT string".to_string());
ptr::null_mut()
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_feature_get_field_as_string(
feature: *const OxiGdalFeature,
field_name: *const c_char,
) -> *mut c_char {
if feature.is_null() || field_name.is_null() {
crate::ffi::error::set_last_error("Null pointer provided".to_string());
return ptr::null_mut();
}
let field = unsafe {
match CStr::from_ptr(field_name).to_str() {
Ok(s) => s,
Err(_) => {
crate::ffi::error::set_last_error("Invalid UTF-8 in field name".to_string());
return ptr::null_mut();
}
}
};
unsafe {
let handle = &*(feature as *const FeatureHandle);
let value_str = match handle.inner().fields.get(field) {
Some(value) => value.to_string_value(),
None => String::new(),
};
match CString::new(value_str) {
Ok(s) => s.into_raw(),
Err(_) => {
crate::ffi::error::set_last_error("Failed to create string".to_string());
ptr::null_mut()
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_feature_get_field_as_integer(
feature: *const OxiGdalFeature,
field_name: *const c_char,
out_value: *mut i64,
) -> OxiGdalErrorCode {
check_null!(feature, "feature");
check_null!(field_name, "field_name");
check_null!(out_value, "out_value");
let field = unsafe {
match CStr::from_ptr(field_name).to_str() {
Ok(s) => s,
Err(_) => {
crate::ffi::error::set_last_error("Invalid UTF-8 in field name".to_string());
return OxiGdalErrorCode::InvalidUtf8;
}
}
};
unsafe {
let handle = deref_ptr!(feature, FeatureHandle, "feature");
let value = match handle.inner().fields.get(field) {
Some(field_value) => field_value.as_integer().unwrap_or(0),
None => 0,
};
*out_value = value;
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_feature_get_field_as_double(
feature: *const OxiGdalFeature,
field_name: *const c_char,
out_value: *mut f64,
) -> OxiGdalErrorCode {
check_null!(feature, "feature");
check_null!(field_name, "field_name");
check_null!(out_value, "out_value");
let field = unsafe {
match CStr::from_ptr(field_name).to_str() {
Ok(s) => s,
Err(_) => {
crate::ffi::error::set_last_error("Invalid UTF-8 in field name".to_string());
return OxiGdalErrorCode::InvalidUtf8;
}
}
};
unsafe {
let handle = deref_ptr!(feature, FeatureHandle, "feature");
let value = match handle.inner().fields.get(field) {
Some(field_value) => field_value.as_double().unwrap_or(0.0),
None => 0.0,
};
*out_value = value;
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_dataset_create_layer(
dataset: *mut OxiGdalDataset,
layer_name: *const c_char,
geom_type: *const c_char,
srs_epsg: i32,
out_layer: *mut *mut OxiGdalLayer,
) -> OxiGdalErrorCode {
check_null!(dataset, "dataset");
check_null!(layer_name, "layer_name");
check_null!(out_layer, "out_layer");
let name = unsafe {
match CStr::from_ptr(layer_name).to_str() {
Ok(s) => s.to_string(),
Err(_) => {
crate::ffi::error::set_last_error("Invalid UTF-8 in layer name".to_string());
return OxiGdalErrorCode::InvalidUtf8;
}
}
};
let geom = if geom_type.is_null() {
"Unknown".to_string()
} else {
unsafe {
match CStr::from_ptr(geom_type).to_str() {
Ok(s) => s.to_string(),
Err(_) => {
crate::ffi::error::set_last_error("Invalid UTF-8 in geometry type".to_string());
return OxiGdalErrorCode::InvalidUtf8;
}
}
}
};
let valid_geom_types = [
"Unknown",
"Point",
"LineString",
"Polygon",
"MultiPoint",
"MultiLineString",
"MultiPolygon",
"GeometryCollection",
];
if !valid_geom_types.contains(&geom.as_str()) {
crate::ffi::error::set_last_error(format!("Invalid geometry type: {}", geom));
return OxiGdalErrorCode::InvalidArgument;
}
if srs_epsg < 0 {
crate::ffi::error::set_last_error("Invalid EPSG code".to_string());
return OxiGdalErrorCode::InvalidArgument;
}
let handle = Box::new(LayerHandle::new(name, geom, srs_epsg));
unsafe {
*out_layer = Box::into_raw(handle) as *mut OxiGdalLayer;
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_set_spatial_filter_bbox(
layer: *mut OxiGdalLayer,
bbox: *const OxiGdalBbox,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
check_null!(bbox, "bbox");
unsafe {
let handle = &mut *(layer as *mut LayerHandle);
let bounds = &*(bbox);
if bounds.min_x > bounds.max_x || bounds.min_y > bounds.max_y {
crate::ffi::error::set_last_error("Invalid bounding box: min > max".to_string());
return OxiGdalErrorCode::InvalidArgument;
}
handle.set_spatial_filter(*bounds);
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_clear_spatial_filter(
layer: *mut OxiGdalLayer,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
unsafe {
let handle = &mut *(layer as *mut LayerHandle);
handle.clear_spatial_filter();
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_get_geom_type(layer: *const OxiGdalLayer) -> *mut c_char {
if layer.is_null() {
crate::ffi::error::set_last_error("Null layer pointer".to_string());
return ptr::null_mut();
}
unsafe {
let handle = &*(layer as *const LayerHandle);
match CString::new(handle.geom_type()) {
Ok(s) => s.into_raw(),
Err(_) => {
crate::ffi::error::set_last_error(
"Failed to create geometry type string".to_string(),
);
ptr::null_mut()
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_get_srs_epsg(
layer: *const OxiGdalLayer,
out_epsg: *mut i32,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
check_null!(out_epsg, "out_epsg");
unsafe {
let handle = deref_ptr!(layer, LayerHandle, "layer");
*out_epsg = handle.srs_epsg();
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_get_name(layer: *const OxiGdalLayer) -> *mut c_char {
if layer.is_null() {
crate::ffi::error::set_last_error("Null layer pointer".to_string());
return ptr::null_mut();
}
unsafe {
let handle = &*(layer as *const LayerHandle);
match CString::new(handle.name()) {
Ok(s) => s.into_raw(),
Err(_) => {
crate::ffi::error::set_last_error("Failed to create layer name string".to_string());
ptr::null_mut()
}
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_layer_add_feature_internal(
layer: *mut OxiGdalLayer,
fid: i64,
x: f64,
y: f64,
) -> OxiGdalErrorCode {
check_null!(layer, "layer");
unsafe {
let handle = &mut *(layer as *mut LayerHandle);
let mut feature = FfiFeature::new(fid);
feature.geometry = Some(FfiGeometry::Point { x, y, z: None });
handle.add_feature(feature);
}
OxiGdalErrorCode::Success
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn oxigdal_feature_get_geometry_type(
feature: *const OxiGdalFeature,
) -> *mut c_char {
if feature.is_null() {
crate::ffi::error::set_last_error("Null feature pointer".to_string());
return ptr::null_mut();
}
unsafe {
let handle = &*(feature as *const FeatureHandle);
let type_str = match &handle.inner().geometry {
Some(geom) => geom.geometry_type(),
None => "None",
};
match CString::new(type_str) {
Ok(s) => s.into_raw(),
Err(_) => {
crate::ffi::error::set_last_error(
"Failed to create geometry type string".to_string(),
);
ptr::null_mut()
}
}
}
}