mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-07 00:15:48 -04:00
Processing raster rank algorithm (#60605)
This commit is contained in:
parent
210378e500
commit
fa9f86769b
@ -193,6 +193,7 @@ set(QGIS_ANALYSIS_SRCS
|
||||
processing/qgsalgorithmrasterlogicalop.cpp
|
||||
processing/qgsalgorithmrasterminmax.cpp
|
||||
processing/qgsalgorithmrasterize.cpp
|
||||
processing/qgsalgorithmrasterrank.cpp
|
||||
processing/qgsalgorithmrastersampling.cpp
|
||||
processing/qgsalgorithmrasterstackposition.cpp
|
||||
processing/qgsalgorithmrasterstatistics.cpp
|
||||
|
327
src/analysis/processing/qgsalgorithmrasterrank.cpp
Normal file
327
src/analysis/processing/qgsalgorithmrasterrank.cpp
Normal file
@ -0,0 +1,327 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmrasterrank.cpp
|
||||
---------------------
|
||||
begin : February 2024
|
||||
copyright : (C) 2024 by Mathieu Pellerin
|
||||
email : mathieu at opengis dot ch
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsalgorithmrasterrank.h"
|
||||
#include "qgsrasterfilewriter.h"
|
||||
#include "qgsrasterprojector.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
QString QgsRasterRankAlgorithm::name() const
|
||||
{
|
||||
return QStringLiteral( "rasterrank" );
|
||||
}
|
||||
|
||||
QString QgsRasterRankAlgorithm::displayName() const
|
||||
{
|
||||
return QObject::tr( "Raster rank" );
|
||||
}
|
||||
|
||||
QStringList QgsRasterRankAlgorithm::tags() const
|
||||
{
|
||||
return QObject::tr( "raster,rank" ).split( ',' );
|
||||
}
|
||||
|
||||
QString QgsRasterRankAlgorithm::group() const
|
||||
{
|
||||
return QObject::tr( "Raster analysis" );
|
||||
}
|
||||
|
||||
QString QgsRasterRankAlgorithm::groupId() const
|
||||
{
|
||||
return QStringLiteral( "rasteranalysis" );
|
||||
}
|
||||
|
||||
QString QgsRasterRankAlgorithm::shortHelpString() const
|
||||
{
|
||||
return QObject::tr( "Performs a cell-by-cell analysis in which output values match the rank of a "
|
||||
"sorted list of overlapping cell values from input layers. The output raster "
|
||||
"will be multi-band if multiple ranks are provided.\n"
|
||||
"If multiband rasters are used in the data raster stack, the algorithm will always "
|
||||
"perform the analysis on the first band of the rasters." );
|
||||
}
|
||||
|
||||
QgsRasterRankAlgorithm *QgsRasterRankAlgorithm::createInstance() const
|
||||
{
|
||||
return new QgsRasterRankAlgorithm();
|
||||
}
|
||||
|
||||
void QgsRasterRankAlgorithm::initAlgorithm( const QVariantMap & )
|
||||
{
|
||||
addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "INPUT_RASTERS" ), QObject::tr( "Input raster layers" ), Qgis::ProcessingSourceType::Raster ) );
|
||||
auto ranksParameter = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "RANKS" ), QObject::tr( "Rank(s) (separate multiple ranks using commas)" ), 1 );
|
||||
ranksParameter->setHelp( QObject::tr( "A rank value must be numerical, with multiple ranks separated by commas. The rank will be used to "
|
||||
"generate output values from sorted lists of input layers’ cell values. A rank value of 1 will pick "
|
||||
"the first value from a given sorted input layers’ cell values list (i.e. the minimum value). "
|
||||
"Negative rank values are supported, and will behave like a negative index. A rank value of -2 will "
|
||||
"pick the second to last value in sorted input values lists, while a rank value of -1 will pick the "
|
||||
"last value (i.e. the maximum value)." ) );
|
||||
addParameter( ranksParameter.release() );
|
||||
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "NODATA_HANDLING" ), QObject::tr( "NoData value handling" ), QStringList() << QObject::tr( "Exclude NoData from values lists" ) << QObject::tr( "Presence of NoData in a values list results in NoData output cell" ), false, 0 ) );
|
||||
|
||||
auto extentParam = std::make_unique<QgsProcessingParameterExtent>( QStringLiteral( "EXTENT" ), QObject::tr( "Output extent" ), QVariant(), true );
|
||||
extentParam->setHelp( QObject::tr( "Extent of the output layer. If not specified, the extent will be the overall extent of all input layers" ) );
|
||||
extentParam->setFlags( extentParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||
addParameter( extentParam.release() );
|
||||
auto cellSizeParam = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "CELL_SIZE" ), QObject::tr( "Output cell size" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true, 0.0 );
|
||||
cellSizeParam->setHelp( QObject::tr( "Cell size of the output layer. If not specified, the smallest cell size from the input layers will be used" ) );
|
||||
cellSizeParam->setFlags( cellSizeParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||
addParameter( cellSizeParam.release() );
|
||||
auto crsParam = std::make_unique<QgsProcessingParameterCrs>( QStringLiteral( "CRS" ), QObject::tr( "Output CRS" ), QVariant(), true );
|
||||
crsParam->setHelp( QObject::tr( "CRS of the output layer. If not specified, the CRS of the first input layer will be used" ) );
|
||||
crsParam->setFlags( crsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
|
||||
addParameter( crsParam.release() );
|
||||
|
||||
addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Ranked" ) ) );
|
||||
}
|
||||
|
||||
bool QgsRasterRankAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback * )
|
||||
{
|
||||
const QStringList rankStrings = parameterAsString( parameters, QStringLiteral( "RANKS" ), context ).split( QLatin1String( "," ) );
|
||||
for ( const QString &rankString : rankStrings )
|
||||
{
|
||||
bool ok = false;
|
||||
const int rank = rankString.toInt( &ok );
|
||||
if ( ok && rank != 0 )
|
||||
{
|
||||
mRanks << rank;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw QgsProcessingException( QObject::tr( "Rank values must be integers (found \"%1\")" ).arg( rankString ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( mRanks.isEmpty() )
|
||||
{
|
||||
throw QgsProcessingException( QObject::tr( "No valid non-zero rank value(s) provided" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT_RASTERS" ), context );
|
||||
for ( const QgsMapLayer *layer : std::as_const( layers ) )
|
||||
{
|
||||
if ( !qobject_cast<const QgsRasterLayer *>( layer ) || !layer->dataProvider() )
|
||||
continue;
|
||||
|
||||
std::unique_ptr<QgsMapLayer> clonedLayer;
|
||||
clonedLayer.reset( layer->clone() );
|
||||
clonedLayer->moveToThread( nullptr );
|
||||
mLayers.push_back( std::move( clonedLayer ) );
|
||||
}
|
||||
|
||||
if ( mLayers.empty() )
|
||||
{
|
||||
throw QgsProcessingException( QObject::tr( "At least one raster input layer must be specified" ) );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
QVariantMap QgsRasterRankAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
|
||||
{
|
||||
QList<QgsMapLayer *> layers; // Needed for QgsProcessingUtils::combineLayerExtents
|
||||
for ( auto &layer : mLayers )
|
||||
{
|
||||
layer->moveToThread( QThread::currentThread() );
|
||||
layers << layer.get();
|
||||
}
|
||||
|
||||
QgsCoordinateReferenceSystem outputCrs;
|
||||
if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
|
||||
{
|
||||
outputCrs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
|
||||
}
|
||||
else
|
||||
{
|
||||
outputCrs = mLayers[0]->crs();
|
||||
}
|
||||
|
||||
|
||||
QgsRasterLayer *templateRasterLayer = qobject_cast<QgsRasterLayer *>( mLayers[0].get() );
|
||||
const Qgis::DataType outputDataType = templateRasterLayer->dataProvider()->dataType( 1 );
|
||||
double outputNoData = 0.0;
|
||||
if ( templateRasterLayer->dataProvider()->sourceHasNoDataValue( 1 ) )
|
||||
{
|
||||
outputNoData = templateRasterLayer->dataProvider()->sourceNoDataValue( 1 );
|
||||
}
|
||||
else
|
||||
{
|
||||
outputNoData = -FLT_MAX;
|
||||
}
|
||||
const bool outputNoDataOverride = parameterAsInt( parameters, QStringLiteral( "NODATA_HANDLING" ), context ) == 1;
|
||||
|
||||
QgsRectangle outputExtent;
|
||||
if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
|
||||
{
|
||||
outputExtent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, outputCrs );
|
||||
}
|
||||
else
|
||||
{
|
||||
outputExtent = QgsProcessingUtils::combineLayerExtents( layers, outputCrs, context );
|
||||
}
|
||||
|
||||
double minCellSizeX = 1e9;
|
||||
double minCellSizeY = 1e9;
|
||||
for ( auto &layer : mLayers )
|
||||
{
|
||||
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer.get() );
|
||||
|
||||
QgsRectangle extent = rasterLayer->extent();
|
||||
if ( rasterLayer->crs() != outputCrs )
|
||||
{
|
||||
QgsCoordinateTransform ct( rasterLayer->crs(), outputCrs, context.transformContext() );
|
||||
extent = ct.transformBoundingBox( extent );
|
||||
}
|
||||
|
||||
minCellSizeX = std::min( minCellSizeX, ( extent.xMaximum() - extent.xMinimum() ) / rasterLayer->width() );
|
||||
minCellSizeY = std::min( minCellSizeY, ( extent.yMaximum() - extent.yMinimum() ) / rasterLayer->height() );
|
||||
}
|
||||
|
||||
double outputCellSizeX = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
|
||||
double outputCellSizeY = outputCellSizeX;
|
||||
if ( outputCellSizeX == 0 )
|
||||
{
|
||||
outputCellSizeX = minCellSizeX;
|
||||
outputCellSizeY = minCellSizeY;
|
||||
}
|
||||
|
||||
qgssize cols = static_cast<qgssize>( std::round( ( outputExtent.xMaximum() - outputExtent.xMinimum() ) / outputCellSizeX ) );
|
||||
qgssize rows = static_cast<qgssize>( std::round( ( outputExtent.yMaximum() - outputExtent.yMinimum() ) / outputCellSizeY ) );
|
||||
|
||||
const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
|
||||
const QFileInfo fi( outputFile );
|
||||
const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
|
||||
|
||||
auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
|
||||
writer->setOutputFormat( outputFormat );
|
||||
std::unique_ptr<QgsRasterDataProvider> provider( writer->createMultiBandRaster( outputDataType, cols, rows, outputExtent, outputCrs, mRanks.size() ) );
|
||||
if ( !provider )
|
||||
throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
|
||||
if ( !provider->isValid() )
|
||||
throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
|
||||
provider->setNoDataValue( 1, outputNoData );
|
||||
|
||||
std::map<QString, std::unique_ptr<QgsRasterInterface>> newProjectorInterfaces;
|
||||
std::map<QString, QgsRasterInterface *> inputInterfaces;
|
||||
std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
|
||||
std::vector<std::unique_ptr<QgsRasterBlock>> outputBlocks;
|
||||
outputBlocks.resize( mRanks.size() );
|
||||
for ( auto &layer : mLayers )
|
||||
{
|
||||
QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer.get() );
|
||||
if ( rasterLayer->crs() != outputCrs )
|
||||
{
|
||||
QgsRasterProjector *projector = new QgsRasterProjector();
|
||||
projector->setCrs( rasterLayer->crs(), outputCrs, context.transformContext() );
|
||||
projector->setInput( rasterLayer->dataProvider() );
|
||||
projector->setPrecision( QgsRasterProjector::Exact );
|
||||
newProjectorInterfaces[rasterLayer->id()].reset( projector );
|
||||
inputInterfaces[rasterLayer->id()] = projector;
|
||||
}
|
||||
else
|
||||
{
|
||||
inputInterfaces[rasterLayer->id()] = rasterLayer->dataProvider();
|
||||
}
|
||||
}
|
||||
|
||||
QgsRasterIterator rasterIterator( inputInterfaces[mLayers.at( 0 )->id()] );
|
||||
rasterIterator.startRasterRead( 1, cols, rows, outputExtent );
|
||||
int blockCount = static_cast<int>( rasterIterator.blockCount() );
|
||||
|
||||
const double step = blockCount > 0 ? 100.0 / blockCount : 0;
|
||||
std::vector<double> inputValues;
|
||||
inputValues.resize( mLayers.size() );
|
||||
for ( int currentBlock = 0; currentBlock < blockCount; currentBlock++ )
|
||||
{
|
||||
if ( feedback->isCanceled() )
|
||||
{
|
||||
break;
|
||||
}
|
||||
feedback->setProgress( currentBlock * step );
|
||||
|
||||
int iterLeft = 0;
|
||||
int iterTop = 0;
|
||||
int iterCols = 0;
|
||||
int iterRows = 0;
|
||||
QgsRectangle blockExtent;
|
||||
rasterIterator.next( 1, iterCols, iterRows, iterLeft, iterTop, blockExtent );
|
||||
|
||||
for ( const auto &inputInterface : inputInterfaces )
|
||||
{
|
||||
inputBlocks[inputInterface.first].reset( inputInterface.second->block( 1, blockExtent, iterCols, iterRows ) );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < mRanks.size(); i++ )
|
||||
{
|
||||
outputBlocks[i].reset( new QgsRasterBlock( outputDataType, iterCols, iterRows ) );
|
||||
outputBlocks[i]->setNoDataValue( outputNoData );
|
||||
}
|
||||
|
||||
for ( int row = 0; row < iterRows; row++ )
|
||||
{
|
||||
for ( int col = 0; col < iterCols; col++ )
|
||||
{
|
||||
int valuesCount = 0;
|
||||
for ( const auto &inputBlock : inputBlocks )
|
||||
{
|
||||
bool isNoData = false;
|
||||
const double value = inputBlock.second->valueAndNoData( row, col, isNoData );
|
||||
if ( !isNoData )
|
||||
{
|
||||
inputValues[valuesCount] = value;
|
||||
valuesCount++;
|
||||
}
|
||||
else if ( outputNoDataOverride )
|
||||
{
|
||||
valuesCount = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::sort( inputValues.begin(), inputValues.begin() + valuesCount );
|
||||
|
||||
for ( int i = 0; i < mRanks.size(); i++ )
|
||||
{
|
||||
if ( valuesCount >= std::abs( mRanks[i] ) )
|
||||
{
|
||||
outputBlocks[i]->setValue( row, col, inputValues[mRanks[i] > 0 ? mRanks[i] - 1 : valuesCount + mRanks[i]] );
|
||||
}
|
||||
else
|
||||
{
|
||||
outputBlocks[i]->setValue( row, col, outputNoData );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( int i = 0; i < mRanks.size(); i++ )
|
||||
{
|
||||
if ( !provider->writeBlock( outputBlocks[i].get(), i + 1, iterLeft, iterTop ) )
|
||||
{
|
||||
throw QgsProcessingException( QObject::tr( "Could not write output raster block: %1" ).arg( provider->error().summary() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QVariantMap outputs;
|
||||
outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
|
||||
return outputs;
|
||||
}
|
||||
|
||||
///@endcond
|
55
src/analysis/processing/qgsalgorithmrasterrank.h
Normal file
55
src/analysis/processing/qgsalgorithmrasterrank.h
Normal file
@ -0,0 +1,55 @@
|
||||
/***************************************************************************
|
||||
qgsalgorithmrasterrank.h
|
||||
---------------------
|
||||
begin : February 2024
|
||||
copyright : (C) 2024 by Mathieu Pellerin
|
||||
email : mathieu at opengis dot ch
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSALGORITHMRASTERRANK_H
|
||||
#define QGSALGORITHMRASTERRANK_H
|
||||
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis_sip.h"
|
||||
#include "qgsprocessingalgorithm.h"
|
||||
#include "qgsapplication.h"
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* Native raster rank algorithm.
|
||||
*/
|
||||
class QgsRasterRankAlgorithm : public QgsProcessingAlgorithm
|
||||
{
|
||||
public:
|
||||
QgsRasterRankAlgorithm() = default;
|
||||
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
|
||||
QString name() const override;
|
||||
QString displayName() const override;
|
||||
QStringList tags() const override;
|
||||
QString group() const override;
|
||||
QString groupId() const override;
|
||||
QString shortHelpString() const override;
|
||||
QgsRasterRankAlgorithm *createInstance() const override SIP_FACTORY;
|
||||
|
||||
protected:
|
||||
bool prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
QVariantMap processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
|
||||
QList<int> mRanks;
|
||||
std::vector<std::unique_ptr<QgsMapLayer>> mLayers;
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSALGORITHMRASTERRANK_H
|
@ -175,6 +175,7 @@
|
||||
#include "qgsalgorithmrasterlogicalop.h"
|
||||
#include "qgsalgorithmrasterminmax.h"
|
||||
#include "qgsalgorithmrasterize.h"
|
||||
#include "qgsalgorithmrasterrank.h"
|
||||
#include "qgsalgorithmrastersampling.h"
|
||||
#include "qgsalgorithmrasterstackposition.h"
|
||||
#include "qgsalgorithmrasterstatistics.h"
|
||||
@ -499,6 +500,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
|
||||
addAlgorithm( new QgsRasterizeAlgorithm() );
|
||||
addAlgorithm( new QgsRasterPixelsToPointsAlgorithm() );
|
||||
addAlgorithm( new QgsRasterPixelsToPolygonsAlgorithm() );
|
||||
addAlgorithm( new QgsRasterRankAlgorithm() );
|
||||
addAlgorithm( new QgsRasterSamplingAlgorithm() );
|
||||
addAlgorithm( new QgsRasterStackHighestPositionAlgorithm() );
|
||||
addAlgorithm( new QgsRasterStackLowestPositionAlgorithm() );
|
||||
|
@ -105,6 +105,9 @@ class TestQgsProcessingAlgsPt1 : public QgsTest
|
||||
void createConstantRaster_data();
|
||||
void createConstantRaster();
|
||||
|
||||
void rasterRank_data();
|
||||
void rasterRank();
|
||||
|
||||
void densifyGeometries_data();
|
||||
void densifyGeometries();
|
||||
|
||||
@ -1897,6 +1900,113 @@ void TestQgsProcessingAlgsPt1::createConstantRaster()
|
||||
}
|
||||
}
|
||||
|
||||
void TestQgsProcessingAlgsPt1::rasterRank_data()
|
||||
{
|
||||
QTest::addColumn<QString>( "expectedRaster" );
|
||||
QTest::addColumn<QString>( "ranks" );
|
||||
QTest::addColumn<int>( "nodataHandling" );
|
||||
|
||||
/*
|
||||
* Testcase 1
|
||||
*/
|
||||
QTest::newRow( "testcase 1" )
|
||||
<< QStringLiteral( "/rasterRank_testcase1.tif" )
|
||||
<< QString::number( 2 )
|
||||
<< 0;
|
||||
|
||||
/*
|
||||
* Testcase 2
|
||||
*/
|
||||
QTest::newRow( "testcase 2" )
|
||||
<< QStringLiteral( "/rasterRank_testcase2.tif" )
|
||||
<< QString::number( -2 )
|
||||
<< 0;
|
||||
|
||||
/*
|
||||
* Testcase 3
|
||||
*/
|
||||
QTest::newRow( "testcase 3" )
|
||||
<< QStringLiteral( "/rasterRank_testcase3.tif" )
|
||||
<< QString::number( 2 )
|
||||
<< 1;
|
||||
|
||||
/*
|
||||
* Testcase 4
|
||||
*/
|
||||
QTest::newRow( "testcase 4" )
|
||||
<< QStringLiteral( "/rasterRank_testcase4.tif" )
|
||||
<< QStringLiteral( "2,-2" )
|
||||
<< 0;
|
||||
}
|
||||
|
||||
void TestQgsProcessingAlgsPt1::rasterRank()
|
||||
{
|
||||
QFETCH( QString, expectedRaster );
|
||||
QFETCH( QString, ranks );
|
||||
QFETCH( int, nodataHandling );
|
||||
|
||||
//prepare input params
|
||||
std::unique_ptr<QgsProcessingAlgorithm> alg( QgsApplication::processingRegistry()->createAlgorithmById( QStringLiteral( "native:rasterrank" ) ) );
|
||||
|
||||
const QString testDataPath( TEST_DATA_DIR ); //defined in CMakeLists.txt
|
||||
|
||||
QVariantMap parameters;
|
||||
|
||||
parameters.insert( QStringLiteral( "INPUT_RASTERS" ), QStringList() << testDataPath + "/raster/rank1.tif" << testDataPath + "/raster/rank2.tif" << testDataPath + "/raster/rank3.tif" << testDataPath + "/raster/rank4.tif" );
|
||||
parameters.insert( QStringLiteral( "RANKS" ), ranks );
|
||||
parameters.insert( QStringLiteral( "NODATA_HANDLING" ), nodataHandling );
|
||||
parameters.insert( QStringLiteral( "OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
|
||||
|
||||
bool ok = false;
|
||||
QgsProcessingFeedback feedback;
|
||||
QVariantMap results;
|
||||
|
||||
//prepare expected raster
|
||||
auto expectedRasterLayer = std::make_unique<QgsRasterLayer>( testDataPath + "/control_images/expected_rasterRank" + expectedRaster, "expectedDataset", "gdal" );
|
||||
std::unique_ptr<QgsRasterInterface> expectedInterface( expectedRasterLayer->dataProvider()->clone() );
|
||||
QgsRasterIterator expectedIter( expectedInterface.get() );
|
||||
expectedIter.startRasterRead( 1, expectedRasterLayer->width(), expectedRasterLayer->height(), expectedInterface->extent() );
|
||||
|
||||
//run algorithm...
|
||||
auto context = std::make_unique<QgsProcessingContext>();
|
||||
results = alg->run( parameters, *context, &feedback, &ok );
|
||||
QVERIFY( ok );
|
||||
|
||||
//...and check results with expected datasets
|
||||
auto outputRaster = std::make_unique<QgsRasterLayer>( results.value( QStringLiteral( "OUTPUT" ) ).toString(), "output", "gdal" );
|
||||
std::unique_ptr<QgsRasterInterface> outputInterface( outputRaster->dataProvider()->clone() );
|
||||
|
||||
QCOMPARE( outputRaster->width(), expectedRasterLayer->width() );
|
||||
QCOMPARE( outputRaster->height(), expectedRasterLayer->height() );
|
||||
|
||||
QgsRasterIterator outputIter( outputInterface.get() );
|
||||
outputIter.startRasterRead( 1, outputRaster->width(), outputRaster->height(), outputInterface->extent() );
|
||||
int outputIterLeft = 0;
|
||||
int outputIterTop = 0;
|
||||
int outputIterCols = 0;
|
||||
int outputIterRows = 0;
|
||||
int expectedIterLeft = 0;
|
||||
int expectedIterTop = 0;
|
||||
int expectedIterCols = 0;
|
||||
int expectedIterRows = 0;
|
||||
|
||||
std::unique_ptr<QgsRasterBlock> outputRasterBlock;
|
||||
std::unique_ptr<QgsRasterBlock> expectedRasterBlock;
|
||||
|
||||
while ( outputIter.readNextRasterPart( 1, outputIterCols, outputIterRows, outputRasterBlock, outputIterLeft, outputIterTop ) && expectedIter.readNextRasterPart( 1, expectedIterCols, expectedIterRows, expectedRasterBlock, expectedIterLeft, expectedIterTop ) )
|
||||
{
|
||||
for ( int row = 0; row < expectedIterRows; row++ )
|
||||
{
|
||||
for ( int column = 0; column < expectedIterCols; column++ )
|
||||
{
|
||||
const double expectedValue = expectedRasterBlock->value( row, column );
|
||||
const double outputValue = outputRasterBlock->value( row, column );
|
||||
QCOMPARE( outputValue, expectedValue );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TestQgsProcessingAlgsPt1::densifyGeometries_data()
|
||||
{
|
||||
|
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase1.tif
vendored
Normal file
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase1.tif
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase2.tif
vendored
Normal file
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase2.tif
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase3.tif
vendored
Normal file
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase3.tif
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase4.tif
vendored
Normal file
BIN
tests/testdata/control_images/expected_rasterRank/rasterRank_testcase4.tif
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/raster/rank1.tif
vendored
Normal file
BIN
tests/testdata/raster/rank1.tif
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/raster/rank2.tif
vendored
Normal file
BIN
tests/testdata/raster/rank2.tif
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/raster/rank3.tif
vendored
Normal file
BIN
tests/testdata/raster/rank3.tif
vendored
Normal file
Binary file not shown.
BIN
tests/testdata/raster/rank4.tif
vendored
Normal file
BIN
tests/testdata/raster/rank4.tif
vendored
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user