Skip to content

Commit

Permalink
Merge pull request telus-agcg#79 from syngenta/feat/initial-gdal-utils
Browse files Browse the repository at this point in the history
feat: initial GDAL Utils support
  • Loading branch information
tindron authored May 8, 2024
2 parents e95a920 + 7cf630d commit 039dfca
Show file tree
Hide file tree
Showing 42 changed files with 3,351 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/ffi/gdal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ module GDAL
autoload :Matching, File.expand_path("gdal/matching.rb", __dir__)
autoload :RPCInfo, File.expand_path("gdal/rpc_info.rb", __dir__)
autoload :TransformerInfo, File.expand_path("gdal/transformer_info.rb", __dir__)
autoload :Utils, File.expand_path("gdal/utils.rb", __dir__)
autoload :VRT, File.expand_path("gdal/vrt.rb", __dir__)
autoload :Warper, File.expand_path("gdal/warper.rb", __dir__)
autoload :WarpOptions, File.expand_path("gdal/warp_options.rb", __dir__)
Expand Down
984 changes: 984 additions & 0 deletions lib/ffi/gdal/utils.rb

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/gdal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ def gdal_require(path)
autoload :Options, gdal_require("gdal/options")
autoload :RasterAttributeTable, gdal_require("gdal/raster_attribute_table")
autoload :RasterBand, gdal_require("gdal/raster_band")
autoload :Utils, gdal_require("gdal/utils")
end

require_relative "gdal/exceptions"
Expand Down
22 changes: 22 additions & 0 deletions lib/gdal/utils.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

module GDAL
# Wrappers for the GDAL utilities.
#
# @see https://gdal.org/programs/index.html GDAL Utils documentation (gdalwarp, gdal_translate, ...).
# @see https://gdal.org/api/gdal_utils.html GDAL Utils C API.
module Utils
# Internal helpers
autoload :Helpers, File.expand_path("utils/helpers", __dir__)

# GDAL Utils
autoload :DEM, File.expand_path("utils/dem", __dir__)
autoload :Grid, File.expand_path("utils/grid", __dir__)
autoload :Nearblack, File.expand_path("utils/nearblack", __dir__)
autoload :Rasterize, File.expand_path("utils/rasterize", __dir__)
autoload :Info, File.expand_path("utils/info", __dir__)
autoload :Translate, File.expand_path("utils/translate", __dir__)
autoload :VectorTranslate, File.expand_path("utils/vector_translate", __dir__)
autoload :Warp, File.expand_path("utils/warp", __dir__)
end
end
108 changes: 108 additions & 0 deletions lib/gdal/utils/dem.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# frozen_string_literal: true

require_relative "dem/options"

module GDAL
module Utils
# Wrapper for gdaldem using GDALDEMProcessing C API.
#
# @see https://gdal.org/programs/gdaldem.html gdaldem utility documentation.
# @see https://gdal.org/api/gdal_utils.html#_CPPv417GDALDEMProcessingPKc12GDALDatasetHPKcPKcPK24GDALDEMProcessingOptionsPi
# GDALDEMProcessing C API.
class DEM
# Perform the gdaldem (GDALDEMProcessing) operation.
#
# @example Create a raster dataset.
# src_dataset = GDAL::Dataset.open("source.tif", "r")
#
# dataset = GDAL::Utils::DEM.perform(
# dst_dataset_path: "destination.tif",
# src_dataset: src_dataset,
# processing: "hillshade"
# )
#
# # Do something with the dataset.
# puts dataset.raster_x_size
#
# # You must close the dataset when you are done with it.
# dataset.close
# src_dataset.close
#
# @example Create a raster dataset for color-relief.
# src_dataset = GDAL::Dataset.open("source.tif", "r")
#
# dataset = GDAL::Utils::DEM.perform(
# dst_dataset_path: "destination.tif",
# src_dataset: src_dataset,
# processing: "color-relief",
# color_filename: "color.txt"
# )
#
# # Do something with the dataset.
# puts dataset.raster_x_size
#
# # You must close the dataset when you are done with it.
# dataset.close
# src_dataset.close
#
# @example Create a raster dataset with options.
# src_dataset = GDAL::Dataset.open("source.tif", "r")
# options = GDAL::Utils::DEM::Options.new(options: ["-of", "GTiff", "-co", "TILED=YES"])
#
# dataset = GDAL::Utils::DEM.perform(
# dst_dataset_path: "destination.tif",
# src_dataset: src_dataset,
# processing: "hillshade",
# options: options
# )
#
# # Do something with the dataset.
# puts dataset.raster_x_size
#
# # You must close the dataset when you are done with it.
# dataset.close
# src_dataset.close
#
# @example Create a raster dataset using block syntax.
# src_dataset = GDAL::Dataset.open("source.tif", "r")
#
# GDAL::Utils::DEM.perform(
# dst_dataset_path: "destination.tif",
# src_dataset: src_dataset,
# processing: "hillshade"
# ) do |dataset|
# # Do something with the dataset.
# puts dataset.raster_x_size
#
# # Dataset will be closed automatically.
# end
# src_dataset.close
#
# @param dst_dataset_path [String] The path to the destination dataset.
# @param src_dataset [OGR::DataSource] The source dataset.
# @param processing [String] The processing type
# (one of "hillshade", "slope", "aspect", "color-relief", "TRI", "TPI", "Roughness").
# @param color_filename [String] color file (mandatory for "color-relief" processing, should be NULL otherwise).
# @param options [GDAL::Utils::DEM::Options] Options.
# @yield [GDAL::Dataset] The destination dataset.
# @return [GDAL::Dataset] The destination dataset (only if block is not specified; dataset must be closed).
# @raise [GDAL::Error] If the operation fails.
def self.perform(dst_dataset_path:, src_dataset:, processing:, color_filename: nil, options: Options.new, &block)
result_code_ptr = ::FFI::MemoryPointer.new(:int)
dst_dataset_ptr = ::FFI::GDAL::Utils.GDALDEMProcessing(
dst_dataset_path,
src_dataset.c_pointer,
processing,
color_filename,
options.c_pointer,
result_code_ptr
)
success = result_code_ptr.read_int.zero?

