20#include <unordered_map>
21#include <unordered_set>
31using namespace Qt::StringLiterals;
35void QgsRasterAnalysisUtils::cellInfoForBBox(
36 const QgsRectangle &rasterBBox,
const QgsRectangle &featureBBox,
double cellSizeX,
double cellSizeY,
int &nCellsX,
int &nCellsY,
int rasterWidth,
int rasterHeight,
QgsRectangle &rasterBlockExtent
50 const int offsetX =
static_cast<int>( std::floor( ( intersectBox.
xMinimum() - rasterBBox.
xMinimum() ) / cellSizeX ) );
51 const int offsetY =
static_cast<int>( std::floor( ( rasterBBox.
yMaximum() - intersectBox.
yMaximum() ) / cellSizeY ) );
53 const int maxColumn =
static_cast<int>( std::floor( ( intersectBox.
xMaximum() - rasterBBox.
xMinimum() ) / cellSizeX ) ) + 1;
54 const int maxRow =
static_cast<int>( std::floor( ( rasterBBox.
yMaximum() - intersectBox.
yMinimum() ) / cellSizeY ) ) + 1;
56 nCellsX = maxColumn - offsetX;
57 nCellsY = maxRow - offsetY;
60 nCellsX = std::min( offsetX + nCellsX, rasterWidth ) - offsetX;
61 nCellsY = std::min( offsetY + nCellsY, rasterHeight ) - offsetY;
64 =
QgsRectangle( rasterBBox.
xMinimum() + offsetX * cellSizeX, rasterBBox.
yMaximum() - offsetY * cellSizeY, rasterBBox.
xMinimum() + ( nCellsX + offsetX ) * cellSizeX, rasterBBox.
yMaximum() - ( nCellsY + offsetY ) * cellSizeY );
67void QgsRasterAnalysisUtils::statisticsFromMiddlePointTest(
76 const std::function<
void(
double,
const QgsPointXY & )> &addValue,
83 auto polyEngine = std::make_unique<QgsGeos>( poly.
constGet() );
84 polyEngine->prepareGeometry();
87 iter.startRasterRead( rasterBand, nCellsX, nCellsY, rasterBBox );
89 std::unique_ptr<QgsRasterBlock> block;
95 bool isNoData =
false;
96 while ( iter.readNextRasterPart( rasterBand, iterCols, iterRows, block, iterLeft, iterTop, &blockExtent ) )
98 double cellCenterY = blockExtent.
yMaximum() - 0.5 * cellSizeY;
100 for (
int row = 0; row < iterRows; ++row )
102 double cellCenterX = blockExtent.
xMinimum() + 0.5 * cellSizeX;
103 for (
int col = 0; col < iterCols; ++col )
105 const double pixelValue = block->valueAndNoData( row, col, isNoData );
106 if ( validPixel( pixelValue ) && ( !skipNodata || !isNoData ) )
108 if ( polyEngine->contains( cellCenterX, cellCenterY ) )
110 addValue( pixelValue,
QgsPointXY( cellCenterX, cellCenterY ) );
113 cellCenterX += cellSizeX;
115 cellCenterY -= cellSizeY;
120void QgsRasterAnalysisUtils::statisticsFromPreciseIntersection(
129 const std::function<
void(
double,
double,
const QgsPointXY & )> &addValue,
135 const double hCellSizeX = cellSizeX / 2.0;
136 const double hCellSizeY = cellSizeY / 2.0;
137 const double pixelArea = cellSizeX * cellSizeY;
145 polyEngine->prepareGeometry();
148 iter.startRasterRead( rasterBand, nCellsX, nCellsY, rasterBBox );
150 std::unique_ptr<QgsRasterBlock> block;
156 bool isNoData =
false;
157 while ( iter.readNextRasterPart( rasterBand, iterCols, iterRows, block, iterLeft, iterTop, &blockExtent ) )
159 double currentY = blockExtent.
yMaximum() - 0.5 * cellSizeY;
160 for (
int row = 0; row < iterRows; ++row )
162 double currentX = blockExtent.
xMinimum() + 0.5 * cellSizeX;
163 for (
int col = 0; col < iterCols; ++col )
165 const double pixelValue = block->valueAndNoData( row, col, isNoData );
166 if ( validPixel( pixelValue ) && ( !skipNodata || !isNoData ) )
171 if ( !pixelRectGeometry.
isNull() && polyEngine->intersects( pixelRectGeometry.
constGet() ) )
175 if ( !intersectGeometry.
isEmpty() )
177 const double intersectionArea = intersectGeometry.
area();
178 if ( intersectionArea > 0.0 )
180 weight = intersectionArea / pixelArea;
181 addValue( pixelValue, weight,
QgsPointXY( currentX, currentY ) );
186 currentX += cellSizeX;
188 currentY -= cellSizeY;
193bool QgsRasterAnalysisUtils::validPixel(
double value )
195 return !std::isnan( value );
198void QgsRasterAnalysisUtils::mapToPixel(
const double x,
const double y,
const QgsRectangle bounds,
const double unitsPerPixelX,
const double unitsPerPixelY,
int &px,
int &py )
200 px = trunc( ( x - bounds.
xMinimum() ) / unitsPerPixelX );
201 py = trunc( ( y - bounds.
yMaximum() ) / -unitsPerPixelY );
204void QgsRasterAnalysisUtils::pixelToMap(
const int px,
const int py,
const QgsRectangle bounds,
const double unitsPerPixelX,
const double unitsPerPixelY,
double &x,
double &y )
206 x = bounds.
xMinimum() + ( px + 0.5 ) * unitsPerPixelX;
207 y = bounds.
yMaximum() - ( py + 0.5 ) * unitsPerPixelY;
210static QVector<QPair<QString, Qgis::DataType>> sDataTypes;
212void populateDataTypes()
214 if ( sDataTypes.empty() )
231std::unique_ptr<QgsProcessingParameterDefinition> QgsRasterAnalysisUtils::createRasterTypeParameter(
const QString &name,
const QString &description,
Qgis::DataType defaultType )
236 int defaultChoice = 0;
238 for (
auto it = sDataTypes.constBegin(); it != sDataTypes.constEnd(); ++it )
240 names.append( it->first );
241 if ( it->second == defaultType )
246 return std::make_unique<QgsProcessingParameterEnum>( name, description, names,
false, defaultChoice );
249Qgis::DataType QgsRasterAnalysisUtils::rasterTypeChoiceToDataType(
int choice )
251 if ( choice < 0 || choice >= sDataTypes.count() )
254 return sDataTypes.value( choice ).second;
257void QgsRasterAnalysisUtils::applyRasterLogicOperator(
258 const std::vector<QgsRasterAnalysisUtils::RasterLogicInput> &inputs,
259 std::unique_ptr<QgsRasterDataProvider> destinationRaster,
260 double outputNoDataValue,
261 const bool treatNoDataAsFalse,
266 std::function<
void(
const std::vector<std::unique_ptr<QgsRasterBlock>> &,
bool &,
bool &,
int,
int,
bool )> &applyLogicFunc,
272 destinationRaster->setEditable(
true );
274 outputIter.startRasterRead( 1, width, height, extent );
276 const bool hasReportsDuringClose = destinationRaster->hasReportsDuringClose();
277 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
284 std::unique_ptr<QgsRasterBlock> outputBlock;
285 while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
287 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
288 for (
const QgsRasterAnalysisUtils::RasterLogicInput &i : inputs )
290 for (
const int band : i.bands )
292 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
293 inputBlocks.emplace_back( std::move( b ) );
297 feedback->
setProgress( maxProgressDuringBlockWriting * outputIter.progress( 1 ) );
298 for (
int row = 0; row < iterRows; row++ )
303 for (
int column = 0; column < iterCols; column++ )
306 bool resIsNoData =
false;
307 applyLogicFunc( inputBlocks, res, resIsNoData, row, column, treatNoDataAsFalse );
315 outputBlock->setValue( row, column, resIsNoData ? outputNoDataValue : ( res ? 1 : 0 ) );
318 if ( !destinationRaster->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
320 throw QgsProcessingException( QObject::tr(
"Could not write raster block: %1" ).arg( destinationRaster->error().summary() ) );
323 destinationRaster->setEditable(
false );
325 if ( hasReportsDuringClose )
328 if ( !destinationRaster->closeWithProgress( scaledFeedback.get() ) )
337std::vector<double> QgsRasterAnalysisUtils::getCellValuesFromBlockStack(
const std::vector<std::unique_ptr<QgsRasterBlock>> &inputBlocks,
int &row,
int &col,
bool &noDataInStack )
340 std::vector<double> cellValues;
341 bool hasNoData =
false;
342 cellValues.reserve( inputBlocks.size() );
344 for (
auto &block : inputBlocks )
347 if ( !block || !block->isValid() )
349 noDataInStack =
true;
354 value = block->valueAndNoData( row, col, hasNoData );
357 noDataInStack =
true;
362 cellValues.push_back( value );
369double QgsRasterAnalysisUtils::meanFromCellValues( std::vector<double> &cellValues,
int stackSize )
371 const double sum = std::accumulate( cellValues.begin(), cellValues.end(), 0.0 );
372 const double mean = sum /
static_cast<double>( stackSize );
376double QgsRasterAnalysisUtils::medianFromCellValues( std::vector<double> &cellValues,
int stackSize )
378 std::sort( cellValues.begin(), cellValues.end() );
379 const bool even = ( stackSize % 2 ) < 1;
382 return ( cellValues[stackSize / 2 - 1] + cellValues[stackSize / 2] ) / 2.0;
386 return cellValues[( stackSize + 1 ) / 2 - 1];
391double QgsRasterAnalysisUtils::stddevFromCellValues( std::vector<double> &cellValues,
int stackSize )
393 const double variance = varianceFromCellValues( cellValues, stackSize );
394 const double stddev = std::sqrt( variance );
398double QgsRasterAnalysisUtils::varianceFromCellValues( std::vector<double> &cellValues,
int stackSize )
400 const double mean = meanFromCellValues( cellValues, stackSize );
402 for (
int i = 0; i < stackSize; i++ )
404 accum += std::pow( ( cellValues.at( i ) - mean ), 2.0 );
406 const double variance = accum /
static_cast<double>( stackSize );
410double QgsRasterAnalysisUtils::maximumFromCellValues( std::vector<double> &cellValues )
412 return *std::max_element( cellValues.begin(), cellValues.end() );
415double QgsRasterAnalysisUtils::minimumFromCellValues( std::vector<double> &cellValues )
417 return *std::min_element( cellValues.begin(), cellValues.end() );
420double QgsRasterAnalysisUtils::majorityFromCellValues( std::vector<double> &cellValues,
const double noDataValue,
int stackSize )
422 if ( stackSize == 1 )
425 return cellValues[0];
427 else if ( stackSize == 2 )
430 return (
qgsDoubleNear( cellValues[0], cellValues[1] ) ) ? cellValues[0] : noDataValue;
432 else if ( std::adjacent_find( cellValues.begin(), cellValues.end(), std::not_equal_to<double>() ) == cellValues.end() )
436 return cellValues[0];
441 std::unordered_map<double, int> map;
443 for (
int i = 0; i < stackSize; i++ )
445 map[cellValues[i]]++;
449 bool multipleMajorities =
false;
450 double result = noDataValue;
451 for (
const auto &pair : std::as_const( map ) )
453 if ( maxCount < pair.second )
456 maxCount = pair.second;
457 multipleMajorities =
false;
459 else if ( maxCount == pair.second )
461 multipleMajorities =
true;
464 return multipleMajorities ? noDataValue : result;
468double QgsRasterAnalysisUtils::minorityFromCellValues( std::vector<double> &cellValues,
const double noDataValue,
int stackSize )
470 if ( stackSize == 1 )
473 return cellValues[0];
475 else if ( stackSize == 2 )
478 return (
qgsDoubleNear( cellValues[0], cellValues[1] ) ) ? cellValues[0] : noDataValue;
480 else if ( std::adjacent_find( cellValues.begin(), cellValues.end(), std::not_equal_to<double>() ) == cellValues.end() )
484 return cellValues[0];
489 std::unordered_map<double, int> map;
491 for (
int i = 0; i < stackSize; i++ )
493 map[cellValues[i]]++;
496 int minCount = stackSize;
497 bool multipleMinorities =
false;
498 double result = noDataValue;
499 for (
const auto &pair : std::as_const( map ) )
501 if ( minCount > pair.second )
504 minCount = pair.second;
505 multipleMinorities =
false;
507 else if ( minCount == pair.second )
509 multipleMinorities =
true;
512 return multipleMinorities ? noDataValue : result;
516double QgsRasterAnalysisUtils::rangeFromCellValues( std::vector<double> &cellValues )
518 const double max = *std::max_element( cellValues.begin(), cellValues.end() );
519 const double min = *std::min_element( cellValues.begin(), cellValues.end() );
523double QgsRasterAnalysisUtils::varietyFromCellValues( std::vector<double> &cellValues )
525 const std::unordered_set<double> uniqueValues( cellValues.begin(), cellValues.end() );
526 return uniqueValues.size();
529double QgsRasterAnalysisUtils::nearestRankPercentile( std::vector<double> &cellValues,
int stackSize,
double percentile )
532 std::sort( cellValues.begin(), cellValues.end() );
535 if ( percentile > 0 )
537 i = std::ceil( percentile *
static_cast<double>( stackSize ) ) - 1;
540 return cellValues[i];
543double QgsRasterAnalysisUtils::interpolatedPercentileInc( std::vector<double> &cellValues,
int stackSize,
double percentile )
545 std::sort( cellValues.begin(), cellValues.end() );
549 return cellValues[stackSize - 1];
553 return cellValues[0];
556 const double x = ( percentile * ( stackSize - 1 ) );
558 const int i =
static_cast<int>( std::floor( x ) );
559 const double xFraction = std::fmod( x, 1 );
561 if ( stackSize == 1 )
563 return cellValues[0];
565 else if ( stackSize == 2 )
567 return cellValues[0] + ( cellValues[1] - cellValues[0] ) * percentile;
571 return cellValues[i] + ( cellValues[i + 1] - cellValues[i] ) * xFraction;
575double QgsRasterAnalysisUtils::interpolatedPercentileExc( std::vector<double> &cellValues,
int stackSize,
double percentile,
double noDataValue )
577 std::sort( cellValues.begin(), cellValues.end() );
578 const double x = ( percentile * ( stackSize + 1 ) );
580 const int i =
static_cast<int>( std::floor( x ) ) - 1;
581 const double xFraction = std::fmod( x, 1 );
582 const double lowerExcValue = 1.0 / (
static_cast<double>( stackSize ) + 1.0 );
583 const double upperExcValue =
static_cast<double>( stackSize ) / (
static_cast<double>( stackSize ) + 1.0 );
585 if ( stackSize < 2 || ( ( percentile < lowerExcValue || percentile > upperExcValue ) ) )
591 return cellValues[i] + ( cellValues[i + 1] - cellValues[i] ) * xFraction;
595double QgsRasterAnalysisUtils::interpolatedPercentRankInc( std::vector<double> &cellValues,
int stackSize,
double value,
double noDataValue )
597 std::sort( cellValues.begin(), cellValues.end() );
599 if ( value < cellValues[0] || value > cellValues[stackSize - 1] )
605 for (
int i = 0; i < stackSize - 1; i++ )
607 if ( cellValues[i] <= value && cellValues[i + 1] >= value )
609 double fraction = 0.0;
613 fraction = ( value - cellValues[i] ) / ( cellValues[i + 1] - cellValues[i] );
615 return ( fraction + i ) / ( stackSize - 1 );
622double QgsRasterAnalysisUtils::interpolatedPercentRankExc( std::vector<double> &cellValues,
int stackSize,
double value,
double noDataValue )
624 std::sort( cellValues.begin(), cellValues.end() );
626 if ( value < cellValues[0] || value > cellValues[stackSize - 1] )
632 for (
int i = 0; i < stackSize - 1; i++ )
634 if ( cellValues[i] <= value && cellValues[i + 1] >= value )
636 double fraction = 0.0;
640 fraction = ( value - cellValues[i] ) / ( cellValues[i + 1] - cellValues[i] );
642 return ( ( i + 1 ) + fraction ) / ( stackSize + 1 );
DataType
Raster data types.
@ Float32
Thirty two bit floating point (float).
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16).
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
@ UInt16
Sixteen bit unsigned integer (quint16).
@ Byte
Eight bit unsigned integer (quint8).
@ Int32
Thirty two bit signed integer (qint32).
@ Float64
Sixty four bit floating point (double).
@ CFloat32
Complex Float32.
@ UInt32
Thirty two bit unsigned integer (quint32).
Base class for feedback objects to be used for cancellation of something running in a worker thread.
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...
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
double area() const
Returns the planar, 2-dimensional area of the geometry.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters ¶meters=QgsGeometryParameters()) const
Returns a geometry representing the points shared by this geometry and other.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
Custom exception class for processing related exceptions.
Base class for processing filters like renderers, reprojector, resampler etc.
Iterator for sequentially processing raster cells.
A rectangle specified with double values.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
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...
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).