27using namespace Qt::StringLiterals;
31QString QgsRasterRankAlgorithm::name()
const
33 return u
"rasterrank"_s;
36QString QgsRasterRankAlgorithm::displayName()
const
38 return QObject::tr(
"Raster rank" );
41QStringList QgsRasterRankAlgorithm::tags()
const
43 return QObject::tr(
"raster,rank" ).split(
',' );
46QString QgsRasterRankAlgorithm::group()
const
48 return QObject::tr(
"Raster analysis" );
51QString QgsRasterRankAlgorithm::groupId()
const
53 return u
"rasteranalysis"_s;
56QString QgsRasterRankAlgorithm::shortHelpString()
const
58 return QObject::tr(
"This algorithm performs a cell-by-cell analysis in which output values match the rank of a "
59 "sorted list of overlapping cell values from input layers. The output raster "
60 "will be multi-band if multiple ranks are provided.\n"
61 "If multiband rasters are used in the data raster stack, the algorithm will always "
62 "perform the analysis on the first band of the rasters." );
65QString QgsRasterRankAlgorithm::shortDescription()
const
67 return QObject::tr(
"Performs a cell-by-cell analysis in which output values match the rank of a "
68 "sorted list of overlapping cell values from input layers." );
71QgsRasterRankAlgorithm *QgsRasterRankAlgorithm::createInstance()
const
73 return new QgsRasterRankAlgorithm();
76void QgsRasterRankAlgorithm::initAlgorithm(
const QVariantMap & )
79 auto ranksParameter = std::make_unique<QgsProcessingParameterString>( u
"RANKS"_s, QObject::tr(
"Rank(s) (separate multiple ranks using commas)" ), 1 );
80 ranksParameter->setHelp( QObject::tr(
"A rank value must be numerical, with multiple ranks separated by commas. The rank will be used to "
81 "generate output values from sorted lists of input layers’ cell values. A rank value of 1 will pick "
82 "the first value from a given sorted input layers’ cell values list (i.e. the minimum value). "
83 "Negative rank values are supported, and will behave like a negative index. A rank value of -2 will "
84 "pick the second to last value in sorted input values lists, while a rank value of -1 will pick the "
85 "last value (i.e. the maximum value)." ) );
86 addParameter( ranksParameter.release() );
87 addParameter(
new QgsProcessingParameterEnum( u
"NODATA_HANDLING"_s, 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 ) );
89 auto extentParam = std::make_unique<QgsProcessingParameterExtent>( u
"EXTENT"_s, QObject::tr(
"Output extent" ), QVariant(),
true );
90 extentParam->setHelp( QObject::tr(
"Extent of the output layer. If not specified, the extent will be the overall extent of all input layers" ) );
92 addParameter( extentParam.release() );
94 cellSizeParam->setHelp( QObject::tr(
"Cell size of the output layer. If not specified, the smallest cell size from the input layers will be used" ) );
96 addParameter( cellSizeParam.release() );
97 auto crsParam = std::make_unique<QgsProcessingParameterCrs>( u
"CRS"_s, QObject::tr(
"Output CRS" ), QVariant(),
true );
98 crsParam->setHelp( QObject::tr(
"CRS of the output layer. If not specified, the CRS of the first input layer will be used" ) );
100 addParameter( crsParam.release() );
107 const QStringList rankStrings = parameterAsString( parameters, u
"RANKS"_s, context ).split(
","_L1 );
108 for (
const QString &rankString : rankStrings )
111 const int rank = rankString.toInt( &ok );
112 if ( ok && rank != 0 )
118 throw QgsProcessingException( QObject::tr(
"Rank values must be integers (found \"%1\")" ).arg( rankString ) );
122 if ( mRanks.isEmpty() )
128 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u
"INPUT_RASTERS"_s, context );
129 for (
const QgsMapLayer *layer : std::as_const( layers ) )
131 if ( !qobject_cast<const QgsRasterLayer *>( layer ) || !layer->dataProvider() )
134 std::unique_ptr<QgsMapLayer> clonedLayer;
135 clonedLayer.reset( layer->clone() );
136 clonedLayer->moveToThread(
nullptr );
137 mLayers.push_back( std::move( clonedLayer ) );
140 if ( mLayers.empty() )
152 QList<QgsMapLayer *> layers;
153 for (
auto &layer : mLayers )
155 layer->moveToThread( QThread::currentThread() );
156 layers << layer.get();
160 if ( parameters.value( u
"CRS"_s ).isValid() )
162 outputCrs = parameterAsCrs( parameters, u
"CRS"_s, context );
166 outputCrs = mLayers[0]->crs();
170 QgsRasterLayer *templateRasterLayer = qobject_cast<QgsRasterLayer *>( mLayers[0].get() );
172 double outputNoData = 0.0;
179 outputNoData = -FLT_MAX;
181 const bool outputNoDataOverride = parameterAsInt( parameters, u
"NODATA_HANDLING"_s, context ) == 1;
184 if ( parameters.value( u
"EXTENT"_s ).isValid() )
186 outputExtent = parameterAsExtent( parameters, u
"EXTENT"_s, context, outputCrs );
193 double minCellSizeX = 1e9;
194 double minCellSizeY = 1e9;
195 for (
auto &layer : mLayers )
197 QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer.get() );
200 if ( rasterLayer->
crs() != outputCrs )
203 extent = ct.transformBoundingBox( extent );
206 const int width = rasterLayer->
width();
207 const int height = rasterLayer->
height();
208 if ( width <= 0 || height <= 0 )
211 minCellSizeX = std::min( minCellSizeX, ( extent.
xMaximum() - extent.
xMinimum() ) / width );
212 minCellSizeY = std::min( minCellSizeY, ( extent.
yMaximum() - extent.
yMinimum() ) / height );
215 double outputCellSizeX = parameterAsDouble( parameters, u
"CELL_SIZE"_s, context );
216 double outputCellSizeY = outputCellSizeX;
217 if ( outputCellSizeX == 0 )
219 outputCellSizeX = minCellSizeX;
220 outputCellSizeY = minCellSizeY;
226 const QString outputFile = parameterAsOutputLayer( parameters, u
"OUTPUT"_s, context );
227 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u
"OUTPUT"_s, context );
229 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
230 writer->setOutputFormat( outputFormat );
231 std::unique_ptr<QgsRasterDataProvider> provider( writer->createMultiBandRaster( outputDataType, cols, rows, outputExtent, outputCrs, mRanks.size() ) );
234 if ( !provider->isValid() )
236 provider->setNoDataValue( 1, outputNoData );
238 std::map<QString, std::unique_ptr<QgsRasterInterface>> newProjectorInterfaces;
239 std::map<QString, QgsRasterInterface *> inputInterfaces;
240 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
241 std::vector<std::unique_ptr<QgsRasterBlock>> outputBlocks;
242 outputBlocks.resize( mRanks.size() );
243 for (
auto &layer : mLayers )
245 QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer.get() );
246 if ( rasterLayer->
crs() != outputCrs )
252 newProjectorInterfaces[rasterLayer->
id()].reset( projector );
253 inputInterfaces[rasterLayer->
id()] = projector;
262 rasterIterator.startRasterRead( 1, cols, rows, outputExtent );
263 int blockCount =
static_cast<int>( rasterIterator.blockCount() );
265 const bool hasReportsDuringClose = provider->hasReportsDuringClose();
266 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
268 const double step = blockCount > 0 ? maxProgressDuringBlockWriting / blockCount : 0;
269 std::vector<double> inputValues;
270 inputValues.resize( mLayers.size() );
271 for (
int currentBlock = 0; currentBlock < blockCount; currentBlock++ )
284 rasterIterator.next( 1, iterCols, iterRows, iterLeft, iterTop, blockExtent );
286 for (
const auto &inputInterface : inputInterfaces )
288 inputBlocks[inputInterface.first].reset( inputInterface.second->block( 1, blockExtent, iterCols, iterRows ) );
291 for (
int i = 0; i < mRanks.size(); i++ )
293 outputBlocks[i] = std::make_unique<QgsRasterBlock>( outputDataType, iterCols, iterRows );
294 outputBlocks[i]->setNoDataValue( outputNoData );
297 for (
int row = 0; row < iterRows; row++ )
299 for (
int col = 0; col < iterCols; col++ )
302 for (
const auto &inputBlock : inputBlocks )
304 bool isNoData =
false;
305 const double value = inputBlock.second->valueAndNoData( row, col, isNoData );
308 inputValues[valuesCount] = value;
311 else if ( outputNoDataOverride )
317 std::sort( inputValues.begin(), inputValues.begin() + valuesCount );
319 for (
int i = 0; i < mRanks.size(); i++ )
321 if ( valuesCount >= std::abs( mRanks[i] ) )
323 outputBlocks[i]->setValue( row, col, inputValues[mRanks[i] > 0 ? mRanks[i] - 1 : valuesCount + mRanks[i]] );
327 outputBlocks[i]->setValue( row, col, outputNoData );
333 for (
int i = 0; i < mRanks.size(); i++ )
335 if ( !provider->writeBlock( outputBlocks[i].get(), i + 1, iterLeft, iterTop ) )
337 throw QgsProcessingException( QObject::tr(
"Could not write output raster block: %1" ).arg( provider->error().summary() ) );
342 if ( feedback && hasReportsDuringClose )
345 if ( !provider->closeWithProgress( scaledFeedback.get() ) )
354 outputs.insert( u
"OUTPUT"_s, outputFile );
DataType
Raster data types.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
@ Double
Double/float values.
Represents a coordinate reference system (CRS).
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
static std::unique_ptr< QgsFeedback > createScaledFeedback(QgsFeedback *parentFeedback, double startPercentage, double endPercentage)
Returns a feedback object whose [0, 100] progression range will be mapped to parentFeedback [startPer...
Base class for all map layer types.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A parameter for processing algorithms which accepts multiple map layers.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
static QgsRectangle combineLayerExtents(const QList< QgsMapLayer * > &layers, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context)
Combines the extent of several map layers.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
virtual bool setInput(QgsRasterInterface *input)
Set input.
Iterator for sequentially processing raster cells.
Represents a raster layer.
int height() const
Returns the height of the (unclipped) raster.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
int width() const
Returns the width of the (unclipped) raster.
Implements approximate projection support for optimised raster transformation.
void setPrecision(Precision precision)
@ Exact
Exact, precise but slow.
Q_DECL_DEPRECATED void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.
A rectangle specified with double values.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...