diff --git a/.docker/qgis3-build-deps.dockerfile b/.docker/qgis3-build-deps.dockerfile index 9e8476e82bd..776a57def10 100644 --- a/.docker/qgis3-build-deps.dockerfile +++ b/.docker/qgis3-build-deps.dockerfile @@ -98,6 +98,7 @@ RUN apt-get update \ qt3d-defaultgeometryloader-plugin \ qt3d-gltfsceneio-plugin \ qt3d-scene2d-plugin \ + qt5-image-formats-plugins \ qt5keychain-dev \ qtbase5-dev \ qtdeclarative5-dev-tools \ diff --git a/python/core/auto_generated/qgsrenderchecker.sip.in b/python/core/auto_generated/qgsrenderchecker.sip.in index 48b48cdaa42..162180b6fef 100644 --- a/python/core/auto_generated/qgsrenderchecker.sip.in +++ b/python/core/auto_generated/qgsrenderchecker.sip.in @@ -82,7 +82,14 @@ Sets the base directory ``name`` for the control image (with control image path suffixed). The path to the image will be constructed like this: -:py:func:`~QgsRenderChecker.controlImagePath` + '/' + control name + '/' + control name + '.png' +:py:func:`~QgsRenderChecker.controlImagePath` + '/' + control name + '/' + control name + '.' + extension ('png' by default) +%End + + void setControlExtension( const QString &extension ); +%Docstring +Sets file extension for the control image. By default it is "png" + +.. versionadded:: 3.20 %End void setControlPathPrefix( const QString &name ); diff --git a/src/analysis/processing/qgsalgorithmrasterize.cpp b/src/analysis/processing/qgsalgorithmrasterize.cpp index 7bee3189015..8e3f7d80765 100644 --- a/src/analysis/processing/qgsalgorithmrasterize.cpp +++ b/src/analysis/processing/qgsalgorithmrasterize.cpp @@ -29,6 +29,7 @@ #include "qgsmaprenderercustompainterjob.h" #include "gdal.h" #include "qgsgdalutils.h" +#include "qgslayertree.h" #include @@ -333,8 +334,16 @@ bool QgsRasterizeAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, Qgs // Still no layers? Get them all from the project if ( mMapLayers.size() == 0 ) { - const auto constLayers { context.project()->mapLayers().values() }; - for ( const QgsMapLayer *ml : constLayers ) + QList layers; + QgsLayerTree *root = context.project()->layerTreeRoot(); + for ( QgsLayerTreeLayer *nodeLayer : root->findLayers() ) + { + QgsMapLayer *layer = nodeLayer->layer(); + if ( nodeLayer->isVisible() && root->layerOrder().contains( layer ) ) + layers << layer; + } + + for ( const QgsMapLayer *ml : qgis::as_const( layers ) ) { mMapLayers.push_back( std::unique_ptr( ml->clone( ) ) ); } diff --git a/src/core/qgsrenderchecker.cpp b/src/core/qgsrenderchecker.cpp index 92ebe68f7d9..d67b96241f6 100644 --- a/src/core/qgsrenderchecker.cpp +++ b/src/core/qgsrenderchecker.cpp @@ -46,7 +46,7 @@ void QgsRenderChecker::setControlImagePath( const QString &path ) void QgsRenderChecker::setControlName( const QString &name ) { mControlName = name; - mExpectedImageFile = controlImagePath() + name + '/' + mControlPathSuffix + name + ".png"; + mExpectedImageFile = controlImagePath() + name + '/' + mControlPathSuffix + name + "." + mControlExtension; } void QgsRenderChecker::setControlPathSuffix( const QString &name ) diff --git a/src/core/qgsrenderchecker.h b/src/core/qgsrenderchecker.h index ffec638d850..1a47ba6092b 100644 --- a/src/core/qgsrenderchecker.h +++ b/src/core/qgsrenderchecker.h @@ -98,10 +98,16 @@ class CORE_EXPORT QgsRenderChecker * suffixed). * * The path to the image will be constructed like this: - * controlImagePath() + '/' + control name + '/' + control name + '.png' + * controlImagePath() + '/' + control name + '/' + control name + '.' + extension ('png' by default) */ void setControlName( const QString &name ); + /** + * Sets file extension for the control image. By default it is "png" + * \since QGIS 3.20 + */ + void setControlExtension( const QString &extension ) { mControlExtension = extension; } + /** * Sets the path prefix where the control images are kept. * This will be appended to controlImagePath(). @@ -237,6 +243,7 @@ class CORE_EXPORT QgsRenderChecker int mMaxSizeDifferenceY = 0; int mElapsedTimeTarget = 0; QgsMapSettings mMapSettings; + QString mControlExtension = QStringLiteral( "png" ); QString mControlPathPrefix; QString mControlPathSuffix; QVector mDashMessages; diff --git a/tests/src/analysis/testqgsprocessingalgs.cpp b/tests/src/analysis/testqgsprocessingalgs.cpp index c612282ad80..1193ecd934d 100644 --- a/tests/src/analysis/testqgsprocessingalgs.cpp +++ b/tests/src/analysis/testqgsprocessingalgs.cpp @@ -39,6 +39,7 @@ #include "qgsreclassifyutils.h" #include "qgsalgorithmrasterlogicalop.h" #include "qgsprintlayout.h" +#include "qgslayertree.h" #include "qgslayoutmanager.h" #include "qgslayoutitemmap.h" #include "qgsmarkersymbollayer.h" @@ -178,6 +179,8 @@ class TestQgsProcessingAlgs: public QObject void fileDownloader(); + void rasterize(); + private: bool imageCheck( const QString &testName, const QString &renderedImage ); @@ -6488,6 +6491,53 @@ void TestQgsProcessingAlgs::fileDownloader() QVERIFY( results.value( QStringLiteral( "OUTPUT" ) ).toString().endsWith( QLatin1String( ".txt" ) ) ); } +void TestQgsProcessingAlgs::rasterize() +{ + std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:rasterize" ) ) ); + QVERIFY( alg != nullptr ); + + QString outputTif = QDir::tempPath() + "/rasterize_output.tif"; + if ( QFile::exists( outputTif ) ) + QFile::remove( outputTif ); + + QVariantMap parameters; + parameters.insert( QStringLiteral( "EXTENT" ), QStringLiteral( "-120,-80,15,55" ) ); + parameters.insert( QStringLiteral( "TILE_SIZE" ), 320 ); + parameters.insert( QStringLiteral( "MAP_UNITS_PER_PIXEL" ), 0.125 ); + parameters.insert( QStringLiteral( "OUTPUT" ), outputTif ); + + // create a temporary project with three layers, but only two are visible + // (to test that the algorithm in the default setup without defined LAYERS or MAP_THEME uses only vsisible + // layers that and in the correct order) + QgsProject project; + QString dataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt + QgsVectorLayer *pointsLayer = new QgsVectorLayer( dataDir + "/points.shp", QStringLiteral( "points" ), QStringLiteral( "ogr" ) ); + QgsVectorLayer *linesLayer = new QgsVectorLayer( dataDir + "/lines.shp", QStringLiteral( "lines" ), QStringLiteral( "ogr" ) ); + QgsVectorLayer *polygonLayer = new QgsVectorLayer( dataDir + "/polys.shp", QStringLiteral( "polygons" ), QStringLiteral( "ogr" ) ); + QVERIFY( pointsLayer->isValid() && linesLayer->isValid() && polygonLayer->isValid() ); + project.addMapLayers( QList() << pointsLayer << linesLayer << polygonLayer ); + QgsLayerTreeLayer *nodePolygons = project.layerTreeRoot()->findLayer( polygonLayer ); + QVERIFY( nodePolygons ); + nodePolygons->setItemVisibilityChecked( false ); + + std::unique_ptr< QgsProcessingContext > context = std::make_unique< QgsProcessingContext >(); + context->setProject( &project ); + QgsProcessingFeedback feedback; + QVariantMap results; + bool ok = false; + + results = alg->run( parameters, *context, &feedback, &ok ); + QVERIFY( ok ); + QVERIFY( QFile::exists( outputTif ) ); + + QgsRenderChecker checker; + checker.setControlPathPrefix( QStringLiteral( "processing_algorithm" ) ); + checker.setControlExtension( "tif" ); + checker.setControlName( "expected_rasterize" ); + checker.setRenderedImage( outputTif ); + QVERIFY( checker.compareImages( "rasterize", 500 ) ); +} + void TestQgsProcessingAlgs::exportMeshTimeSeries() { std::unique_ptr< QgsProcessingAlgorithm > alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:meshexporttimeseries" ) ) ); diff --git a/tests/testdata/control_images/processing_algorithm/expected_rasterize/expected_rasterize.tif b/tests/testdata/control_images/processing_algorithm/expected_rasterize/expected_rasterize.tif new file mode 100644 index 00000000000..3a7605fc2c9 Binary files /dev/null and b/tests/testdata/control_images/processing_algorithm/expected_rasterize/expected_rasterize.tif differ