From b50a974d75b020c32e510667388753eb5ce61b03 Mon Sep 17 00:00:00 2001 From: "Carlos A. Parra F" Date: Sun, 9 Jul 2023 18:49:21 +0200 Subject: [PATCH 1/9] refactor: :art: Refactored the `GeoRaster::get_as_array()` function a bit. Refactored the `get_as_array()` function a bit (according to the TODO), so that it is a bit more compact and doesn't have any changes in functionality. --- src/raster-tile-extractor/GeoRaster.cpp | 174 ++++++++++-------------- 1 file changed, 74 insertions(+), 100 deletions(-) diff --git a/src/raster-tile-extractor/GeoRaster.cpp b/src/raster-tile-extractor/GeoRaster.cpp index caf53fc..2828c7d 100644 --- a/src/raster-tile-extractor/GeoRaster.cpp +++ b/src/raster-tile-extractor/GeoRaster.cpp @@ -84,27 +84,18 @@ void *GeoRaster::get_as_array() { // Depending on the image format, we need to structure the resulting array differently and/or // read multiple bands. - // TODO: Lots of code duplication down there, but due to C++'s lack of reflection and all the - // subtle differences, it's tricky to generalize it. A templated factory class might help, but - // seems overkill if (format == RF) { GDALRasterBand *band = data->GetRasterBand(1); float nodata = static_cast(band->GetNoDataValue()); - + float *array = new float[get_pixel_size_x() * get_pixel_size_y()]; + std::fill(array, array + get_pixel_size_x() * get_pixel_size_y(), nodata); if (usable_width <= 0 || usable_height <= 0) { // Empty results are still valid and should be treated normally, so return an array with // only 0s - float *target_array = new float[get_pixel_size_x() * get_pixel_size_y()]; - std::fill(target_array, target_array + get_pixel_size_x() * get_pixel_size_y(), - nodata); - return target_array; + return array; } - // Write the data directly into a float array. - float *array = new float[get_pixel_size_x() * get_pixel_size_y()]; - std::fill(array, array + get_pixel_size_x() * get_pixel_size_y(), nodata); - error = band->RasterIO( GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, usable_height, array + remainder_y_top * destination_window_size_pixels + remainder_x_left, @@ -116,102 +107,85 @@ void *GeoRaster::get_as_array() { // Delete array in case of error delete [] array; - } else if (format == RGBA) { - if (usable_width <= 0 || usable_height <= 0) { - // Empty results are still valid and should be treated normally, so return an array with - // only 0s - uint8_t *target_array = new uint8_t[get_size_in_bytes()]; - std::fill(target_array, target_array + get_size_in_bytes(), 0); - return target_array; - } - - // Write the data into a byte array like this: - // R R R - // G G G - // B B B - // A A A - // So that the result is RGBARGBARGBA. + } else { + // Otherwise we are dealing with only uint8_t *arrays + // The only thing that changes is what we put in the arrays uint8_t *array = new uint8_t[get_size_in_bytes()]; std::fill(array, array + get_size_in_bytes(), 0); - - for (int band_number = 1; band_number < 5; band_number++) { - GDALRasterBand *band = data->GetRasterBand(band_number); - - // Read into the array with 4 bytes between the pixels - error = band->RasterIO( - GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, - usable_height, - array + (remainder_y_top * destination_window_size_pixels + remainder_x_left) * 4 + - (band_number - 1), - target_width, target_height, GDT_Byte, 4, destination_window_size_pixels * 4, - &rasterio_args); - } - - if (error < CE_Failure) { return array; } - - // Delete array in case of error - delete [] array; - - } else if (format == RGB) { if (usable_width <= 0 || usable_height <= 0) { // Empty results are still valid and should be treated normally, so return an array with // only 0s - uint8_t *target_array = new uint8_t[get_size_in_bytes()]; - std::fill(target_array, target_array + get_size_in_bytes(), 0); - return target_array; + return array; } - - // Write the data into a byte array like this: - // R R R - // G G G - // B B B - // So that the result is RGBRGBRGB. - uint8_t *array = new uint8_t[get_size_in_bytes()]; - std::fill(array, array + get_size_in_bytes(), 0); - - for (int band_number = 1; band_number < 4; band_number++) { - GDALRasterBand *band = data->GetRasterBand(band_number); - - // Read into the array with 4 bytes between the pixels - error = band->RasterIO( - GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, - usable_height, - array + (remainder_y_top * destination_window_size_pixels + remainder_x_left) * 3 + - (band_number - 1), - target_width, target_height, GDT_Byte, 3, destination_window_size_pixels * 3, - &rasterio_args); + switch (format) { + case RGBA: + // Write the data into a byte array like this: + // R R R + // G G G + // B B B + // A A A + // So that the result is RGBARGBARGBA. + for (int band_number = 1; band_number < 5; band_number++) { + GDALRasterBand *band = data->GetRasterBand(band_number); + + // Read into the array with 4 bytes between the pixels + error = band->RasterIO( + GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, + usable_height, + array + (remainder_y_top * destination_window_size_pixels + remainder_x_left) * 4 + + (band_number - 1), + target_width, target_height, GDT_Byte, 4, destination_window_size_pixels * 4, + &rasterio_args); + } + + if (error < CE_Failure) { return array; } + + // Delete array in case of error + delete [] array; + break; + case RGB: + // Write the data into a byte array like this: + // R R R + // G G G + // B B B + // So that the result is RGBRGBRGB. + for (int band_number = 1; band_number < 4; band_number++) { + GDALRasterBand *band = data->GetRasterBand(band_number); + + // Read into the array with 4 bytes between the pixels + error = band->RasterIO( + GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, + usable_height, + array + (remainder_y_top * destination_window_size_pixels + remainder_x_left) * 3 + + (band_number - 1), + target_width, target_height, GDT_Byte, 3, destination_window_size_pixels * 3, + &rasterio_args); + } + + if (error < CE_Failure) { return array; } + + // Delete array in case of error + delete [] array; + break; + case BYTE: + GDALRasterBand *band = data->GetRasterBand(1); + error = band->RasterIO( + GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, usable_height, + array + remainder_y_top * destination_window_size_pixels + remainder_x_left, + target_width, target_height, GDT_Byte, 0, destination_window_size_pixels, + &rasterio_args); + + if (error < CE_Failure) { return array; } + + // Delete array in case of error + delete [] array; + break; + default: + // In case we had a not-supported format, delete the created array + delete [] array; + break; } - - if (error < CE_Failure) { return array; } - - // Delete array in case of error - delete [] array; - - } else if (format == BYTE) { - if (usable_width <= 0 || usable_height <= 0) { - // Empty results are still valid and should be treated normally, so return an array with - // only 0s - uint8_t *target_array = new uint8_t[get_size_in_bytes()]; - std::fill(target_array, target_array + get_size_in_bytes(), 0); - return target_array; - } - - GDALRasterBand *band = data->GetRasterBand(1); - uint8_t *array = new uint8_t[get_size_in_bytes()]; - std::fill(array, array + get_size_in_bytes(), 0); - - error = band->RasterIO( - GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, usable_height, - array + remainder_y_top * destination_window_size_pixels + remainder_x_left, - target_width, target_height, GDT_Byte, 0, destination_window_size_pixels, - &rasterio_args); - - if (error < CE_Failure) { return array; } - - // Delete array in case of error - delete [] array; } - // If nothing worked, return null return nullptr; } From 5d3f1e36b7c8a5b0100944ce70ba493260c4ff7f Mon Sep 17 00:00:00 2001 From: "Carlos A. Parra F" Date: Sun, 9 Jul 2023 19:04:13 +0200 Subject: [PATCH 2/9] refactor: Removed one useless TODO and completed another one Removed useless TODO of "throwing error on type mismatch" and used solution of said TODO to complete another similar TODO. --- src/geodata.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/geodata.cpp b/src/geodata.cpp index 9302ae8..0253d39 100644 --- a/src/geodata.cpp +++ b/src/geodata.cpp @@ -435,7 +435,7 @@ void GeoRasterLayer::set_value_at_position(double pos_x, double pos_y, Variant v delete[] values; } else { - // TODO: Output an error since there was apparently a type mis-match + std::cout << "Type mismatch: value of type " << value.get_type() << " and dataset of type " << get_format() << std::endl; } dataset->dataset->FlushCache(); @@ -527,7 +527,6 @@ void GeoRasterLayer::overlay_image_at_position(double pos_x, double pos_y, Refget_format() << " and dataset of type " << get_format() << std::endl; } } From 7de2b0ea9e7b64b19d31aee9c989828c50861b0e Mon Sep 17 00:00:00 2001 From: "Carlos A. Parra F" Date: Wed, 12 Jul 2023 16:29:56 +0200 Subject: [PATCH 3/9] refactor: Adapted the GeoRaster::get_format() function to be a bit more precise about result. The get_format function (and its enum) have been expanded on an extra value, that will be returned when a raster has multiple bands of different (mismatched) types. --- src/raster-tile-extractor/GeoRaster.cpp | 47 +++++++++++++++++-------- src/raster-tile-extractor/GeoRaster.h | 13 +++---- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/raster-tile-extractor/GeoRaster.cpp b/src/raster-tile-extractor/GeoRaster.cpp index 2828c7d..b36daa9 100644 --- a/src/raster-tile-extractor/GeoRaster.cpp +++ b/src/raster-tile-extractor/GeoRaster.cpp @@ -118,7 +118,7 @@ void *GeoRaster::get_as_array() { return array; } switch (format) { - case RGBA: + case RGBA: { // Write the data into a byte array like this: // R R R // G G G @@ -143,7 +143,8 @@ void *GeoRaster::get_as_array() { // Delete array in case of error delete [] array; break; - case RGB: + } + case RGB: { // Write the data into a byte array like this: // R R R // G G G @@ -151,7 +152,6 @@ void *GeoRaster::get_as_array() { // So that the result is RGBRGBRGB. for (int band_number = 1; band_number < 4; band_number++) { GDALRasterBand *band = data->GetRasterBand(band_number); - // Read into the array with 4 bytes between the pixels error = band->RasterIO( GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, @@ -167,7 +167,8 @@ void *GeoRaster::get_as_array() { // Delete array in case of error delete [] array; break; - case BYTE: + } + case BYTE: { GDALRasterBand *band = data->GetRasterBand(1); error = band->RasterIO( GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, usable_height, @@ -180,10 +181,12 @@ void *GeoRaster::get_as_array() { // Delete array in case of error delete [] array; break; - default: + } + default: { // In case we had a not-supported format, delete the created array delete [] array; break; + } } } // If nothing worked, return null @@ -258,17 +261,31 @@ uint64_t *GeoRaster::get_histogram() { GeoRaster::FORMAT GeoRaster::get_format_for_dataset(GDALDataset *data) { int raster_count = data->GetRasterCount(); - GDALDataType raster_type = data->GetRasterBand(1)->GetRasterDataType(); - - if (raster_type == GDT_Byte) { - if (raster_count == 4) { - return RGBA; - } else if (raster_count == 3) { - return RGB; - } else { - return BYTE; + GDALDataType first_raster_type = data->GetRasterBand(1)->GetRasterDataType(); + bool raster_types_mismatch = false; + for (int next = 2; next < raster_count + 1; next++) { + GDALDataType next_raster_type = data->GetRasterBand(next)->GetRasterDataType(); + if (next_raster_type != first_raster_type) { + raster_types_mismatch = true; + break; + } + } + + if (first_raster_type == GDT_Byte) { + switch (raster_count) { + case 4: return RGBA; + case 3: return RGB; + default: { + if (raster_types_mismatch) { + return MIXED; + } + return BYTE; + } + } + } else if (first_raster_type == GDT_Float32 || first_raster_type == GDT_Float64) { + if (raster_types_mismatch) { + return MIXED; } - } else if (raster_type == GDT_Float32 || raster_type == GDT_Float64) { return RF; } else { return UNKNOWN; diff --git a/src/raster-tile-extractor/GeoRaster.h b/src/raster-tile-extractor/GeoRaster.h index ac2d263..949e3bb 100644 --- a/src/raster-tile-extractor/GeoRaster.h +++ b/src/raster-tile-extractor/GeoRaster.h @@ -11,12 +11,13 @@ class GDALDataset; /// Provides easy access without GDAL dependencies to library users. class GeoRaster { public: - enum FORMAT { - RGB, // 3 8-bit int channels - RGBA, // 4 8-bit int channels - RF, // 1 32-bit float channel - BYTE, // 1 8-bit int channel - UNKNOWN + enum FORMAT { // size (bits) | data type | Number of chanels + RGB, // 8 | int | 3 + RGBA, // 8 | int | 4 + RF, // 32 | float | X >= 1 + BYTE, // 8 | int | X >= 1 + MIXED, // 8<=X<=64 | int and/or float | X >= 2 + UNKNOWN // unknown | unknown | X >= 1 }; GeoRaster(GDALDataset *data, int interpolation_type); From cc42ccacd86198b0cb647c18a9928467d8ad6388 Mon Sep 17 00:00:00 2001 From: "Carlos A. Parra F" Date: Thu, 20 Jul 2023 14:37:22 +0200 Subject: [PATCH 4/9] refactor: Refactored `set_raster` function to avoid repetition and make it more readable. Used some DRY and switch-case principles to make the `set_raster` function somewhat more readable, for the following changes. --- src/geoimage.cpp | 123 +++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 63 deletions(-) diff --git a/src/geoimage.cpp b/src/geoimage.cpp index 4bd6b52..e5d2486 100644 --- a/src/geoimage.cpp +++ b/src/geoimage.cpp @@ -51,69 +51,7 @@ void GeoImage::set_raster(GeoRaster *raster, int interpolation) { // Image->create_from_data. GeoRaster::FORMAT format = raster->get_format(); - if (format == GeoRaster::RGB) { - uint8_t *data = (uint8_t *)raster->get_as_array(); - - if (data == nullptr) return; - - // This copy is straightforward since the result if - // ImageRaster::get_data is already in the correct format. Format of the - // PoolByteArray: (RGB)(RGB)(RGB)... - for (int i = 0; i < img_size_x * img_size_y * 3; i++) { - pba.set(i, data[i]); - } - - // All content of data is now in pba, so we can delete it - delete[] data; - - // Create an image from the PoolByteArray - image = - Image::create_from_data(img_size_x, img_size_y, false, Image::Format::FORMAT_RGB8, pba); - } else if (format == GeoRaster::RGBA) { - uint8_t *data = (uint8_t *)raster->get_as_array(); - - if (data == nullptr) return; - - // Copy each of the 4 bands - // Format of the PoolByteArray: (RGBA)(RGBA)(RGBA)... - for (int y = 0; y < img_size_y; y++) { - for (int x = 0; x < img_size_x; x++) { - int rgba_index = y * img_size_x + x; - pba.set(index++, data[rgba_index * 4 + 0]); - pba.set(index++, data[rgba_index * 4 + 1]); - pba.set(index++, data[rgba_index * 4 + 2]); - pba.set(index++, data[rgba_index * 4 + 3]); - } - } - - // All content of data is now in pba, so we can delete it - delete[] data; - - // Create an image from the PoolByteArray - image = Image::create_from_data(img_size_x, img_size_y, false, Image::Format::FORMAT_RGBA8, - pba); - } else if (format == GeoRaster::BYTE) { - uint8_t *data = (uint8_t *)raster->get_as_array(); - - if (data == nullptr) return; - - // 1:1 copy - // Format of the PoolByteArray: (B)(B)(B)... - for (int y = 0; y < img_size_y; y++) { - for (int x = 0; x < img_size_x; x++) { - // We need to convert the float into 4 bytes because that's the - // format Godot expects - pba.set(index++, data[y * img_size_x + x]); - } - } - - // All content of data is now in pba, so we can delete it - delete[] data; - - // Create an image from the PoolByteArray - image = - Image::create_from_data(img_size_x, img_size_y, false, Image::Format::FORMAT_R8, pba); - } else if (format == GeoRaster::RF) { + if (format == GeoRaster::RF) { float *data = (float *)raster->get_as_array(); if (data == nullptr) return; @@ -144,6 +82,65 @@ void GeoImage::set_raster(GeoRaster *raster, int interpolation) { // Create an image from the PoolByteArray image = Image::create_from_data(img_size_x, img_size_y, false, Image::Format::FORMAT_RF, pba); + } else { + uint8_t *data = (uint8_t *)raster->get_as_array(); + Image::Format img_format = Image::Format::FORMAT_MAX; + if (data == nullptr) { return; } + switch (format) { + case GeoRaster::BYTE: { + // 1:1 copy + // Format of the PoolByteArray: (B)(B)(B)... + for (int y = 0; y < img_size_y; y++) { + for (int x = 0; x < img_size_x; x++) { + // We need to convert the float into 4 bytes because that's the + // format Godot expects + pba.set(index++, data[y * img_size_x + x]); + } + } + img_format = Image::Format::FORMAT_R8; + break; + } + case GeoRaster::RGB: { + // This copy is straightforward since the result if + // ImageRaster::get_data is already in the correct format. Format of the + // PoolByteArray: (RGB)(RGB)(RGB)... + for (int i = 0; i < img_size_x * img_size_y * 3; i++) { + pba.set(i, data[i]); + } + img_format = Image::Format::FORMAT_RGB8; + break; + } + case GeoRaster::RGBA: { + // Copy each of the 4 bands + // Format of the PoolByteArray: (RGBA)(RGBA)(RGBA)... + for (int y = 0; y < img_size_y; y++) { + for (int x = 0; x < img_size_x; x++) { + int rgba_index = y * img_size_x + x; + pba.set(index++, data[rgba_index * 4 + 0]); + pba.set(index++, data[rgba_index * 4 + 1]); + pba.set(index++, data[rgba_index * 4 + 2]); + pba.set(index++, data[rgba_index * 4 + 3]); + } + } + img_format = Image::Format::FORMAT_RGBA8; + break; + } + case GeoRaster::MIXED: { + // TODO: figure out a way to correctly extract mixed data from a Raster effectively + break; + } + default: + // Unknown/unsupported data + break; + } + // All content of data is now in pba, so we can delete it + delete[] data; + if (img_format == Image::Format::FORMAT_MAX) { + // The format wasn't set, so it was an unknown type. + return; + } + image = Image::create_from_data(img_size_x, img_size_y, false, img_format, + pba); } validity = true; From fe54111d799ed0097317c019177fb8e9e3d0b28c Mon Sep 17 00:00:00 2001 From: "Carlos A. Parra F" Date: Thu, 13 Jul 2023 17:24:07 +0200 Subject: [PATCH 5/9] feat: :sparkles: Added option to extract information from particular band layers. Implemented functionality to extract a particular RasterBand (band_index) from a GDALDataset. --- src/geodata.cpp | 105 ++++++++++++--- src/geodata.h | 34 ++++- src/geoimage.cpp | 83 ++++++++++++ src/geoimage.h | 3 + src/raster-tile-extractor/GeoRaster.cpp | 168 ++++++++++++++++++++---- src/raster-tile-extractor/GeoRaster.h | 33 ++++- src/vector-extractor/NativeDataset.cpp | 14 ++ src/vector-extractor/NativeDataset.h | 3 + 8 files changed, 393 insertions(+), 50 deletions(-) diff --git a/src/geodata.cpp b/src/geodata.cpp index 0253d39..c401915 100644 --- a/src/geodata.cpp +++ b/src/geodata.cpp @@ -9,6 +9,7 @@ #include "vector-extractor/VectorExtractor.h" #include +#include namespace godot { @@ -21,9 +22,11 @@ void GeoDataset::_bind_methods() { ClassDB::bind_method(D_METHOD("get_file_info"), &GeoDataset::get_file_info); ClassDB::bind_method(D_METHOD("has_write_access"), &GeoDataset::has_write_access); ClassDB::bind_method(D_METHOD("get_raster_layers"), &GeoDataset::get_raster_layers); + ClassDB::bind_method(D_METHOD("get_raster_layers_by_band"), &GeoDataset::get_raster_layers_by_band); ClassDB::bind_method(D_METHOD("get_feature_layers"), &GeoDataset::get_feature_layers); - ClassDB::bind_method(D_METHOD("get_raster_layer", "name"), &GeoDataset::get_raster_layer); + ClassDB::bind_method(D_METHOD("get_raster_layer", "name", "band_index"), &GeoDataset::get_raster_layer); ClassDB::bind_method(D_METHOD("get_feature_layer", "name"), &GeoDataset::get_feature_layer); + ClassDB::bind_method(D_METHOD("get_raster_count"), &GeoDataset::get_raster_count); ClassDB::bind_method(D_METHOD("load_from_file", "file_path", "write_access"), &GeoDataset::load_from_file); } @@ -49,14 +52,26 @@ Array GeoDataset::get_raster_layers() { Array layers = Array(); std::vector names = dataset->get_raster_layer_names(); - - for (std::string name : names) { - layers.append(get_raster_layer(name.c_str())); + int total_names = names.size(); + for (int i = 0; i < total_names; i++ ) { + std::string name = names[i]; + layers.append(get_raster_layer(name.c_str(), i + 1)); } return layers; } +Array GeoDataset::get_raster_layers_by_band() { + Array result = Array(); + + std::vector descriptions = dataset->get_raster_band_descriptions(); + for (int i = 0; i < descriptions.size(); i++) { + std::string description = descriptions[i]; + result.append(get_raster_band(description.c_str(), i + 1)); + } + return result; +} + Array GeoDataset::get_feature_layers() { Array layers = Array(); @@ -69,13 +84,26 @@ Array GeoDataset::get_feature_layers() { return layers; } -Ref GeoDataset::get_raster_layer(String name) { +Ref GeoDataset::get_raster_layer(String name, int band_index) { Ref raster_layer; raster_layer.instantiate(); raster_layer->set_native_dataset(dataset->get_subdataset(name.utf8().get_data())); raster_layer->set_name(name); raster_layer->set_origin_dataset(this); + raster_layer->set_band_index(band_index); + + return raster_layer; +} + +Ref GeoDataset::get_raster_band(String description, int band_index) { + Ref raster_layer; + raster_layer.instantiate(); + + raster_layer->set_native_dataset(std::shared_ptr(this->dataset)); + raster_layer->set_name(description); + raster_layer->set_origin_dataset(this); + raster_layer->set_band_index(band_index); return raster_layer; } @@ -102,6 +130,13 @@ void GeoDataset::set_native_dataset(std::shared_ptr new_dataset) dataset = new_dataset; } +int GeoDataset::get_raster_count() { + if (dataset == nullptr || !this->is_valid()) { + return 0; + } + return dataset->dataset->GetRasterCount(); +} + void GeoFeatureLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_valid"), &GeoFeatureLayer::is_valid); ClassDB::bind_method(D_METHOD("get_file_info"), &GeoFeatureLayer::get_file_info); @@ -280,6 +315,9 @@ void GeoRasterLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_image", "top_left_x", "top_left_y", "size_meters", "img_size", "interpolation_type"), &GeoRasterLayer::get_image); + ClassDB::bind_method(D_METHOD("get_band_image", "top_left_x", "top_left_y", "size_meters", + "img_size", "interpolation_type"), + &GeoRasterLayer::get_band_image); ClassDB::bind_method(D_METHOD("get_value_at_position", "pos_x", "pos_y"), &GeoRasterLayer::get_value_at_position); ClassDB::bind_method(D_METHOD("get_value_at_position_with_resolution"), @@ -295,6 +333,7 @@ void GeoRasterLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_center"), &GeoRasterLayer::get_center); ClassDB::bind_method(D_METHOD("get_min"), &GeoRasterLayer::get_min); ClassDB::bind_method(D_METHOD("get_max"), &GeoRasterLayer::get_max); + ClassDB::bind_method(D_METHOD("get_band_index"), &GeoRasterLayer::get_band_index); ClassDB::bind_method(D_METHOD("get_pixel_size"), &GeoRasterLayer::get_pixel_size); ClassDB::bind_method(D_METHOD("clone"), &GeoRasterLayer::clone); ClassDB::bind_method(D_METHOD("load_from_file", "file_path", "write_access"), @@ -330,17 +369,18 @@ Dictionary GeoRasterLayer::get_file_info() { Image::Format GeoRasterLayer::get_format() { GeoRaster::FORMAT format = GeoRaster::get_format_for_dataset(dataset->dataset); - if (format == GeoRaster::BYTE) { - return Image::FORMAT_R8; - } else if (format == GeoRaster::RF) { - return Image::FORMAT_RF; - } else if (format == GeoRaster::RGB) { - return Image::FORMAT_RGB8; - } else if (format == GeoRaster::RGBA) { - return Image::FORMAT_RGBA8; - } else { - // FORMAT_MAX is returned as a fallback - return Image::FORMAT_MAX; + switch (format) { + case GeoRaster::BYTE: + return Image::FORMAT_R8; + case GeoRaster::RF: + return Image::FORMAT_RF; + case GeoRaster::RGB: + return Image::FORMAT_RGB8; + case GeoRaster::RGBA: + return Image::FORMAT_RGBA8; + default: + // FORMAT_MAX is returned as a fallback for mixed, and unknown + return Image::FORMAT_MAX; } } @@ -376,6 +416,26 @@ Ref GeoRasterLayer::get_image(double top_left_x, double top_left_y, do return image; } +Ref GeoRasterLayer::get_band_image(double top_left_x, double top_left_y, double size_meters, + int img_size, int interpolation_type) { + Ref image; + image.instantiate(); + if (dataset == nullptr || !dataset->is_valid()) { + UtilityFunctions::push_error("Raster layer '", get_name(), "', index '", + band_index, "', is invalid. cannot get_band_image!"); + return image; + } + GeoRaster *raster = RasterTileExtractor::get_tile_from_dataset( + dataset->dataset, top_left_x, top_left_y, size_meters, img_size, interpolation_type); + if (raster == nullptr) { + UtilityFunctions::push_error("No valid data was available in the raster layer '", + get_name(), "', index '", band_index, "', at the requested position position!"); + return image; + } + image->set_raster_from_band(raster, interpolation_type, band_index); + return image; +} + float GeoRasterLayer::get_value_at_position(double pos_x, double pos_y) { // 0.0001 meters are used for the size because it can't be 0, but should be a pinpoint value. return get_value_at_position_with_resolution(pos_x, pos_y, 0.0001); @@ -444,7 +504,7 @@ void GeoRasterLayer::set_value_at_position(double pos_x, double pos_y, Variant v void GeoRasterLayer::smooth_add_value_at_position(double pos_x, double pos_y, double summand, double radius) { // FIXME: Like overlay_image_at_position, this could be done much more efficiently by batch-reading and writing - + float resolution = get_pixel_size(); for (float offset_x = -radius; offset_x <= radius; offset_x += resolution) { @@ -557,6 +617,14 @@ void GeoRasterLayer::set_origin_dataset(Ref dataset) { this->origin_dataset = dataset; } +void GeoRasterLayer::set_band_index(int band_index) { + this->band_index = band_index; +} + +int GeoRasterLayer::get_band_index() { + return this->band_index; +} + Ref GeoRasterLayer::clone() { Ref layer_clone; layer_clone.instantiate(); @@ -564,13 +632,14 @@ Ref GeoRasterLayer::clone() { layer_clone->set_native_dataset(dataset->clone()); layer_clone->set_origin_dataset(origin_dataset); layer_clone->set_name(get_name()); + layer_clone->set_band_index(this->band_index); return layer_clone; } void GeoRasterLayer::load_from_file(String file_path, bool write_access) { this->write_access = write_access; - + this->band_index = 1; // This is the default index if none is set explicitly dataset = VectorExtractor::open_dataset(file_path.utf8().get_data(), write_access); // TODO: Might be better to produce a hard crash here, but CRASH_COND doesn't have the desired diff --git a/src/geodata.h b/src/geodata.h index d1be0ed..918b104 100644 --- a/src/geodata.h +++ b/src/geodata.h @@ -129,6 +129,14 @@ class EXPORT GeoRasterLayer : public Resource { Ref get_image(double top_left_x, double top_left_y, double size_meters, int img_size, int interpolation_type); + /// Like get_image but only returns the GeoImage of a single Band. + /// Each GeoRasterLayer has an index that represents the RasterBand index from where the + /// the information is taken in the Dataset. Bands that have been initialized without a specific + /// index will (probably erroneously) be assigned index 1. + /// TODO: Make sure that all layers get a correctly assigned index. + Ref get_band_image(double top_left_x, double top_left_y, double size_meters, int img_size, + int interpolation_type); + /// Returns the value in the GeoRasterLayer at exactly the given position. /// Note that when reading many values from a confined area, it is more efficient to call /// get_image and read the pixels from there. @@ -189,12 +197,22 @@ class EXPORT GeoRasterLayer : public Resource { /// Not exposed to Godot since it should never construct GeoFeatureLayers by hand. void set_origin_dataset(Ref dataset); + /// Get the band_index that this raster_layer is set to. + int get_band_index(); + + /// @brief Sets the band_index for this layer. + /// + /// Not exposed to Godot, since it's only meant for cloning/internal use. + void set_band_index(int band_index); + bool write_access; private: Ref origin_dataset; std::shared_ptr dataset; ExtentData extent_data; + // The Band (in the Raster) that this GeoRasterLayer represents + int band_index; }; /// A dataset which contains layers of geodata. @@ -221,13 +239,22 @@ class EXPORT GeoDataset : public Resource { /// Return all GeoRasterLayers objects for this dataset. Array get_raster_layers(); + /// Like `get_raster_layers`, but instead of looking for them by name, looking by index + Array get_raster_layers_by_band(); + /// Return all GeoFeatureLayer objects for this dataset. Array get_feature_layers(); /// Returns a GeoRasterLayer object of the layer within this dataset with /// the given name. It is recommended to check the validity of the returned /// object with GeoRasterLayer::is_valid(). - Ref get_raster_layer(String name); + Ref get_raster_layer(String name, int band_index); + + /// Like `get_raster_layer` returns a GeoRasterLayer. + /// Different to `get_raster_layer` it not attempt to extract (possibly) + /// non-existent SUBDATASETS from the main Dataset and just sets the main + /// dataset as the native dataset of the Layer. + Ref get_raster_band(String description, int band_index); /// Returns a GeoFeatureLayer object of the layer within this dataset with /// the given name. It is recommended to check the validity of the returned @@ -243,6 +270,11 @@ class EXPORT GeoDataset : public Resource { /// is only for internal use. void set_native_dataset(std::shared_ptr new_dataset); + /// @brief Get the total amount of raster bands contained in the dataset. + /// Returns 0 if dataset is not valid + /// @return the total amount of raster bands in the dataset. + int get_raster_count(); + bool write_access; std::shared_ptr dataset; diff --git a/src/geoimage.cpp b/src/geoimage.cpp index e5d2486..943e974 100644 --- a/src/geoimage.cpp +++ b/src/geoimage.cpp @@ -1,4 +1,5 @@ #include "geoimage.h" +#include "GeoRaster.h" #include @@ -146,6 +147,88 @@ void GeoImage::set_raster(GeoRaster *raster, int interpolation) { validity = true; } +void GeoImage::set_raster_from_band(GeoRaster *raster, int interpolation, int band_index) { + this->raster = raster; + this->interpolation = interpolation; + int size = raster->get_pixel_size_x() * raster->get_pixel_size_y(); + GeoRaster::FORMAT band_format = raster->get_band_format(band_index); + if (band_format == GeoRaster::RF) { + // Only two types currently supported at the moment FLOAT and BYTE + // and the former is 4 x bigger than the latter. + size *= 4; + } + + PackedByteArray pba; + + // Multiply by 4 since we want to put 32-float values into a byte array + pba.resize(size); + + int index = 0; + int img_size_x = raster->get_pixel_size_x(); + int img_size_y = raster->get_pixel_size_y(); + + // Depending on the data type, the insertion of the raw image into the + // PoolByteArray is different. The format is dependent on how Godot handles + // Image->create_from_data. + + switch (band_format) { + case GeoRaster::RF: { + float *data = (float *)raster->get_band_as_array(band_index); + + if (data == nullptr) return; + // Convert the float into 4 bytes and add those to the array + // Format of the PoolByteArray: (F1F2F3F4)(F1F2F3F4)(F1F2F3F4)... + union { + float fval; + int8_t bval[4]; + } floatAsBytes; + + for (int y = 0; y < img_size_y; y++) { + for (int x = 0; x < img_size_x; x++) { + floatAsBytes.fval = data[y * img_size_x + x]; + + pba.set(index, floatAsBytes.bval[0]); + pba.set(++index, floatAsBytes.bval[1]); + pba.set(++index, floatAsBytes.bval[2]); + pba.set(++index, floatAsBytes.bval[3]); + + index++; + } + } + // All content of data is now in pba, so we can delete it + delete[] data; + // Create an image from the PoolByteArray + image = + Image::create_from_data(img_size_x, img_size_y, false, Image::Format::FORMAT_RF, pba); + break; + } + case GeoRaster::BYTE: { + uint8_t *data = (uint8_t *)raster->get_band_as_array(band_index); + Image::Format img_format = Image::Format::FORMAT_R8; + if (data == nullptr) { return; } + // 1:1 copy + // Format of the PoolByteArray: (B)(B)(B)... + for (int y = 0; y < img_size_y; y++) { + for (int x = 0; x < img_size_x; x++) { + // We need to convert the float into 4 bytes because that's the + // format Godot expects + pba.set(index++, data[y * img_size_x + x]); + } + } + // All content of data is now in pba, so we can delete it + delete[] data; + image = Image::create_from_data(img_size_x, img_size_y, false, img_format, + pba); + break; + } + default: + break; + } + + validity = true; + +} + Ref GeoImage::get_image() { return image; } diff --git a/src/geoimage.h b/src/geoimage.h index 71901fe..19e5fbe 100644 --- a/src/geoimage.h +++ b/src/geoimage.h @@ -53,6 +53,9 @@ class EXPORT GeoImage : public Resource { /// which already have raster data. void set_raster(GeoRaster *raster, int interpolation); + /// Like `set_raster` but uses only the band at band_index from raster. + void set_raster_from_band(GeoRaster *raster, int interpolation, int band_index); + /// Get a Godot Image with the GeoImage's data Ref get_image(); diff --git a/src/raster-tile-extractor/GeoRaster.cpp b/src/raster-tile-extractor/GeoRaster.cpp index b36daa9..4fcecd4 100644 --- a/src/raster-tile-extractor/GeoRaster.cpp +++ b/src/raster-tile-extractor/GeoRaster.cpp @@ -3,19 +3,8 @@ #include // For std::clamp etc #include -void *GeoRaster::get_as_array() { - GDALRasterIOExtraArg rasterio_args; - INIT_RASTERIO_EXTRA_ARG(rasterio_args); - - int interpolation = interpolation_type; - - // If we're requesting downscaled data, always use nearest neighbour scaling. - // TODO: Would be good if this could be overridden with an optional parameter, but any other - // scaling usually causes very long loading times so this is the default for now - if (destination_window_size_pixels < source_window_size_pixels) { interpolation = 0; } - - rasterio_args.eResampleAlg = static_cast(interpolation); - +RasterIOHelper GeoRaster::get_raster_io_helper() { + RasterIOHelper result; // Restrict the offset and extent to the available data // TODO: Handle properly: // 1. Create array using size destination_window_size_pixels @@ -77,6 +66,32 @@ void *GeoRaster::get_as_array() { target_height = destination_window_size_pixels; } + result.clamped_pixel_offset_x = clamped_pixel_offset_x; + result.clamped_pixel_offset_y = clamped_pixel_offset_y; + result.min_raster_size = min_raster_size; + result.remainder_x_left = remainder_x_left; + result.remainder_y_top = remainder_y_top; + result.target_height = target_width; + result.target_width = target_width; + result.usable_height = usable_height; + result.usable_width = usable_width; + return result; +} + +void *GeoRaster::get_as_array() { + GDALRasterIOExtraArg rasterio_args; + INIT_RASTERIO_EXTRA_ARG(rasterio_args); + + int interpolation = interpolation_type; + + // If we're requesting downscaled data, always use nearest neighbour scaling. + // TODO: Would be good if this could be overridden with an optional parameter, but any other + // scaling usually causes very long loading times so this is the default for now + if (destination_window_size_pixels < source_window_size_pixels) { interpolation = 0; } + + rasterio_args.eResampleAlg = static_cast(interpolation); + + RasterIOHelper helper = get_raster_io_helper(); // TODO: We could do more precise error handling by getting the error number using // CPLGetLastErrorNo() and returning that to the user somehow - maybe a flag in the // GeoRaster. @@ -90,16 +105,16 @@ void *GeoRaster::get_as_array() { float nodata = static_cast(band->GetNoDataValue()); float *array = new float[get_pixel_size_x() * get_pixel_size_y()]; std::fill(array, array + get_pixel_size_x() * get_pixel_size_y(), nodata); - if (usable_width <= 0 || usable_height <= 0) { + if (helper.usable_width <= 0 || helper.usable_height <= 0) { // Empty results are still valid and should be treated normally, so return an array with // only 0s return array; } error = band->RasterIO( - GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, usable_height, - array + remainder_y_top * destination_window_size_pixels + remainder_x_left, - target_width, target_height, GDT_Float32, 0, destination_window_size_pixels * 4, + GF_Read, helper.clamped_pixel_offset_x, helper.clamped_pixel_offset_y, helper.usable_width, helper.usable_height, + array + helper.remainder_y_top * destination_window_size_pixels + helper.remainder_x_left, + helper.target_width, helper.target_height, GDT_Float32, 0, destination_window_size_pixels * 4, &rasterio_args); if (error < CE_Failure) { return array; } @@ -112,7 +127,7 @@ void *GeoRaster::get_as_array() { // The only thing that changes is what we put in the arrays uint8_t *array = new uint8_t[get_size_in_bytes()]; std::fill(array, array + get_size_in_bytes(), 0); - if (usable_width <= 0 || usable_height <= 0) { + if (helper.usable_width <= 0 || helper.usable_height <= 0) { // Empty results are still valid and should be treated normally, so return an array with // only 0s return array; @@ -130,11 +145,11 @@ void *GeoRaster::get_as_array() { // Read into the array with 4 bytes between the pixels error = band->RasterIO( - GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, - usable_height, - array + (remainder_y_top * destination_window_size_pixels + remainder_x_left) * 4 + + GF_Read, helper.clamped_pixel_offset_x, helper.clamped_pixel_offset_y, helper.usable_width, + helper.usable_height, + array + (helper.remainder_y_top * destination_window_size_pixels + helper.remainder_x_left) * 4 + (band_number - 1), - target_width, target_height, GDT_Byte, 4, destination_window_size_pixels * 4, + helper.target_width, helper.target_height, GDT_Byte, 4, destination_window_size_pixels * 4, &rasterio_args); } @@ -154,11 +169,11 @@ void *GeoRaster::get_as_array() { GDALRasterBand *band = data->GetRasterBand(band_number); // Read into the array with 4 bytes between the pixels error = band->RasterIO( - GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, - usable_height, - array + (remainder_y_top * destination_window_size_pixels + remainder_x_left) * 3 + + GF_Read, helper.clamped_pixel_offset_x, helper.clamped_pixel_offset_y, helper.usable_width, + helper.usable_height, + array + (helper.remainder_y_top * destination_window_size_pixels + helper.remainder_x_left) * 3 + (band_number - 1), - target_width, target_height, GDT_Byte, 3, destination_window_size_pixels * 3, + helper.target_width, helper.target_height, GDT_Byte, 3, destination_window_size_pixels * 3, &rasterio_args); } @@ -171,9 +186,9 @@ void *GeoRaster::get_as_array() { case BYTE: { GDALRasterBand *band = data->GetRasterBand(1); error = band->RasterIO( - GF_Read, clamped_pixel_offset_x, clamped_pixel_offset_y, usable_width, usable_height, - array + remainder_y_top * destination_window_size_pixels + remainder_x_left, - target_width, target_height, GDT_Byte, 0, destination_window_size_pixels, + GF_Read, helper.clamped_pixel_offset_x, helper.clamped_pixel_offset_y, helper.usable_width, helper.usable_height, + array + helper.remainder_y_top * destination_window_size_pixels + helper.remainder_x_left, + helper.target_width, helper.target_height, GDT_Byte, 0, destination_window_size_pixels, &rasterio_args); if (error < CE_Failure) { return array; } @@ -193,6 +208,77 @@ void *GeoRaster::get_as_array() { return nullptr; } +void *GeoRaster::get_band_as_array(int band_index) { + GDALRasterIOExtraArg rasterio_args; + INIT_RASTERIO_EXTRA_ARG(rasterio_args); + + int interpolation = interpolation_type; + // If we're requesting downscaled data, always use nearest neighbour scaling. + // TODO: Would be good if this could be overridden with an optional parameter, but any other + // scaling usually causes very long loading times so this is the default for now + if (destination_window_size_pixels < source_window_size_pixels) { interpolation = 0; } + rasterio_args.eResampleAlg = static_cast(interpolation); + + RasterIOHelper helper = get_raster_io_helper(); + // TODO: We could do more precise error handling by getting the error number using + // CPLGetLastErrorNo() and returning that to the user somehow - maybe a flag in the + // GeoRaster. + CPLErr error = CE_Failure; + FORMAT band_format = get_band_format(band_index); + int pixel_size = get_pixel_size_x() * get_pixel_size_y(); + switch (band_format) { + case BYTE: { + GDALRasterBand *band = data->GetRasterBand(band_index); + uint8_t *array = new uint8_t[pixel_size]; + std::fill(array, array + pixel_size, 0); + if (helper.usable_width <= 0 || helper.usable_height <= 0) { + // Empty results are still valid and should be treated normally, so return an array with + // only 0s + return array; + } + error = band->RasterIO( + GF_Read, helper.clamped_pixel_offset_x, helper.clamped_pixel_offset_y, helper.usable_width, helper.usable_height, + array + helper.remainder_y_top * destination_window_size_pixels + helper.remainder_x_left, + helper.target_width, helper.target_height, GDT_Byte, 0, destination_window_size_pixels, + &rasterio_args); + + if (error < CE_Failure) { return array; } + + // Delete array in case of error + delete [] array; + + break; + } + case RF: { + GDALRasterBand *band = data->GetRasterBand(band_index); + float nodata = static_cast(band->GetNoDataValue()); + float *array = new float[pixel_size]; + std::fill(array, array + pixel_size, nodata); + if (helper.usable_width <= 0 || helper.usable_height <= 0) { + // Empty results are still valid and should be treated normally, so return an array with + // only 0s + return array; + } + + error = band->RasterIO( + GF_Read, helper.clamped_pixel_offset_x, helper.clamped_pixel_offset_y, helper.usable_width, helper.usable_height, + array + helper.remainder_y_top * destination_window_size_pixels + helper.remainder_x_left, + helper.target_width, helper.target_height, GDT_Float32, 0, destination_window_size_pixels * 4, + &rasterio_args); + + if (error < CE_Failure) { return array; } + + // Delete array in case of error + delete [] array; + + + break; + } + default: break; + } + return nullptr; +} + int GeoRaster::get_size_in_bytes() { int pixel_size = get_pixel_size_x() * get_pixel_size_y(); @@ -214,6 +300,30 @@ GeoRaster::FORMAT GeoRaster::get_format() { return format; } +GeoRaster::FORMAT GeoRaster::get_band_format(int band_index) { + int raster_count = data->GetRasterCount(); + if (band_index < 1 || band_index > raster_count) { + return UNKNOWN; + } + GDALDataType d_format = data->GetRasterBand(band_index)->GetRasterDataType(); + switch (d_format) { + case GDT_Unknown: return UNKNOWN; + case GDT_Byte: return BYTE; + // case GDT_UInt16: + // case GDT_Int16: + // case GDT_UInt32: + // case GDT_Int32: + case GDT_Float32: return RF; + case GDT_Float64: return RF; + // case GDT_CInt16: + // case GDT_CInt32: + // case GDT_CFloat32: + // case GDT_CFloat64: + // case GDT_TypeCount: + default: return UNKNOWN; + } +} + int GeoRaster::get_pixel_size_x() { return destination_window_size_pixels; } diff --git a/src/raster-tile-extractor/GeoRaster.h b/src/raster-tile-extractor/GeoRaster.h index 949e3bb..1709dd4 100644 --- a/src/raster-tile-extractor/GeoRaster.h +++ b/src/raster-tile-extractor/GeoRaster.h @@ -4,6 +4,18 @@ #include "defines.h" #include +struct RasterIOHelper { + int clamped_pixel_offset_x; + int clamped_pixel_offset_y; + int min_raster_size; // Currently unused + int remainder_x_left; + int remainder_y_top; + int target_height; + int target_width; + int usable_height; + int usable_width; +}; + // Forward declaration of GDALDataset from class GDALDataset; @@ -14,7 +26,7 @@ class GeoRaster { enum FORMAT { // size (bits) | data type | Number of chanels RGB, // 8 | int | 3 RGBA, // 8 | int | 4 - RF, // 32 | float | X >= 1 + RF, // 32<=X<=64 | float | X >= 1 BYTE, // 8 | int | X >= 1 MIXED, // 8<=X<=64 | int and/or float | X >= 2 UNKNOWN // unknown | unknown | X >= 1 @@ -29,7 +41,7 @@ class GeoRaster { ~GeoRaster() = default; static FORMAT get_format_for_dataset(GDALDataset *data); - + /// Return the data of the GeoRaster as an array. The array contains type of the raster format /// (get_format). /// BYTE -> (B)(B)(B) with B of type uint8_t @@ -39,6 +51,12 @@ class GeoRaster { /// @RequiresManualDelete void *get_as_array(); + /// @brief Return the data within a single band of the GeoRaster as an array. + /// The type of the array can be any of: BYTE, RF, or UNKNOWN. + /// @param band_index the index of the band to be returned as array. + /// @return the band as array. + void *get_band_as_array(int band_index); + /// Return the total size of the data in bytes. Useful in conjunction with get_as_array. /// An optional pixel_size can be given if it deviates from the standard size saved in the /// object. @@ -47,6 +65,13 @@ class GeoRaster { /// Return the format of the data of this GeoRaster. FORMAT get_format(); + /// @brief get the FORMAT of the band at band_index within the dataset. + /// This function is most useful when working with either MIXED datasets or simply for + /// extracting specific band information + /// @param band_index index of the band whose format will be found + /// @return the FORMAT of the band at band_index + FORMAT get_band_format(int band_index); + int get_pixel_size_x(); int get_pixel_size_y(); @@ -76,6 +101,10 @@ class GeoRaster { int destination_window_size_pixels; int interpolation_type; + + /// Returns a RasterIOHelper with attributes needed for IO operations with native raster. + /// Internal function to extract data from native raster. + RasterIOHelper get_raster_io_helper(); }; #endif // RASTERTILEEXTRACTOR_GEORASTER_H diff --git a/src/vector-extractor/NativeDataset.cpp b/src/vector-extractor/NativeDataset.cpp index ca00c0e..e8c5553 100644 --- a/src/vector-extractor/NativeDataset.cpp +++ b/src/vector-extractor/NativeDataset.cpp @@ -41,6 +41,20 @@ std::vector NativeDataset::get_raster_layer_names() { return names; } +std::vector NativeDataset::get_raster_band_descriptions() { + std::vector result; + if (!this->is_valid()) { + return result; + } + int total_raster_bands = this->dataset->GetRasterCount(); + for (int i = 1; i < total_raster_bands + 1; i++) { + GDALRasterBand *band = this->dataset->GetRasterBand(i); + std::string description(band->GetDescription()); + result.emplace_back(description); + } + return result; +} + std::shared_ptr NativeDataset::get_layer(const char *name) const { return std::make_shared(dataset->GetLayerByName(name)); } diff --git a/src/vector-extractor/NativeDataset.h b/src/vector-extractor/NativeDataset.h index 386e99e..1504530 100644 --- a/src/vector-extractor/NativeDataset.h +++ b/src/vector-extractor/NativeDataset.h @@ -14,6 +14,9 @@ class NativeDataset { /// Return the names of all raster layers as std::strings. std::vector get_raster_layer_names(); + /// Return the descriptions of all raster bands as std::strings. + std::vector get_raster_band_descriptions(); + /// Returns the layer from this dataset with the given name, or null if there is no layer /// with that name. std::shared_ptr get_layer(const char *name) const; From 4b2970fa9ea584b1dd4924883c9c01267bdcc7b6 Mon Sep 17 00:00:00 2001 From: "Carlos A. Parra F" Date: Thu, 27 Jul 2023 11:32:10 +0200 Subject: [PATCH 6/9] feat: Updated gitignore and updated demo project to v4.1 - Added `.godot/` subfolder to gitignore so local project stuff is not exposed to git. - Updated project and one import to the latest stable relase of Godot. --- .gitignore | 1 + demo/geodata/vienna-test-ortho.jpg.import | 2 +- demo/project.godot | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e658581..3b13bff 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build/ **/.sconsign.dblite **/.vscode/ demo/.import/ +**/.godot/* \ No newline at end of file diff --git a/demo/geodata/vienna-test-ortho.jpg.import b/demo/geodata/vienna-test-ortho.jpg.import index ace162c..2b2a63d 100644 --- a/demo/geodata/vienna-test-ortho.jpg.import +++ b/demo/geodata/vienna-test-ortho.jpg.import @@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/vienna-test-ortho.jpg-5325b663943c1c2e87bc1a6 [params] compress/mode=0 +compress/high_quality=false compress/lossy_quality=0.7 compress/hdr_compression=1 -compress/bptc_ldr=0 compress/normal_map=0 compress/channel_pack=0 mipmaps/generate=false diff --git a/demo/project.godot b/demo/project.godot index 1e7ab22..74803bd 100644 --- a/demo/project.godot +++ b/demo/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="Demo" run/main_scene="res://RasterDemo.tscn" -config/features=PackedStringArray("4.0") +config/features=PackedStringArray("4.1") config/icon="res://icon.png" [autoload] From 77996d4bf1cbae929f8ea052b79b78a6d48b8c8a Mon Sep 17 00:00:00 2001 From: LandscapeLab Office Date: Mon, 31 Jul 2023 13:13:21 +0200 Subject: [PATCH 7/9] Add default value for band_index --- src/geodata.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/geodata.cpp b/src/geodata.cpp index c401915..becc1b2 100644 --- a/src/geodata.cpp +++ b/src/geodata.cpp @@ -24,7 +24,7 @@ void GeoDataset::_bind_methods() { ClassDB::bind_method(D_METHOD("get_raster_layers"), &GeoDataset::get_raster_layers); ClassDB::bind_method(D_METHOD("get_raster_layers_by_band"), &GeoDataset::get_raster_layers_by_band); ClassDB::bind_method(D_METHOD("get_feature_layers"), &GeoDataset::get_feature_layers); - ClassDB::bind_method(D_METHOD("get_raster_layer", "name", "band_index"), &GeoDataset::get_raster_layer); + ClassDB::bind_method(D_METHOD("get_raster_layer", "name", "band_index"), &GeoDataset::get_raster_layer, DEFVAL(1)); ClassDB::bind_method(D_METHOD("get_feature_layer", "name"), &GeoDataset::get_feature_layer); ClassDB::bind_method(D_METHOD("get_raster_count"), &GeoDataset::get_raster_count); ClassDB::bind_method(D_METHOD("load_from_file", "file_path", "write_access"), From 50c6c292d6d224a9d7c91ccc296c3f3557d6f791 Mon Sep 17 00:00:00 2001 From: LandscapeLab Office Date: Mon, 31 Jul 2023 13:17:00 +0200 Subject: [PATCH 8/9] Move band_index from GeoRasterLayer to get_band_image only --- src/geodata.cpp | 44 ++++---------------------------------------- src/geodata.h | 23 ++--------------------- 2 files changed, 6 insertions(+), 61 deletions(-) diff --git a/src/geodata.cpp b/src/geodata.cpp index becc1b2..6717be2 100644 --- a/src/geodata.cpp +++ b/src/geodata.cpp @@ -22,9 +22,8 @@ void GeoDataset::_bind_methods() { ClassDB::bind_method(D_METHOD("get_file_info"), &GeoDataset::get_file_info); ClassDB::bind_method(D_METHOD("has_write_access"), &GeoDataset::has_write_access); ClassDB::bind_method(D_METHOD("get_raster_layers"), &GeoDataset::get_raster_layers); - ClassDB::bind_method(D_METHOD("get_raster_layers_by_band"), &GeoDataset::get_raster_layers_by_band); ClassDB::bind_method(D_METHOD("get_feature_layers"), &GeoDataset::get_feature_layers); - ClassDB::bind_method(D_METHOD("get_raster_layer", "name", "band_index"), &GeoDataset::get_raster_layer, DEFVAL(1)); + ClassDB::bind_method(D_METHOD("get_raster_layer", "name"), &GeoDataset::get_raster_layer); ClassDB::bind_method(D_METHOD("get_feature_layer", "name"), &GeoDataset::get_feature_layer); ClassDB::bind_method(D_METHOD("get_raster_count"), &GeoDataset::get_raster_count); ClassDB::bind_method(D_METHOD("load_from_file", "file_path", "write_access"), @@ -55,23 +54,12 @@ Array GeoDataset::get_raster_layers() { int total_names = names.size(); for (int i = 0; i < total_names; i++ ) { std::string name = names[i]; - layers.append(get_raster_layer(name.c_str(), i + 1)); + layers.append(get_raster_layer(name.c_str())); } return layers; } -Array GeoDataset::get_raster_layers_by_band() { - Array result = Array(); - - std::vector descriptions = dataset->get_raster_band_descriptions(); - for (int i = 0; i < descriptions.size(); i++) { - std::string description = descriptions[i]; - result.append(get_raster_band(description.c_str(), i + 1)); - } - return result; -} - Array GeoDataset::get_feature_layers() { Array layers = Array(); @@ -84,26 +72,13 @@ Array GeoDataset::get_feature_layers() { return layers; } -Ref GeoDataset::get_raster_layer(String name, int band_index) { +Ref GeoDataset::get_raster_layer(String name) { Ref raster_layer; raster_layer.instantiate(); raster_layer->set_native_dataset(dataset->get_subdataset(name.utf8().get_data())); raster_layer->set_name(name); raster_layer->set_origin_dataset(this); - raster_layer->set_band_index(band_index); - - return raster_layer; -} - -Ref GeoDataset::get_raster_band(String description, int band_index) { - Ref raster_layer; - raster_layer.instantiate(); - - raster_layer->set_native_dataset(std::shared_ptr(this->dataset)); - raster_layer->set_name(description); - raster_layer->set_origin_dataset(this); - raster_layer->set_band_index(band_index); return raster_layer; } @@ -333,7 +308,6 @@ void GeoRasterLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_center"), &GeoRasterLayer::get_center); ClassDB::bind_method(D_METHOD("get_min"), &GeoRasterLayer::get_min); ClassDB::bind_method(D_METHOD("get_max"), &GeoRasterLayer::get_max); - ClassDB::bind_method(D_METHOD("get_band_index"), &GeoRasterLayer::get_band_index); ClassDB::bind_method(D_METHOD("get_pixel_size"), &GeoRasterLayer::get_pixel_size); ClassDB::bind_method(D_METHOD("clone"), &GeoRasterLayer::clone); ClassDB::bind_method(D_METHOD("load_from_file", "file_path", "write_access"), @@ -417,7 +391,7 @@ Ref GeoRasterLayer::get_image(double top_left_x, double top_left_y, do } Ref GeoRasterLayer::get_band_image(double top_left_x, double top_left_y, double size_meters, - int img_size, int interpolation_type) { + int img_size, int interpolation_type, int band_index) { Ref image; image.instantiate(); if (dataset == nullptr || !dataset->is_valid()) { @@ -617,14 +591,6 @@ void GeoRasterLayer::set_origin_dataset(Ref dataset) { this->origin_dataset = dataset; } -void GeoRasterLayer::set_band_index(int band_index) { - this->band_index = band_index; -} - -int GeoRasterLayer::get_band_index() { - return this->band_index; -} - Ref GeoRasterLayer::clone() { Ref layer_clone; layer_clone.instantiate(); @@ -632,14 +598,12 @@ Ref GeoRasterLayer::clone() { layer_clone->set_native_dataset(dataset->clone()); layer_clone->set_origin_dataset(origin_dataset); layer_clone->set_name(get_name()); - layer_clone->set_band_index(this->band_index); return layer_clone; } void GeoRasterLayer::load_from_file(String file_path, bool write_access) { this->write_access = write_access; - this->band_index = 1; // This is the default index if none is set explicitly dataset = VectorExtractor::open_dataset(file_path.utf8().get_data(), write_access); // TODO: Might be better to produce a hard crash here, but CRASH_COND doesn't have the desired diff --git a/src/geodata.h b/src/geodata.h index 918b104..8aadc16 100644 --- a/src/geodata.h +++ b/src/geodata.h @@ -135,7 +135,7 @@ class EXPORT GeoRasterLayer : public Resource { /// index will (probably erroneously) be assigned index 1. /// TODO: Make sure that all layers get a correctly assigned index. Ref get_band_image(double top_left_x, double top_left_y, double size_meters, int img_size, - int interpolation_type); + int interpolation_type, int band_index); /// Returns the value in the GeoRasterLayer at exactly the given position. /// Note that when reading many values from a confined area, it is more efficient to call @@ -197,22 +197,12 @@ class EXPORT GeoRasterLayer : public Resource { /// Not exposed to Godot since it should never construct GeoFeatureLayers by hand. void set_origin_dataset(Ref dataset); - /// Get the band_index that this raster_layer is set to. - int get_band_index(); - - /// @brief Sets the band_index for this layer. - /// - /// Not exposed to Godot, since it's only meant for cloning/internal use. - void set_band_index(int band_index); - bool write_access; private: Ref origin_dataset; std::shared_ptr dataset; ExtentData extent_data; - // The Band (in the Raster) that this GeoRasterLayer represents - int band_index; }; /// A dataset which contains layers of geodata. @@ -239,22 +229,13 @@ class EXPORT GeoDataset : public Resource { /// Return all GeoRasterLayers objects for this dataset. Array get_raster_layers(); - /// Like `get_raster_layers`, but instead of looking for them by name, looking by index - Array get_raster_layers_by_band(); - /// Return all GeoFeatureLayer objects for this dataset. Array get_feature_layers(); /// Returns a GeoRasterLayer object of the layer within this dataset with /// the given name. It is recommended to check the validity of the returned /// object with GeoRasterLayer::is_valid(). - Ref get_raster_layer(String name, int band_index); - - /// Like `get_raster_layer` returns a GeoRasterLayer. - /// Different to `get_raster_layer` it not attempt to extract (possibly) - /// non-existent SUBDATASETS from the main Dataset and just sets the main - /// dataset as the native dataset of the Layer. - Ref get_raster_band(String description, int band_index); + Ref get_raster_layer(String name); /// Returns a GeoFeatureLayer object of the layer within this dataset with /// the given name. It is recommended to check the validity of the returned From 5a27d243c00119ac6ac1637791405e10704b0056 Mon Sep 17 00:00:00 2001 From: LandscapeLab Office Date: Mon, 31 Jul 2023 13:33:21 +0200 Subject: [PATCH 9/9] Move band count and descriptions to GeoRasterLayer --- src/geodata.cpp | 26 ++++++++++++++++++-------- src/geodata.h | 13 ++++++++----- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/src/geodata.cpp b/src/geodata.cpp index 6717be2..9f066ba 100644 --- a/src/geodata.cpp +++ b/src/geodata.cpp @@ -25,7 +25,6 @@ void GeoDataset::_bind_methods() { ClassDB::bind_method(D_METHOD("get_feature_layers"), &GeoDataset::get_feature_layers); ClassDB::bind_method(D_METHOD("get_raster_layer", "name"), &GeoDataset::get_raster_layer); ClassDB::bind_method(D_METHOD("get_feature_layer", "name"), &GeoDataset::get_feature_layer); - ClassDB::bind_method(D_METHOD("get_raster_count"), &GeoDataset::get_raster_count); ClassDB::bind_method(D_METHOD("load_from_file", "file_path", "write_access"), &GeoDataset::load_from_file); } @@ -105,13 +104,6 @@ void GeoDataset::set_native_dataset(std::shared_ptr new_dataset) dataset = new_dataset; } -int GeoDataset::get_raster_count() { - if (dataset == nullptr || !this->is_valid()) { - return 0; - } - return dataset->dataset->GetRasterCount(); -} - void GeoFeatureLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("is_valid"), &GeoFeatureLayer::is_valid); ClassDB::bind_method(D_METHOD("get_file_info"), &GeoFeatureLayer::get_file_info); @@ -286,6 +278,8 @@ void GeoRasterLayer::_bind_methods() { ClassDB::bind_method(D_METHOD("has_write_access"), &GeoRasterLayer::has_write_access); ClassDB::bind_method(D_METHOD("get_file_info"), &GeoRasterLayer::get_file_info); ClassDB::bind_method(D_METHOD("get_format"), &GeoRasterLayer::get_format); + ClassDB::bind_method(D_METHOD("get_band_count"), &GeoRasterLayer::get_band_count); + ClassDB::bind_method(D_METHOD("get_band_descriptions"), &GeoRasterLayer::get_band_descriptions); ClassDB::bind_method(D_METHOD("get_dataset"), &GeoRasterLayer::get_dataset); ClassDB::bind_method(D_METHOD("get_image", "top_left_x", "top_left_y", "size_meters", "img_size", "interpolation_type"), @@ -322,6 +316,18 @@ bool GeoRasterLayer::has_write_access() { return write_access; } +Array GeoRasterLayer::get_band_descriptions() { + Array result = Array(); + + std::vector descriptions = dataset->get_raster_band_descriptions(); + for (int i = 0; i < descriptions.size(); i++) { + std::string description = descriptions[i]; + result.append(description.c_str()); + } + + return result; +} + Dictionary GeoRasterLayer::get_file_info() { Dictionary info; @@ -358,6 +364,10 @@ Image::Format GeoRasterLayer::get_format() { } } +int GeoRasterLayer::get_band_count() { + return dataset->dataset->GetRasterCount(); +} + Ref GeoRasterLayer::get_dataset() { return origin_dataset; } diff --git a/src/geodata.h b/src/geodata.h index 8aadc16..5237ea8 100644 --- a/src/geodata.h +++ b/src/geodata.h @@ -114,6 +114,14 @@ class EXPORT GeoRasterLayer : public Resource { /// Returns the Image format which corresponds to the data within this raster layer. Image::Format get_format(); + /// @brief Get the total amount of raster bands contained in the layer. + /// Returns 0 if layer is not valid + /// @return the total amount of raster bands in the layer. + int get_band_count(); + + /// Returns the descriptions of the individual raster bands as strings in an array. + Array get_band_descriptions(); + /// Returns the dataset which this layer was opened from or null if it was opened directly, e.g. /// from a GeoTIFF. Ref get_dataset(); @@ -251,11 +259,6 @@ class EXPORT GeoDataset : public Resource { /// is only for internal use. void set_native_dataset(std::shared_ptr new_dataset); - /// @brief Get the total amount of raster bands contained in the dataset. - /// Returns 0 if dataset is not valid - /// @return the total amount of raster bands in the dataset. - int get_raster_count(); - bool write_access; std::shared_ptr dataset;