Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions c/sedona-gdal/src/dyn_load.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ fn load_all_symbols(lib: &Library, api: &mut SedonaGdalApi) -> Result<(), GdalIn
load_fn!(lib, api, VSIFileFromMemBuffer);
load_fn!(lib, api, VSIFCloseL);
load_fn!(lib, api, VSIUnlink);
load_fn!(lib, api, VSIGetDirectorySeparator);
load_fn!(lib, api, VSIOpenDir);
load_fn!(lib, api, VSIGetNextDirEntry);
load_fn!(lib, api, VSICloseDir);
load_fn!(lib, api, VSIGetMemFileBuffer);
load_fn!(lib, api, VSIFree);
load_fn!(lib, api, VSIMalloc);
Expand Down
17 changes: 17 additions & 0 deletions c/sedona-gdal/src/gdal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,23 @@ impl Gdal {
vsi::get_vsi_mem_file_bytes_owned(self.api, file_name)
}

/// Open a VSI directory for iteration.
/// See also [`vsi::open_dir`].
pub fn open_vsi_dir(
&self,
path: &str,
recurse_depth: i32,
options: Option<&crate::cpl::CslStringList>,
) -> Result<crate::vsi::VsiDir> {
crate::vsi::open_dir(self.api, path, recurse_depth, options)
}

/// Return the directory separator used by GDAL for a given VSI path.
/// See also [`vsi::get_directory_separator`].
pub fn vsi_directory_separator(&self, path: &str) -> Result<String> {
crate::vsi::get_directory_separator(self.api, path)
}

// -- Raster operations ---------------------------------------------------

/// Create a bare in-memory MEM dataset with GDAL-owned bands.
Expand Down
39 changes: 39 additions & 0 deletions c/sedona-gdal/src/gdal_dyn_bindgen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ pub type GDALRWFlag = c_int;
pub type OGRwkbByteOrder = c_int;
pub type GDALOpenFlags = c_uint;
pub type GDALRIOResampleAlg = c_int;
pub type GUIntBig = u64;
pub type GIntBig = i64;
pub type vsi_l_offset = GUIntBig;

// --- Opaque handle types ---

Expand All @@ -44,6 +47,31 @@ pub type OGRFeatureH = *mut c_void;
pub type OGRFieldDefnH = *mut c_void;
pub type VSILFILE = *mut c_void;

// --- VSI mode constants ---

pub const VSI_S_IFMT: i32 = 0o170000;
pub const VSI_S_IFDIR: i32 = 0o040000;
pub const VSI_S_IFREG: i32 = 0o100000;

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct VSIDIR {
_unused: [u8; 0],
}

#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct VSIDIREntry {
pub pszName: *mut c_char,
pub nMode: c_int,
pub nSize: vsi_l_offset,
pub nMTime: GIntBig,
pub bModeKnown: c_char,
pub bSizeKnown: c_char,
pub bMTimeKnown: c_char,
pub papszExtra: *mut *mut c_char,
}

// --- Enum types ---

