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 );
649double QgsRasterAnalysisUtils::neighborCellDistance(
int direction,
double cellSizeX,
double cellSizeY )
660 return std::hypot( cellSizeX, cellSizeY );
665bool QgsRasterAnalysisUtils::neighborCellCoordinates(
int direction,
int row,
int column,
int &neighborRow,
int &neighborColumn,
int rows,
int columns )
667 static constexpr std::array<int, 8> COL_DIRECTION_OFFSETS { 0, 1, 1, 1, 0, -1, -1, -1 };
668 static constexpr std::array<int, 8> ROW_DIRECTION_OFFSETS { -1, -1, 0, 1, 1, 1, 0, -1 };
670 neighborColumn = column + COL_DIRECTION_OFFSETS[direction];
671 neighborRow = row + ROW_DIRECTION_OFFSETS[direction];
672 return ( neighborColumn >= 0 && neighborColumn < columns && neighborRow >= 0 && neighborRow < rows );
675int QgsRasterAnalysisUtils::steepestGradientDirection(
const QgsRasterBlock *demBlock,
int row,
int column,
double cellSizeX,
double cellSizeY,
bool down,
bool noEdges )
677 const int rows = demBlock->
height();
678 const int columns = demBlock->
width();
680 bool isCenterNoData =
false;
681 const double z = demBlock->
valueAndNoData( row, column, isCenterNoData );
682 if ( isCenterNoData )
685 double maxGradient = 0.0;
686 int steepestDir = -1;
688 bool isNeighborNodata =
false;
690 for (
int dir = 0; dir < 8; ++dir )
694 if ( neighborCellCoordinates( dir, row, column, nRow, nCol, rows, columns ) )
696 const double nZ = demBlock->
valueAndNoData( nRow, nCol, isNeighborNodata );
697 if ( !isNeighborNodata )
699 const double grad = ( z - nZ ) / neighborCellDistance( dir, cellSizeX, cellSizeY );
701 if ( ( !down || grad > 0 ) && ( steepestDir == -1 || grad > maxGradient ) )
717 return isFlat ? -2 : steepestDir;
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.
QgsGeometry intersection(const QgsGeometry &geometry, const QgsGeometryParameters ¶meters=QgsGeometryParameters(), QgsFeedback *feedback=nullptr) const
Returns a geometry representing the points shared by this geometry and other.
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.
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.
int height() const
Returns the height (number of rows) of the raster block.
double valueAndNoData(int row, int column, bool &isNoData) const
Reads a single value from the pixel at row and column, if type of block is numeric.
int width() const
Returns the width (number of columns) of the raster block.
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.
#define BUILTIN_UNREACHABLE
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).