raise ::GDAL::Error, "GDALDEMProcessing failed." if dst_dataset_ptr.null? || !success

::GDAL::Dataset.open(dst_dataset_ptr, "w", &block)
end
end
end
end
52 changes: 52 additions & 0 deletions lib/gdal/utils/dem/options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module GDAL
module Utils
class DEM
# Ruby wrapper for GDALDEMProcessingOptions C API (options for gdaldem utility).
#
# @see GDAL::Utils::DEM
# @see https://gdal.org/programs/gdaldem.html gdaldem utility documentation.
class Options
# @private
class AutoPointer < ::FFI::AutoPointer
# @param pointer [FFI::Pointer]
def self.release(pointer)
return unless pointer && !pointer.null?

::FFI::GDAL::Utils.GDALDEMProcessingOptionsFree(pointer)
end
end

# @return [AutoPointer] C pointer to the GDALDEMProcessingOptions.
attr_reader :c_pointer

# @return [Array<String>] The options.
attr_reader :options

# Create a new instance.
#
# @see https://gdal.org/programs/gdaldem.html
# List of available options could be found in gdaldem utility documentation.
#
# @example Create a new instance.
# options = GDAL::Utils::DEM::Options.new(options: ["-of", "GTiff", "-co", "COMPRESS=DEFLATE"])
#
# @param options [Array<String>] The options list.
def initialize(options: [])
@options = options
@string_list = ::GDAL::Utils::Helpers::StringList.new(strings: options)
@c_pointer = AutoPointer.new(options_pointer)
end

private

attr_reader :string_list

def options_pointer
::FFI::GDAL::Utils.GDALDEMProcessingOptionsNew(string_list.c_pointer, nil)
end
end
end
end
end
72 changes: 72 additions & 0 deletions lib/gdal/utils/grid.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require_relative "grid/options"

