From c87e87f1655783831cb05bafa44711c560b5e932 Mon Sep 17 00:00:00 2001 From: caiubi Date: Wed, 27 Nov 2024 18:06:23 +0000 Subject: [PATCH] chore: prevents write_xy_narray from writing incomplete blocks and adds more tests --- lib/gdal/extensions/raster_band/extensions.rb | 4 +-- .../extensions/raster_band/io_extensions.rb | 23 +++++++++------- .../raster_band/io_extensions_spec.rb | 26 ++++++++++++++++++- .../extensions/raster_band_classifier_spec.rb | 1 + 4 files changed, 41 insertions(+), 13 deletions(-) diff --git a/lib/gdal/extensions/raster_band/extensions.rb b/lib/gdal/extensions/raster_band/extensions.rb index b24716ed..45e06635 100644 --- a/lib/gdal/extensions/raster_band/extensions.rb +++ b/lib/gdal/extensions/raster_band/extensions.rb @@ -30,7 +30,7 @@ def to_a # # @return [NArray] def to_na(to_data_type = nil) - narray = NArray.to_na(to_a) + narray = to_nna return narray unless to_data_type @@ -40,7 +40,7 @@ def to_na(to_data_type = nil) end def to_nna - Numo::NArray[to_a] + NArray.to_na(to_a) end # Each pixel of the raster projected using the dataset's geo_transform. diff --git a/lib/gdal/extensions/raster_band/io_extensions.rb b/lib/gdal/extensions/raster_band/io_extensions.rb index f2a269fe..555f6355 100644 --- a/lib/gdal/extensions/raster_band/io_extensions.rb +++ b/lib/gdal/extensions/raster_band/io_extensions.rb @@ -21,29 +21,32 @@ def readlines end end - # Writes a 2-dimensional NArray of (x, y) pixels to the raster band using - # {GDAL::RasterBand#raster_io}. It determines +x_size+ and +y_size+ for - # the {GDAL::RasterBand#raster_io} call using the dimensions of the array. - # + # Writes a 2-dimensional NArray of (x, y) pixels to the raster band block-by-block. + # Expects the input array to have the same shape as the raster band. # @param pixel_array [NArray] The 2d list of pixels. def write_xy_narray(pixel_array) data_pointer = FFI::MemoryPointer.new(:buffer_out, block_buffer_size) - read_start = 0 block_count[:y].times do |y_block_number| block_count[:x].times do |x_block_number| + # Create a block-sized NArray of zeros. This represents the block of pixels that will be written. + block_pixels = NArray.to_na(Array.new(block_size[:y]) { Array.new(block_size[:x], 0) }) + y_block_size = calculate_y_block_size(y_block_number) x_block_size = calculate_x_block_size(x_block_number) - pixel_count_per_block = x_block_size * y_block_size - read_range = (read_start...(read_start + pixel_count_per_block)) - pixels = pixel_array[read_range] - GDAL._write_pointer(data_pointer, data_type, pixels.to_a) + # Map the range of pixels corresponding to the current block. + source_x_range = x_block_number * block_size[:x]...(x_block_number * block_size[:x]) + x_block_size + source_y_range = y_block_number * block_size[:y]...(y_block_number * block_size[:y]) + y_block_size + + # Copy the corresponding pixels from the input array to the block pixels. + block_pixels[0...x_block_size, 0...y_block_size] = pixel_array[source_x_range, source_y_range] + + GDAL._write_pointer(data_pointer, data_type, block_pixels.to_a.flatten) write_block(x_block_number, y_block_number, data_pointer) data_pointer.clear - read_start = read_range.end end end end diff --git a/spec/unit/gdal/extensions/raster_band/io_extensions_spec.rb b/spec/unit/gdal/extensions/raster_band/io_extensions_spec.rb index 317804fe..c641ebad 100644 --- a/spec/unit/gdal/extensions/raster_band/io_extensions_spec.rb +++ b/spec/unit/gdal/extensions/raster_band/io_extensions_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "gdal/driver" -require "gdal/extensions/raster_band/io_extensions" +require "gdal/extensions/raster_band/extensions" RSpec.describe "GDAL::RasterBand::IOExtensions" do let(:driver) { GDAL::Driver.by_name("MEM") } @@ -156,6 +156,30 @@ end describe "#read_lines_by_block" do + context "block size is larger than the raster" do + let(:rows) { (1..4).map { |i| Array.new(15) { i } } } + + let(:dataset_path) do + filename = "/tmp/#{SecureRandom.uuid}.tif" + options = { BLOCKXSIZE: 256, BLOCKYSIZE: 256, TILED: "YES" } + GDAL::Driver.by_name("GTiff").create_dataset(filename, 15, 4, **options) do |dataset| + dataset.raster_band(1).write_xy_narray(NArray.to_na(rows)) + end + filename + end + + after { FileUtils.rm_f(dataset_path) } + + let(:dataset) { GDAL::Dataset.open(dataset_path, "r") } + + subject { dataset.raster_band(1) } + + it "yields all block rows with correct values" do + expect { |b| subject.read_lines_by_block(&b) } + .to yield_successive_args(*rows) + end + end + context "block is given" do it "yields each row of pixels" do expect { |b| subject.read_lines_by_block(&b) } diff --git a/spec/unit/gdal/extensions/raster_band_classifier_spec.rb b/spec/unit/gdal/extensions/raster_band_classifier_spec.rb index cdb8dec5..ab43b2c1 100644 --- a/spec/unit/gdal/extensions/raster_band_classifier_spec.rb +++ b/spec/unit/gdal/extensions/raster_band_classifier_spec.rb @@ -2,6 +2,7 @@ require "gdal/driver" require "gdal/extensions/raster_band_classifier" +require "gdal/extensions/raster_band/extensions" RSpec.describe GDAL::RasterBandClassifier do let(:driver) { GDAL::Driver.by_name("MEM") }