From bf2a6df16a8051ca590f923fd62061fd8b08f9b2 Mon Sep 17 00:00:00 2001 From: Alexander Bruy Date: Wed, 12 Feb 2025 13:36:00 +0000 Subject: [PATCH] round width and height to the nearest integer in native raster creation Processing algorithms to match behaviour of gdal_rasterize (fix #43547) --- .../processing/qgsalgorithmconstantraster.cpp | 6 +- .../processing/qgsalgorithmlinedensity.cpp | 6 +- .../processing/qgsalgorithmrandomraster.cpp | 6 +- .../src/analysis/testqgsprocessingalgspt2.cpp | 87 ++++++++++++++++++ tests/testdata/raster/gdal_rasterize.tif | Bin 0 -> 1668 bytes 5 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 tests/testdata/raster/gdal_rasterize.tif diff --git a/src/analysis/processing/qgsalgorithmconstantraster.cpp b/src/analysis/processing/qgsalgorithmconstantraster.cpp index 8446f2cc4b8..80c819304bc 100644 --- a/src/analysis/processing/qgsalgorithmconstantraster.cpp +++ b/src/analysis/processing/qgsalgorithmconstantraster.cpp @@ -161,8 +161,10 @@ QVariantMap QgsConstantRasterAlgorithm::processAlgorithm( const QVariantMap &par const QFileInfo fi( outputFile ); const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() ); - const int rows = std::max( std::ceil( extent.height() / pixelSize ), 1.0 ); - const int cols = std::max( std::ceil( extent.width() / pixelSize ), 1.0 ); + // round up width and height to the nearest integer as GDAL does (e.g. in gdal_rasterize) + // see https://github.com/qgis/QGIS/issues/43547 + const int rows = static_cast( 0.5 + extent.height() / pixelSize ); + const int cols = static_cast( 0.5 + extent.width() / pixelSize ); //build new raster extent based on number of columns and cellsize //this prevents output cellsize being calculated too small diff --git a/src/analysis/processing/qgsalgorithmlinedensity.cpp b/src/analysis/processing/qgsalgorithmlinedensity.cpp index 5b80afa9a43..00fcf9905f0 100644 --- a/src/analysis/processing/qgsalgorithmlinedensity.cpp +++ b/src/analysis/processing/qgsalgorithmlinedensity.cpp @@ -136,8 +136,10 @@ QVariantMap QgsLineDensityAlgorithm::processAlgorithm( const QVariantMap ¶me const QFileInfo fi( outputFile ); const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() ); - const int rows = std::max( std::ceil( mExtent.height() / mPixelSize ), 1.0 ); - const int cols = std::max( std::ceil( mExtent.width() / mPixelSize ), 1.0 ); + // round up width and height to the nearest integer as GDAL does (e.g. in gdal_rasterize) + // see https://github.com/qgis/QGIS/issues/43547 + const int rows = static_cast( 0.5 + mExtent.height() / mPixelSize ); + const int cols = static_cast( 0.5 + mExtent.width() / mPixelSize ); //build new raster extent based on number of columns and cellsize //this prevents output cellsize being calculated too small diff --git a/src/analysis/processing/qgsalgorithmrandomraster.cpp b/src/analysis/processing/qgsalgorithmrandomraster.cpp index 01769b54a0a..c20c820e259 100644 --- a/src/analysis/processing/qgsalgorithmrandomraster.cpp +++ b/src/analysis/processing/qgsalgorithmrandomraster.cpp @@ -83,8 +83,10 @@ QVariantMap QgsRandomRasterAlgorithmBase::processAlgorithm( const QVariantMap &p const QFileInfo fi( outputFile ); const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() ); - const int rows = std::max( std::ceil( mExtent.height() / mPixelSize ), 1.0 ); - const int cols = std::max( std::ceil( mExtent.width() / mPixelSize ), 1.0 ); + // round up width and height to the nearest integer as GDAL does (e.g. in gdal_rasterize) + // see https://github.com/qgis/QGIS/issues/43547 + const int rows = static_cast( 0.5 + mExtent.height() / mPixelSize ); + const int cols = static_cast( 0.5 + mExtent.width() / mPixelSize ); //build new raster extent based on number of columns and cellsize //this prevents output cellsize being calculated too small diff --git a/tests/src/analysis/testqgsprocessingalgspt2.cpp b/tests/src/analysis/testqgsprocessingalgspt2.cpp index aac2f8bb084..b0be3b93173 100644 --- a/tests/src/analysis/testqgsprocessingalgspt2.cpp +++ b/tests/src/analysis/testqgsprocessingalgspt2.cpp @@ -111,6 +111,8 @@ class TestQgsProcessingAlgsPt2 : public QgsTest void updateMetadata(); void setMetadataFields(); + void nativeAlgsRasterSize(); + private: QString mPointLayerPath; QgsVectorLayer *mPointsLayer = nullptr; @@ -2154,5 +2156,90 @@ void TestQgsProcessingAlgsPt2::setMetadataFields() QCOMPARE( layer->metadata().crs().authid(), QStringLiteral( "EPSG:4326" ) ); } +void TestQgsProcessingAlgsPt2::nativeAlgsRasterSize() +{ + auto layer = std::make_unique( QStringLiteral( "LineString?crs=epsg:32633" ), QStringLiteral( "input" ), QStringLiteral( "memory" ) ); + QVERIFY( layer->isValid() ); + + QgsFeature f; + f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString(656000 4551184, 656184 4551000)" ) ) ); + layer->dataProvider()->addFeature( f ); + + const QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt + + // reference raster was generated by gdal_rasterize algorithm using following parameters + // UNITS: Georeferenced units + // WIDTH: 10 + // HEIGHT: 10 + // EXTENT: 656000,656184,4551000,4551184 [EPSG:32633] + auto gdalRasterLayer = std::make_unique( dataDir + QStringLiteral( "/raster/gdal_rasterize.tif" ), "gdal_rasterize", "gdal" ); + + // create constant raster + std::unique_ptr alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createconstantrasterlayer" ) ) ); + QVERIFY( alg != nullptr ); + + QVariantMap parameters; + parameters.clear(); + parameters.insert( QStringLiteral( "EXTENT" ), QStringLiteral( "656000,656184,4551000,4551184 [EPSG:32633]" ) ); + parameters.insert( QStringLiteral( "NUMBER" ), 1 ); + parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), 0 ); + parameters.insert( QStringLiteral( "PIXEL_SIZE" ), 10 ); + parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( "EPSG:32633" ) ); + parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT ); + + bool ok = false; + auto context = std::make_unique(); + QgsProcessingFeedback feedback; + QVariantMap results; + results = alg->run( parameters, *context, &feedback, &ok ); + QVERIFY( ok ); + + auto constantRasterLayer = std::make_unique( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "constant", "gdal" ); + + QCOMPARE( constantRasterLayer->extent(), gdalRasterLayer->extent() ); + QCOMPARE( constantRasterLayer->height(), gdalRasterLayer->height() ); + QCOMPARE( constantRasterLayer->width(), gdalRasterLayer->width() ); + + // create random raster (it is enough to check only one alg here as they have same base class) + alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:createrandomuniformrasterlayer" ) ) ); + QVERIFY( alg != nullptr ); + + parameters.clear(); + parameters.insert( QStringLiteral( "EXTENT" ), QStringLiteral( "656000,656184,4551000,4551184 [EPSG:32633]" ) ); + parameters.insert( QStringLiteral( "OUTPUT_TYPE" ), 0 ); + parameters.insert( QStringLiteral( "PIXEL_SIZE" ), 10 ); + parameters.insert( QStringLiteral( "TARGET_CRS" ), QgsCoordinateReferenceSystem( "EPSG:32633" ) ); + parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT ); + + ok = false; + results = alg->run( parameters, *context, &feedback, &ok ); + QVERIFY( ok ); + + auto randomRasterLayer = std::make_unique( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "random", "gdal" ); + + QCOMPARE( randomRasterLayer->extent(), gdalRasterLayer->extent() ); + QCOMPARE( randomRasterLayer->height(), gdalRasterLayer->height() ); + QCOMPARE( randomRasterLayer->width(), gdalRasterLayer->width() ); + + // line density + alg.reset( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:linedensity" ) ) ); + QVERIFY( alg != nullptr ); + + parameters.clear(); + parameters.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( layer.get() ) ); + parameters.insert( QStringLiteral( "PIXEL_SIZE" ), 10 ); + parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT ); + + ok = false; + results = alg->run( parameters, *context, &feedback, &ok ); + QVERIFY( ok ); + + auto densityRasterLayer = std::make_unique( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "density", "gdal" ); + + QCOMPARE( densityRasterLayer->extent(), gdalRasterLayer->extent() ); + QCOMPARE( densityRasterLayer->height(), gdalRasterLayer->height() ); + QCOMPARE( densityRasterLayer->width(), gdalRasterLayer->width() ); +} + QGSTEST_MAIN( TestQgsProcessingAlgsPt2 ) #include "testqgsprocessingalgspt2.moc" diff --git a/tests/testdata/raster/gdal_rasterize.tif b/tests/testdata/raster/gdal_rasterize.tif new file mode 100644 index 0000000000000000000000000000000000000000..62d87a11977e2429159e3a2727d6dbbfd149f4f8 GIT binary patch literal 1668 zcmebD)MDUZU|-pD0L7W1Y>+xOB(@+U3s`RnP(l<*Tnx$v znJErcBf!eQAcLeP7|Lb_s^M$qVF1zRfVioJhk*@9KLg_R?aT}cKw1oF{>FAD200)* z49IR=!o0mJ?mW}OV zAbF0B?d%L<3~a!lV`S)4X5azQmGvxOT2ndPJy^lQL_uF6G{je-DnBn(!PwZZ1|nJm i)ZbtavV}Te)GX4%aWuR}!wWqGM$-g(h~Vc_H@pBnL?@~M literal 0 HcmV?d00001