#[repr(C)]
Expand Down Expand Up @@ -459,6 +487,17 @@ pub(crate) struct SedonaGdalApi {
>,
pub VSIFCloseL: Option<unsafe extern "C" fn(fp: VSILFILE) -> c_int>,
pub VSIUnlink: Option<unsafe extern "C" fn(pszFilename: *const c_char) -> c_int>,
pub VSIGetDirectorySeparator:
Option<unsafe extern "C" fn(pszPath: *const c_char) -> *const c_char>,
pub VSIOpenDir: Option<
unsafe extern "C" fn(
pszPath: *const c_char,
nRecurseDepth: c_int,
papszOptions: *const *const c_char,
) -> *mut VSIDIR,
>,
pub VSIGetNextDirEntry: Option<unsafe extern "C" fn(dir: *mut VSIDIR) -> *const VSIDIREntry>,
pub VSICloseDir: Option<unsafe extern "C" fn(dir: *mut VSIDIR)>,
pub VSIGetMemFileBuffer: Option<
unsafe extern "C" fn(
pszFilename: *const c_char,
Expand Down
87 changes: 87 additions & 0 deletions c/sedona-gdal/src/vsi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,90 @@ mod tests {
.unwrap();
}
}

pub struct VsiDirEntry {
pub name: String,
pub mode: Option<i32>,
pub size: Option<crate::gdal_dyn_bindgen::vsi_l_offset>,
pub mtime: Option<crate::gdal_dyn_bindgen::GIntBig>,
}

pub struct VsiDir {
api: &'static crate::gdal_api::GdalApi,
handle: *mut crate::gdal_dyn_bindgen::VSIDIR,
}

impl VsiDir {
pub fn next_entry(&mut self) -> Option<VsiDirEntry> {
let entry = unsafe { (self.api.inner.VSIGetNextDirEntry?)(self.handle) };
if entry.is_null() {
return None;
}
let entry = unsafe { &*entry };

let name = if entry.pszName.is_null() {
String::new()
} else {
unsafe { std::ffi::CStr::from_ptr(entry.pszName) }
.to_string_lossy()
.into_owned()
};

Some(VsiDirEntry {
name,
mode: (entry.bModeKnown != 0).then_some(entry.nMode),
size: (entry.bSizeKnown != 0).then_some(entry.nSize),
mtime: (entry.bMTimeKnown != 0).then_some(entry.nMTime),
})
}
}

impl Iterator for VsiDir {
type Item = VsiDirEntry;
fn next(&mut self) -> Option<Self::Item> {
self.next_entry()
}
}

impl Drop for VsiDir {
fn drop(&mut self) {
if !self.handle.is_null() {
if let Some(close) = self.api.inner.VSICloseDir {
unsafe { close(self.handle) };
}
self.handle = std::ptr::null_mut();
}
}
}

pub fn open_dir(
api: &'static crate::gdal_api::GdalApi,
path: &str,
recurse_depth: i32,
options: Option<&crate::cpl::CslStringList>,
) -> crate::errors::Result<VsiDir> {
let c_path = std::ffi::CString::new(path)?;
let options_ptr: *const *const std::os::raw::c_char = options
.map(|opts| opts.as_ptr() as *const *const std::os::raw::c_char)
.unwrap_or(std::ptr::null());
let handle =
unsafe { (api.inner.VSIOpenDir.unwrap())(c_path.as_ptr(), recurse_depth, options_ptr) };
if handle.is_null() {
return Err(api.last_null_pointer_err("VSIOpenDir"));
}
Ok(VsiDir { api, handle })
}

pub fn get_directory_separator(
api: &'static crate::gdal_api::GdalApi,
path: &str,
) -> crate::errors::Result<String> {
let c_path = std::ffi::CString::new(path)?;
let separator_ptr = unsafe { (api.inner.VSIGetDirectorySeparator.unwrap())(c_path.as_ptr()) };
if separator_ptr.is_null() {
return Err(api.last_null_pointer_err("VSIGetDirectorySeparator"));
}
Ok(unsafe { std::ffi::CStr::from_ptr(separator_ptr) }
.to_string_lossy()
.into_owned())
}
28 changes: 28 additions & 0 deletions rust/sedona-raster-functions/src/crs_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,34 @@ pub fn crs_transform_wkb(
Ok(out)
}

/// Transform a single coordinate pair from one CRS to another.
///
/// This is a utility used by raster/spatial functions when only an `(x, y)`
/// coordinate needs reprojection and full geometry decoding would be unnecessary.
///
/// **Behavior**
/// - Builds a PROJ pipeline for `from_crs` -> `to_crs`.
/// - Applies the transformation in place and returns the transformed coordinate.
///
/// **Errors**
/// - Returns an error if PROJ cannot build the CRS-to-CRS transform,
/// or if the coordinate transformation itself fails.
pub fn crs_transform_coord(
engine: &dyn CrsEngine,
coord: (f64, f64),
from_crs: &str,
to_crs: &str,
) -> Result<(f64, f64)> {
let trans = engine
.get_transform_crs_to_crs(from_crs, to_crs, None, "")
.map_err(|e| DataFusionError::External(Box::new(e)))?;
let mut coord = coord;
trans
.transform_coord(&mut coord)
.map_err(|e| DataFusionError::External(Box::new(e)))?;
Ok(coord)
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions rust/sedona-raster-functions/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub mod register;
pub mod rs_band_accessors;
pub mod rs_bandpath;
pub mod rs_convexhull;
pub mod rs_count;
pub mod rs_envelope;
pub mod rs_example;
pub mod rs_georeference;
Expand Down
1 change: 1 addition & 0 deletions rust/sedona-raster-functions/src/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub fn default_function_set() -> FunctionSet {
crate::rs_band_accessors::rs_bandnodatavalue_udf,
crate::rs_bandpath::rs_bandpath_udf,
crate::rs_convexhull::rs_convexhull_udf,
crate::rs_count::rs_count_udf,
crate::rs_envelope::rs_envelope_udf,
crate::rs_example::rs_example_udf,
crate::rs_georeference::rs_georeference_udf,
Expand Down
Loading
Loading