-
Notifications
You must be signed in to change notification settings - Fork 47
feat(sedona-gdal): add convenience facade and mem builder #697
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5756615
ef70c23
8809a0a
eb8c928
00f46e9
fb80d56
bc23f6f
7036c43
55dd466
319f310
43b3ba5
82b17ac
4409107
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
Kontinuation marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // -- 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, | ||
| ) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
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)