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
59 "This algorithm performs a cell-by-cell analysis in which output values match the rank of a "
60 "sorted list of overlapping cell values from input layers. The output raster "
61 "will be multi-band if multiple ranks are provided.\n"
62 "If multiband rasters are used in the data raster stack, the algorithm will always "
63 "perform the analysis on the first band of the rasters."
67QString QgsRasterRankAlgorithm::shortDescription()
const
70 "Performs a cell-by-cell analysis in which output values match the rank of a "
71 "sorted list of overlapping cell values from input layers."
75QgsRasterRankAlgorithm *QgsRasterRankAlgorithm::createInstance()
const
77 return new QgsRasterRankAlgorithm();
80void QgsRasterRankAlgorithm::initAlgorithm(
const QVariantMap & )
83 auto ranksParameter = std::make_unique<QgsProcessingParameterString>( u
"RANKS"_s, QObject::tr(
"Rank(s) (separate multiple ranks using commas)" ), 1 );
84 ranksParameter->setHelp(
86 "A rank value must be numerical, with multiple ranks separated by commas. The rank will be used to "
87 "generate output values from sorted lists of input layers’ cell values. A rank value of 1 will pick "
88 "the first value from a given sorted input layers’ cell values list (i.e. the minimum value). "
89 "Negative rank values are supported, and will behave like a negative index. A rank value of -2 will "
90 "pick the second to last value in sorted input values lists, while a rank value of -1 will pick the "
91 "last value (i.e. the maximum value)."
94 addParameter( ranksParameter.release() );
96 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 )
99 auto extentParam = std::make_unique<QgsProcessingParameterExtent>( u
"EXTENT"_s, QObject::tr(
"Output extent" ), QVariant(),
true );
100 extentParam->setHelp( QObject::tr(
"Extent of the output layer. If not specified, the extent will be the overall extent of all input layers" ) );
102 addParameter( extentParam.release() );
104 cellSizeParam->setHelp( QObject::tr(
"Cell size of the output layer. If not specified, the smallest cell size from the input layers will be used" ) );
106 addParameter( cellSizeParam.release() );
107 auto crsParam = std::make_unique<QgsProcessingParameterCrs>( u
"CRS"_s, QObject::tr(
"Output CRS" ), QVariant(),
true );
108 crsParam->setHelp( QObject::tr(
"CRS of the output layer. If not specified, the CRS of the first input layer will be used" ) );
110 addParameter( crsParam.release() );
117 const QStringList rankStrings = parameterAsString( parameters, u
"RANKS"_s, context ).split(
","_L1 );
118 for (
const QString &rankString : rankStrings )
121 const int rank = rankString.toInt( &ok );
122 if ( ok && rank != 0 )
128 throw QgsProcessingException( QObject::tr(
"Rank values must be integers (found \"%1\")" ).arg( rankString ) );
132 if ( mRanks.isEmpty() )
138 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u
"INPUT_RASTERS"_s, context );
139 for (
const QgsMapLayer *layer : std::as_const( layers ) )
141 if ( !qobject_cast<const QgsRasterLayer *>( layer ) || !layer->dataProvider() )
144 std::unique_ptr<QgsMapLayer> clonedLayer;
145 clonedLayer.reset( layer->clone() );
146 clonedLayer->moveToThread(
nullptr );
147 mLayers.push_back( std::move( clonedLayer ) );
150 if ( mLayers.empty() )
162 QList<QgsMapLayer *> layers;
163 for (
auto &layer : mLayers )
165 layer->moveToThread( QThread::currentThread() );
166 layers << layer.get();
170 if ( parameters.value( u
"CRS"_s ).isValid() )
172 outputCrs = parameterAsCrs( parameters, u
"CRS"_s, context );
176 outputCrs = mLayers[0]->crs();
180 QgsRasterLayer *templateRasterLayer = qobject_cast<QgsRasterLayer *>( mLayers[0].get() );
182 double outputNoData = 0.0;
189 outputNoData = -FLT_MAX;
191 const bool outputNoDataOverride = parameterAsInt( parameters, u
"NODATA_HANDLING"_s, context ) == 1;
194 if ( parameters.value( u
"EXTENT"_s ).isValid() )
196 outputExtent = parameterAsExtent( parameters, u
"EXTENT"_s, context, outputCrs );
203 double minCellSizeX = 1e9;
204 double minCellSizeY = 1e9;
205 for (
auto &layer : mLayers )
207 QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer.get() );
210 if ( rasterLayer->
crs() != outputCrs )
213 extent = ct.transformBoundingBox( extent );
216 const int width = rasterLayer->
width();
217 const int height = rasterLayer->
height();
218 if ( width <= 0 || height <= 0 )
221 minCellSizeX = std::min( minCellSizeX, ( extent.
xMaximum() - extent.
xMinimum() ) / width );
222 minCellSizeY = std::min( minCellSizeY, ( extent.
yMaximum() - extent.
yMinimum() ) / height );
225 double outputCellSizeX = parameterAsDouble( parameters, u
"CELL_SIZE"_s, context );
226 double outputCellSizeY = outputCellSizeX;
227 if ( outputCellSizeX == 0 )
229 outputCellSizeX = minCellSizeX;
230 outputCellSizeY = minCellSizeY;
236 const QString outputFile = parameterAsOutputLayer( parameters, u
"OUTPUT"_s, context );
237 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u
"OUTPUT"_s, context );
239 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
240 writer->setOutputFormat( outputFormat );
241 std::unique_ptr<QgsRasterDataProvider> provider( writer->createMultiBandRaster( outputDataType, cols, rows, outputExtent, outputCrs, mRanks.size() ) );
244 if ( !provider->isValid() )
246 provider->setNoDataValue( 1, outputNoData );
248 std::map<QString, std::unique_ptr<QgsRasterInterface>> newProjectorInterfaces;
249 std::map<QString, QgsRasterInterface *> inputInterfaces;
250 std::map<QString, std::unique_ptr<QgsRasterBlock>> inputBlocks;
251 std::vector<std::unique_ptr<QgsRasterBlock>> outputBlocks;
252 outputBlocks.resize( mRanks.size() );
253 for (
auto &layer : mLayers )
255 QgsRasterLayer *rasterLayer = qobject_cast<QgsRasterLayer *>( layer.get() );
256 if ( rasterLayer->
crs() != outputCrs )
262 newProjectorInterfaces[rasterLayer->
id()].reset( projector );
263 inputInterfaces[rasterLayer->
id()] = projector;
272 rasterIterator.startRasterRead( 1, cols, rows, outputExtent );
273 int blockCount =
static_cast<int>( rasterIterator.blockCount() );
275 const bool hasReportsDuringClose = provider->hasReportsDuringClose();
276 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
278 const double step = blockCount > 0 ? maxProgressDuringBlockWriting / blockCount : 0;
279 std::vector<double> inputValues;
280 inputValues.resize( mLayers.size() );
281 for (
int currentBlock = 0; currentBlock < blockCount; currentBlock++ )
294 rasterIterator.next( 1, iterCols, iterRows, iterLeft, iterTop, blockExtent );
296 for (
const auto &inputInterface : inputInterfaces )
298 inputBlocks[inputInterface.first].reset( inputInterface.second->block( 1, blockExtent, iterCols, iterRows ) );
301 for (
int i = 0; i < mRanks.size(); i++ )
303 outputBlocks[i] = std::make_unique<QgsRasterBlock>( outputDataType, iterCols, iterRows );
304 outputBlocks[i]->setNoDataValue( outputNoData );
307 for (
int row = 0; row < iterRows; row++ )
309 for (
int col = 0; col < iterCols; col++ )
312 for (
const auto &inputBlock : inputBlocks )
314 bool isNoData =
false;
315 const double value = inputBlock.second->valueAndNoData( row, col, isNoData );
318 inputValues[valuesCount] = value;
321 else if ( outputNoDataOverride )
327 std::sort( inputValues.begin(), inputValues.begin() + valuesCount );
329 for (
int i = 0; i < mRanks.size(); i++ )
331 if ( valuesCount >= std::abs( mRanks[i] ) )
333 outputBlocks[i]->setValue( row, col, inputValues[mRanks[i] > 0 ? mRanks[i] - 1 : valuesCount + mRanks[i]] );
337 outputBlocks[i]->setValue( row, col, outputNoData );
343 for (
int i = 0; i < mRanks.size(); i++ )
345 if ( !provider->writeBlock( outputBlocks[i].get(), i + 1, iterLeft, iterTop ) )
347 throw QgsProcessingException( QObject::tr(
"Could not write output raster block: %1" ).arg( provider->error().summary() ) );
352 if ( feedback && hasReportsDuringClose )
355 if ( !provider->closeWithProgress( scaledFeedback.get() ) )
364 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...