module GDAL
module Utils
# Wrapper for gdal_grid using GDALGrid C API.
#
# @see https://gdal.org/programs/gdal_grid.html gdal_grid utility documentation.
# @see https://gdal.org/api/gdal_utils.html#_CPPv48GDALGridPKc12GDALDatasetHPK15GDALGridOptionsPi GDALGrid C API.
class Grid
# Perform the gdal_grid (GDALGrid) operation.
#
# @example Create a raster dataset.
# src_dataset = GDAL::Dataset.open("source.tif", "r")
# dataset = GDAL::Utils::Grid.perform(dst_dataset_path: "destination.tif", src_dataset: src_dataset)
#
# # Do something with the dataset.
# puts dataset.raster_x_size
#
# # You must close the dataset when you are done with it.
# dataset.close
# src_dataset.close
#
# @example Create a raster dataset with options.
# src_dataset = GDAL::Dataset.open("source.tif", "r")
# dataset = GDAL::Utils::Grid.perform(
# dst_dataset_path: "destination.tif",
# src_dataset: src_dataset,
# options: GDAL::Utils::Grid::Options.new(options: ["-of", "GTiff", "-co", "COMPRESS=DEFLATE"])
# )
#
# # Do something with the dataset.
# puts dataset.raster_x_size
#
# # You must close the dataset when you are done with it.
# dataset.close
# src_dataset.close
#
# @example Create a raster dataset using block syntax.
# src_dataset = GDAL::Dataset.open("source.tif", "r")
# GDAL::Utils::Grid.perform(dst_dataset_path: "destination.tif", src_dataset: src_dataset) do |dataset|
# # Do something with the dataset.
# puts dataset.raster_x_size
#
# # Dataset will be closed automatically.
# end
# src_dataset.close
#
# @param dst_dataset_path [String] The path to the destination dataset.
# @param src_dataset [OGR::DataSource] The source dataset.
# @param options [GDAL::Utils::Grid::Options] Options.
# @yield [GDAL::Dataset] The destination dataset.
# @return [GDAL::Dataset] The destination dataset (only if block is not specified; dataset must be closed).
# @raise [GDAL::Error] If the operation fails.
def self.perform(dst_dataset_path:, src_dataset:, options: Options.new, &block)
result_code_ptr = ::FFI::MemoryPointer.new(:int)
dst_dataset_ptr = ::FFI::GDAL::Utils.GDALGrid(
dst_dataset_path,
src_dataset.c_pointer,
options.c_pointer,
result_code_ptr
)
success = result_code_ptr.read_int.zero?

raise ::GDAL::Error, "GDALGrid failed." if dst_dataset_ptr.null? || !success

::GDAL::Dataset.open(dst_dataset_ptr, "w", &block)
end
end
end
end
52 changes: 52 additions & 0 deletions lib/gdal/utils/grid/options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module GDAL
module Utils
class Grid
# Ruby wrapper for GDALGridOptions C API (options for gdal_grid utility).
#
# @see GDAL::Utils::Grid
# @see https://gdal.org/programs/gdal_grid.html gdal_grid utility documentation.
class Options
# @private
class AutoPointer < ::FFI::AutoPointer
# @param pointer [FFI::Pointer]
def self.release(pointer)
return unless pointer && !pointer.null?

::FFI::GDAL::Utils.GDALGridOptionsFree(pointer)
end
end

# @return [AutoPointer] C pointer to the GDALGridOptions.
attr_reader :c_pointer

# @return [Array<String>] The options.
attr_reader :options

# Create a new instance.
#
# @see https://gdal.org/programs/gdal_grid.html
# List of available options could be found in gdal_grid utility documentation.
#
# @example Create a new instance.
# options = GDAL::Utils::Grid::Options.new(options: ["-of", "GTiff", "-outsize", "10", "10"])
#
# @param options [Array<String>] The options list.
def initialize(options: [])
@options = options
@string_list = ::GDAL::Utils::Helpers::StringList.new(strings: options)
@c_pointer = AutoPointer.new(options_pointer)
end

private

attr_reader :string_list

def options_pointer
::FFI::GDAL::Utils.GDALGridOptionsNew(string_list.c_pointer, nil)
end
end
end
end
end
11 changes: 11 additions & 0 deletions lib/gdal/utils/helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

module GDAL
module Utils
# Internal helpers for GDAL Utils.
module Helpers
autoload :DatasetList, File.expand_path("helpers/dataset_list", __dir__)
autoload :StringList, File.expand_path("helpers/string_list", __dir__)
end
end
end
43 changes: 43 additions & 0 deletions lib/gdal/utils/helpers/dataset_list.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# frozen_string_literal: true

module GDAL
module Utils
module Helpers
# A basic wrapper for C Array of dataset handlers (e.g. GDALDatasetH *pahSrcDS).
#
# @private
# @note This class is intended only to be used internally in ffi-gdal. It's API may change.
# Do not use this class directly.
class DatasetList
# @return [FFI::Pointer] C pointer to the Array of dataset handlers (e.g. GDALDatasetH *pahSrcDS).
attr_reader :c_pointer

# @return [Array<GDAL::Dataset>] List of datasets.
attr_reader :datasets

# @param datasets [Array<GDAL::Dataset>] List of datasets.
def initialize(datasets: [])
@datasets = datasets
@c_pointer = datasets_pointer
end

# @return [Integer] The number of datasets in the list.
def count
dataset_pointers.count
end

private

def dataset_pointers
@dataset_pointers ||= datasets.map(&:c_pointer)
end

def datasets_pointer
::FFI::MemoryPointer
.new(:pointer, count)
.write_array_of_pointer(dataset_pointers)
end
end
end
end
end
Loading

0 comments on commit 039dfca

Please sign in to comment.