Skip to content
Open
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
4 changes: 2 additions & 2 deletions c/sedona-gdal/src/dataset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,11 +300,11 @@ impl Dataset {
///
/// # Safety
///
/// `data_ptr` must point to valid band data that outlives this dataset.
/// `data_ptr` must point to valid mutable band data that outlives this dataset.
pub unsafe fn add_band_with_data(
&self,
data_type: RustGdalDataType,
data_ptr: *const u8,
data_ptr: *mut u8,
Comment on lines +303 to +307
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this actually required to be mutable? (This seems like a strange constraint, and also one that will result in it not being particularly useful to us)

pixel_offset: Option<i64>,
line_offset: Option<i64>,
) -> Result<()> {
Expand Down
237 changes: 237 additions & 0 deletions c/sedona-gdal/src/gdal.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

//! High-level convenience wrapper around [`GdalApi`].
//!
//! [`Gdal`] bundles a `&'static GdalApi` reference and exposes ergonomic
//! methods that delegate to the lower-level constructors and free functions
//! scattered across the crate, eliminating the need to pass `api` explicitly
//! at every call site.

use crate::config;
use crate::dataset::Dataset;
use crate::driver::{Driver, DriverManager};
use crate::errors::Result;
use crate::gdal_api::GdalApi;
use crate::gdal_dyn_bindgen::{GDALOpenFlags, OGRFieldType};
use crate::mem::create_mem_dataset;
use crate::raster::polygonize::{polygonize, PolygonizeOptions};
use crate::raster::rasterband::RasterBand;
use crate::raster::rasterize::{rasterize, RasterizeOptions};
use crate::raster::rasterize_affine::rasterize_affine;
use crate::raster::types::DatasetOptions;
use crate::raster::types::GdalDataType;
use crate::spatial_ref::SpatialRef;
use crate::vector::feature::FieldDefn;
use crate::vector::geometry::Geometry;
use crate::vector::layer::Layer;
use crate::vrt::VrtDataset;
use crate::vsi;

/// High-level convenience wrapper around [`GdalApi`].
///
/// Stores a `&'static GdalApi` reference and provides ergonomic methods that
/// delegate to the various constructors and free functions in the crate.
pub struct Gdal {
api: &'static GdalApi,
}

impl Gdal {
Comment on lines +45 to +53
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do like the idea of a high-level object that dishes out other safe high-level objects, although putting it all here involves repeating some documentation (or omitting it) and I'm not sure it's needed given that the constructors it refers to are pub anyway. Reexporting the structs or functions that are part of the intended external API might be another way to expose the API without loosing the docs?

/// Create a `Gdal` wrapper for the given API reference.
pub(crate) fn new(api: &'static GdalApi) -> Self {
Self { api }
}

// -- Info ----------------------------------------------------------------

/// Return the name of the loaded GDAL library.
pub fn name(&self) -> &str {
self.api.name()
}

/// Fetch GDAL version information for a standard request key.
pub fn version_info(&self, request: &str) -> String {
Comment on lines +66 to +67
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are all a bit light on the documentation (maybe they could link to the underlying implementation's docs which I seem to remember have a bit more depth?). Also OK to defer since this is mostly intended to be for internal usage.

self.api.version_info(request)
}

// -- Config --------------------------------------------------------------

/// Set a thread-local GDAL configuration option.
pub fn set_thread_local_config_option(&self, key: &str, value: &str) -> Result<()> {
config::set_thread_local_config_option(self.api, key, value)
}

// -- Driver --------------------------------------------------------------

/// Fetch a GDAL driver by its short name.
pub fn get_driver_by_name(&self, name: &str) -> Result<Driver> {
DriverManager::get_driver_by_name(self.api, name)
}

// -- Dataset -------------------------------------------------------------

/// Open a dataset with extended options.
pub fn open_ex(
&self,
path: &str,
open_flags: GDALOpenFlags,
allowed_drivers: Option<&[&str]>,
open_options: Option<&[&str]>,
sibling_files: Option<&[&str]>,
) -> Result<Dataset> {
Dataset::open_ex(
self.api,
path,
open_flags,
allowed_drivers,
open_options,
sibling_files,
)
}

/// Open a dataset using a [`DatasetOptions`] struct.
pub fn open_ex_with_options(&self, path: &str, options: DatasetOptions<'_>) -> Result<Dataset> {
Dataset::open_ex_with_options(self.api, path, options)
}

// -- Spatial Reference ---------------------------------------------------

/// Create a spatial reference from a WKT string.
pub fn spatial_ref_from_wkt(&self, wkt: &str) -> Result<SpatialRef> {
SpatialRef::from_wkt(self.api, wkt)
}

// -- VRT -----------------------------------------------------------------

/// Create an empty VRT dataset with the given raster size.
pub fn create_vrt(&self, x_size: usize, y_size: usize) -> Result<VrtDataset> {
VrtDataset::create(self.api, x_size, y_size)
}

// -- Geometry ------------------------------------------------------------

/// Create a geometry from WKB bytes.
pub fn geometry_from_wkb(&self, wkb: &[u8]) -> Result<Geometry> {
Geometry::from_wkb(self.api, wkb)
}

/// Create a geometry from a WKT string.
pub fn geometry_from_wkt(&self, wkt: &str) -> Result<Geometry> {
Geometry::from_wkt(self.api, wkt)
}

// -- Vector --------------------------------------------------------------

/// Create an OGR field definition.
pub fn create_field_defn(&self, name: &str, field_type: OGRFieldType) -> Result<FieldDefn> {
FieldDefn::new(self.api, name, field_type)
}

// -- VSI (Virtual File System) -------------------------------------------

/// Create a VSI in-memory file from the given bytes.
pub fn create_mem_file(&self, file_name: &str, data: &[u8]) -> Result<()> {
vsi::create_mem_file(self.api, file_name, data)
}

/// Delete a VSI in-memory file.
pub fn unlink_mem_file(&self, file_name: &str) -> Result<()> {
vsi::unlink_mem_file(self.api, file_name)
}

/// Copy the bytes of a VSI in-memory file, taking ownership of the GDAL buffer.
pub fn get_vsi_mem_file_bytes_owned(&self, file_name: &str) -> Result<Vec<u8>> {
vsi::get_vsi_mem_file_bytes_owned(self.api, file_name)
}

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

/// Create a bare in-memory MEM dataset with GDAL-owned bands.
/// For a higher-level builder with external bands and metadata, use `MemDatasetBuilder`.
pub fn create_mem_dataset(
&self,
width: usize,
height: usize,
n_owned_bands: usize,
owned_bands_data_type: GdalDataType,
) -> Result<Dataset> {
create_mem_dataset(
self.api,
width,
height,
n_owned_bands,
owned_bands_data_type,
)
}

/// Rasterize geometries using the dataset geotransform as the transformer.
pub fn rasterize_affine(
&self,
dataset: &Dataset,
bands: &[usize],
geometries: &[Geometry],
burn_values: &[f64],
all_touched: bool,
) -> Result<()> {
rasterize_affine(
self.api,
dataset,
bands,
geometries,
burn_values,
all_touched,
)
}

/// Rasterize geometries into the selected dataset bands.
pub fn rasterize(
&self,
dataset: &Dataset,
band_list: &[i32],
geometries: &[&Geometry],
burn_values: &[f64],
options: Option<RasterizeOptions>,
) -> Result<()> {
rasterize(
self.api,
dataset,
band_list,
geometries,
burn_values,
options,
)
}

/// Polygonize a raster band into a vector layer.
pub fn polygonize(
&self,
src_band: &RasterBand<'_>,
mask_band: Option<&RasterBand<'_>>,
out_layer: &Layer<'_>,
pixel_value_field: i32,
options: &PolygonizeOptions,
) -> Result<()> {
polygonize(
self.api,
src_band,
mask_band,
out_layer,
pixel_value_field,
options,
)
}
}
1 change: 1 addition & 0 deletions c/sedona-gdal/src/gdal_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::gdal_dyn_bindgen::SedonaGdalApi;
/// initialization of [`GdalApi`] via [`GdalApi::try_from_shared_library`] or
/// [`GdalApi::try_from_current_process`], and you cannot obtain a `&GdalApi`
/// without successful initialization.
#[macro_export]
macro_rules! call_gdal_api {
($api:expr, $func:ident $(, $arg:expr)*) => {
if let Some(func) = $api.inner.$func {
Expand Down
11 changes: 11 additions & 0 deletions c/sedona-gdal/src/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
// under the License.

use crate::errors::GdalInitLibraryError;
use crate::gdal::Gdal;
use crate::gdal_api::GdalApi;
use std::path::PathBuf;
use std::sync::{Mutex, OnceLock};
Expand Down Expand Up @@ -203,6 +204,16 @@ where
Ok(func(api))
}

/// Execute a closure with the process-global high-level [`Gdal`] handle.
/// The global API is initialized lazily on first use and then reused.
pub fn with_global_gdal<F, R>(func: F) -> Result<R, GdalInitLibraryError>
where
F: FnOnce(&Gdal) -> R,
{
let api = get_global_gdal_api()?;
Ok(func(&Gdal::new(api)))
}

/// Verify that the GDAL library meets the minimum version requirement.
///
/// We use `GDALVersionInfo("VERSION_NUM")` instead of `GDALCheckVersion` because
Expand Down
2 changes: 2 additions & 0 deletions c/sedona-gdal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod gdal_dyn_bindgen;
pub mod errors;

// --- Core API ---
pub mod gdal;
pub mod gdal_api;
pub mod global;

Expand All @@ -32,6 +33,7 @@ pub mod cpl;
pub mod dataset;
pub mod driver;
pub mod geo_transform;
pub mod mem;
pub mod raster;
pub mod spatial_ref;
pub mod vector;
Expand Down
Loading
Loading