QGIS API Documentation 4.3.0-Master (45633be667c)
Loading...
Searching...
No Matches
qgsrasteranalysisutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasteranalysisutils.cpp
3 ---------------------
4 Date : June 2018
5 Copyright : (C) 2018 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include <cmath>
19#include <map>
20#include <unordered_map>
21#include <unordered_set>
22
23#include "qgsfeedback.h"
24#include "qgsgeos.h"
26#include "qgsrasterblock.h"
27#include "qgsrasteriterator.h"
28
29#include <QString>
30
31using namespace Qt::StringLiterals;
32
34
35void QgsRasterAnalysisUtils::cellInfoForBBox(
36 const QgsRectangle &rasterBBox, const QgsRectangle &featureBBox, double cellSizeX, double cellSizeY, int &nCellsX, int &nCellsY, int rasterWidth, int rasterHeight, QgsRectangle &rasterBlockExtent
37)
38{
39 //get intersecting bbox
40 const QgsRectangle intersectBox = rasterBBox.intersect( featureBBox );
41 if ( intersectBox.isEmpty() )
42 {
43 nCellsX = 0;
44 nCellsY = 0;
45 rasterBlockExtent = QgsRectangle();
46 return;
47 }
48
49 //get offset in pixels in x- and y- direction
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 ) );
52
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;
55
56 nCellsX = maxColumn - offsetX;
57 nCellsY = maxRow - offsetY;
58
59 //avoid access to cells outside of the raster (may occur because of rounding)
60 nCellsX = std::min( offsetX + nCellsX, rasterWidth ) - offsetX;
61 nCellsY = std::min( offsetY + nCellsY, rasterHeight ) - offsetY;
62
63 rasterBlockExtent
64 = QgsRectangle( rasterBBox.xMinimum() + offsetX * cellSizeX, rasterBBox.yMaximum() - offsetY * cellSizeY, rasterBBox.xMinimum() + ( nCellsX + offsetX ) * cellSizeX, rasterBBox.yMaximum() - ( nCellsY + offsetY ) * cellSizeY );
65}
66
67void QgsRasterAnalysisUtils::statisticsFromMiddlePointTest(
68 QgsRasterInterface *rasterInterface,
69 int rasterBand,
70 const QgsGeometry &poly,
71 int nCellsX,
72 int nCellsY,
73 double cellSizeX,
74 double cellSizeY,
75 const QgsRectangle &rasterBBox,
76 const std::function<void( double, const QgsPointXY & )> &addValue,
77 bool skipNodata
78)
79{
80 if ( !poly.constGet() )
81 return;
82
83 auto polyEngine = std::make_unique<QgsGeos>( poly.constGet() );
84 polyEngine->prepareGeometry();
85
86 QgsRasterIterator iter( rasterInterface );
87 iter.startRasterRead( rasterBand, nCellsX, nCellsY, rasterBBox );
88
89 std::unique_ptr<QgsRasterBlock> block;
90 int iterLeft = 0;
91 int iterTop = 0;
92 int iterCols = 0;
93 int iterRows = 0;
94 QgsRectangle blockExtent;
95 bool isNoData = false;
96 while ( iter.readNextRasterPart( rasterBand, iterCols, iterRows, block, iterLeft, iterTop, &blockExtent ) )
97 {
98 double cellCenterY = blockExtent.yMaximum() - 0.5 * cellSizeY;
99
100 for ( int row = 0; row < iterRows; ++row )
101 {
102 double cellCenterX = blockExtent.xMinimum() + 0.5 * cellSizeX;
103 for ( int col = 0; col < iterCols; ++col )
104 {
105 const double pixelValue = block->valueAndNoData( row, col, isNoData );
106 if ( validPixel( pixelValue ) && ( !skipNodata || !isNoData ) )
107 {
108 if ( polyEngine->contains( cellCenterX, cellCenterY ) )
109 {
110 addValue( pixelValue, QgsPointXY( cellCenterX, cellCenterY ) );
111 }
112 }
113 cellCenterX += cellSizeX;
114 }
115 cellCenterY -= cellSizeY;
116 }
117 }
118}
119
120void QgsRasterAnalysisUtils::statisticsFromPreciseIntersection(
121 QgsRasterInterface *rasterInterface,
122 int rasterBand,
123 const QgsGeometry &poly,
124 int nCellsX,
125 int nCellsY,
126 double cellSizeX,
127 double cellSizeY,
128 const QgsRectangle &rasterBBox,
129 const std::function<void( double, double, const QgsPointXY & )> &addValue,
130 bool skipNodata
131)
132{
133 QgsGeometry pixelRectGeometry;
134
135 const double hCellSizeX = cellSizeX / 2.0;
136 const double hCellSizeY = cellSizeY / 2.0;
137 const double pixelArea = cellSizeX * cellSizeY;
138 double weight = 0;
139
140 std::unique_ptr<QgsGeometryEngine> polyEngine( QgsGeometry::createGeometryEngine( poly.constGet() ) );
141 if ( !polyEngine )
142 {
143 return;
144 }
145 polyEngine->prepareGeometry();
146
147 QgsRasterIterator iter( rasterInterface );
148 iter.startRasterRead( rasterBand, nCellsX, nCellsY, rasterBBox );
149
150 std::unique_ptr<QgsRasterBlock> block;
151 int iterLeft = 0;
152 int iterTop = 0;
153 int iterCols = 0;
154 int iterRows = 0;
155 QgsRectangle blockExtent;
156 bool isNoData = false;
157 while ( iter.readNextRasterPart( rasterBand, iterCols, iterRows, block, iterLeft, iterTop, &blockExtent ) )
158 {
159 double currentY = blockExtent.yMaximum() - 0.5 * cellSizeY;
160 for ( int row = 0; row < iterRows; ++row )
161 {
162 double currentX = blockExtent.xMinimum() + 0.5 * cellSizeX;
163 for ( int col = 0; col < iterCols; ++col )
164 {
165 const double pixelValue = block->valueAndNoData( row, col, isNoData );
166 if ( validPixel( pixelValue ) && ( !skipNodata || !isNoData ) )
167 {
168 pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
169 // GEOS intersects tests on prepared geometry is MAGNITUDES faster than calculating the intersection itself,
170 // so we first test to see if there IS an intersection before doing the actual calculation
171 if ( !pixelRectGeometry.isNull() && polyEngine->intersects( pixelRectGeometry.constGet() ) )
172 {
173 //intersection
174 const QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly );
175 if ( !intersectGeometry.isEmpty() )
176 {
177 const double intersectionArea = intersectGeometry.area();
178 if ( intersectionArea > 0.0 )
179 {
180 weight = intersectionArea / pixelArea;
181 addValue( pixelValue, weight, QgsPointXY( currentX, currentY ) );
182 }
183 }
184 }
185 }
186 currentX += cellSizeX;
187 }
188 currentY -= cellSizeY;
189 }
190 }
191}
192
193bool QgsRasterAnalysisUtils::validPixel( double value )
194{
195 return !std::isnan( value );
196}
197
198void QgsRasterAnalysisUtils::mapToPixel( const double x, const double y, const QgsRectangle bounds, const double unitsPerPixelX, const double unitsPerPixelY, int &px, int &py )
199{
200 px = trunc( ( x - bounds.xMinimum() ) / unitsPerPixelX );
201 py = trunc( ( y - bounds.yMaximum() ) / -unitsPerPixelY );
202}
203
204void QgsRasterAnalysisUtils::pixelToMap( const int px, const int py, const QgsRectangle bounds, const double unitsPerPixelX, const double unitsPerPixelY, double &x, double &y )
205{
206 x = bounds.xMinimum() + ( px + 0.5 ) * unitsPerPixelX;
207 y = bounds.yMaximum() - ( py + 0.5 ) * unitsPerPixelY;
208}
209
210static QVector<QPair<QString, Qgis::DataType>> sDataTypes;
211
212void populateDataTypes()
213{
214 if ( sDataTypes.empty() )
215 {
216 sDataTypes.append( qMakePair( u"Byte"_s, Qgis::DataType::Byte ) );
217 sDataTypes.append( qMakePair( u"Int16"_s, Qgis::DataType::Int16 ) );
218 sDataTypes.append( qMakePair( u"UInt16"_s, Qgis::DataType::UInt16 ) );
219 sDataTypes.append( qMakePair( u"Int32"_s, Qgis::DataType::Int32 ) );
220 sDataTypes.append( qMakePair( u"UInt32"_s, Qgis::DataType::UInt32 ) );
221 sDataTypes.append( qMakePair( u"Float32"_s, Qgis::DataType::Float32 ) );
222 sDataTypes.append( qMakePair( u"Float64"_s, Qgis::DataType::Float64 ) );
223 sDataTypes.append( qMakePair( u"CInt16"_s, Qgis::DataType::CInt16 ) );
224 sDataTypes.append( qMakePair( u"CInt32"_s, Qgis::DataType::CInt32 ) );
225 sDataTypes.append( qMakePair( u"CFloat32"_s, Qgis::DataType::CFloat32 ) );
226 sDataTypes.append( qMakePair( u"CFloat64"_s, Qgis::DataType::CFloat64 ) );
227 sDataTypes.append( qMakePair( u"Int8"_s, Qgis::DataType::Int8 ) );
228 }
229}
230
231std::unique_ptr<QgsProcessingParameterDefinition> QgsRasterAnalysisUtils::createRasterTypeParameter( const QString &name, const QString &description, Qgis::DataType defaultType )
232{
233 populateDataTypes();
234
235 QStringList names;
236 int defaultChoice = 0;
237 int i = 0;
238 for ( auto it = sDataTypes.constBegin(); it != sDataTypes.constEnd(); ++it )
239 {
240 names.append( it->first );
241 if ( it->second == defaultType )
242 defaultChoice = i;
243 i++;
244 }
245
246 return std::make_unique<QgsProcessingParameterEnum>( name, description, names, false, defaultChoice );
247}
248
249Qgis::DataType QgsRasterAnalysisUtils::rasterTypeChoiceToDataType( int choice )
250{
251 if ( choice < 0 || choice >= sDataTypes.count() )
253
254 return sDataTypes.value( choice ).second;
255}
256
257void QgsRasterAnalysisUtils::applyRasterLogicOperator(
258 const std::vector<QgsRasterAnalysisUtils::RasterLogicInput> &inputs,
259 std::unique_ptr<QgsRasterDataProvider> destinationRaster,
260 double outputNoDataValue,
261 const bool treatNoDataAsFalse,
262 int width,
263 int height,
264 const QgsRectangle &extent,
265 QgsFeedback *feedback,
266 std::function<void( const std::vector<std::unique_ptr<QgsRasterBlock>> &, bool &, bool &, int, int, bool )> &applyLogicFunc,
267 qgssize &noDataCount,
268 qgssize &trueCount,
269 qgssize &falseCount
270)
271{
272 destinationRaster->setEditable( true );
273 QgsRasterIterator outputIter( destinationRaster.get() );
274 outputIter.startRasterRead( 1, width, height, extent );
275
276 const bool hasReportsDuringClose = destinationRaster->hasReportsDuringClose();
277 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
278
279 int iterLeft = 0;
280 int iterTop = 0;
281 int iterCols = 0;
282 int iterRows = 0;
283 QgsRectangle blockExtent;
284 std::unique_ptr<QgsRasterBlock> outputBlock;
285 while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
286 {
287 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
288 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : inputs )
289 {
290 for ( const int band : i.bands )
291 {
292 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
293 inputBlocks.emplace_back( std::move( b ) );
294 }
295 }
296
297 feedback->setProgress( maxProgressDuringBlockWriting * outputIter.progress( 1 ) );
298 for ( int row = 0; row < iterRows; row++ )
299 {
300 if ( feedback->isCanceled() )
301 break;
302
303 for ( int column = 0; column < iterCols; column++ )
304 {
305 bool res = false;
306 bool resIsNoData = false;
307 applyLogicFunc( inputBlocks, res, resIsNoData, row, column, treatNoDataAsFalse );
308 if ( resIsNoData )
309 noDataCount++;
310 else if ( res )
311 trueCount++;
312 else
313 falseCount++;
314
315 outputBlock->setValue( row, column, resIsNoData ? outputNoDataValue : ( res ? 1 : 0 ) );
316 }
317 }
318 if ( !destinationRaster->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
319 {
320 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( destinationRaster->error().summary() ) );
321 }
322 }
323 destinationRaster->setEditable( false );
324
325 if ( hasReportsDuringClose )
326 {
327 std::unique_ptr<QgsFeedback> scaledFeedback( QgsFeedback::createScaledFeedback( feedback, maxProgressDuringBlockWriting, 100.0 ) );
328 if ( !destinationRaster->closeWithProgress( scaledFeedback.get() ) )
329 {
330 if ( feedback->isCanceled() )
331 return;
332 throw QgsProcessingException( QObject::tr( "Could not write raster dataset" ) );
333 }
334 }
335}
336
337std::vector<double> QgsRasterAnalysisUtils::getCellValuesFromBlockStack( const std::vector<std::unique_ptr<QgsRasterBlock>> &inputBlocks, int &row, int &col, bool &noDataInStack )
338{
339 //get all values from inputBlocks
340 std::vector<double> cellValues;
341 bool hasNoData = false;
342 cellValues.reserve( inputBlocks.size() );
343
344 for ( auto &block : inputBlocks )
345 {
346 double value = 0;
347 if ( !block || !block->isValid() )
348 {
349 noDataInStack = true;
350 break;
351 }
352 else
353 {
354 value = block->valueAndNoData( row, col, hasNoData );
355 if ( hasNoData )
356 {
357 noDataInStack = true;
358 continue; //NoData is not included in the cell value vector
359 }
360 else
361 {
362 cellValues.push_back( value );
363 }
364 }
365 }
366 return cellValues;
367}
368
369double QgsRasterAnalysisUtils::meanFromCellValues( std::vector<double> &cellValues, int stackSize )
370{
371 const double sum = std::accumulate( cellValues.begin(), cellValues.end(), 0.0 );
372 const double mean = sum / static_cast<double>( stackSize );
373 return mean;
374}
375
376double QgsRasterAnalysisUtils::medianFromCellValues( std::vector<double> &cellValues, int stackSize )
377{
378 std::sort( cellValues.begin(), cellValues.end() );
379 const bool even = ( stackSize % 2 ) < 1;
380 if ( even )
381 {
382 return ( cellValues[stackSize / 2 - 1] + cellValues[stackSize / 2] ) / 2.0;
383 }
384 else //odd
385 {
386 return cellValues[( stackSize + 1 ) / 2 - 1];
387 }
388}
389
390
391double QgsRasterAnalysisUtils::stddevFromCellValues( std::vector<double> &cellValues, int stackSize )
392{
393 const double variance = varianceFromCellValues( cellValues, stackSize );
394 const double stddev = std::sqrt( variance );
395 return stddev;
396}
397
398double QgsRasterAnalysisUtils::varianceFromCellValues( std::vector<double> &cellValues, int stackSize )
399{
400 const double mean = meanFromCellValues( cellValues, stackSize );
401 double accum = 0.0;
402 for ( int i = 0; i < stackSize; i++ )
403 {
404 accum += std::pow( ( cellValues.at( i ) - mean ), 2.0 );
405 }
406 const double variance = accum / static_cast<double>( stackSize );
407 return variance;
408}
409
410double QgsRasterAnalysisUtils::maximumFromCellValues( std::vector<double> &cellValues )
411{
412 return *std::max_element( cellValues.begin(), cellValues.end() );
413}
414
415double QgsRasterAnalysisUtils::minimumFromCellValues( std::vector<double> &cellValues )
416{
417 return *std::min_element( cellValues.begin(), cellValues.end() );
418}
419
420double QgsRasterAnalysisUtils::majorityFromCellValues( std::vector<double> &cellValues, const double noDataValue, int stackSize )
421{
422 if ( stackSize == 1 )
423 {
424 //output will be same as input if only one layer is entered
425 return cellValues[0];
426 }
427 else if ( stackSize == 2 )
428 {
429 //if only two layers are input, return NoData if values are not the same (eg. no Majority could be found)
430 return ( qgsDoubleNear( cellValues[0], cellValues[1] ) ) ? cellValues[0] : noDataValue;
431 }
432 else if ( std::adjacent_find( cellValues.begin(), cellValues.end(), std::not_equal_to<double>() ) == cellValues.end() )
433 {
434 //check if all values in cellValues are equal
435 //output will be same as input if all cellValues of the stack are the same
436 return cellValues[0];
437 }
438 else
439 {
440 //search for majority using hash map [O(n)]
441 std::unordered_map<double, int> map;
442
443 for ( int i = 0; i < stackSize; i++ )
444 {
445 map[cellValues[i]]++;
446 }
447
448 int maxCount = 0;
449 bool multipleMajorities = false;
450 double result = noDataValue;
451 for ( const auto &pair : std::as_const( map ) )
452 {
453 if ( maxCount < pair.second )
454 {
455 result = pair.first;
456 maxCount = pair.second;
457 multipleMajorities = false;
458 }
459 else if ( maxCount == pair.second )
460 {
461 multipleMajorities = true;
462 }
463 }
464 return multipleMajorities ? noDataValue : result;
465 }
466}
467
468double QgsRasterAnalysisUtils::minorityFromCellValues( std::vector<double> &cellValues, const double noDataValue, int stackSize )
469{
470 if ( stackSize == 1 )
471 {
472 //output will be same as input if only one layer is entered
473 return cellValues[0];
474 }
475 else if ( stackSize == 2 )
476 {
477 //if only two layers are input, return NoData if values are not the same (eg. no minority could be found)
478 return ( qgsDoubleNear( cellValues[0], cellValues[1] ) ) ? cellValues[0] : noDataValue;
479 }
480 else if ( std::adjacent_find( cellValues.begin(), cellValues.end(), std::not_equal_to<double>() ) == cellValues.end() )
481 {
482 //check if all values in cellValues are equal
483 //output will be same as input if all cellValues of the stack are the same
484 return cellValues[0];
485 }
486 else
487 {
488 //search for minority using hash map [O(n)]
489 std::unordered_map<double, int> map;
490
491 for ( int i = 0; i < stackSize; i++ )
492 {
493 map[cellValues[i]]++;
494 }
495
496 int minCount = stackSize;
497 bool multipleMinorities = false;
498 double result = noDataValue; //result will stay NoData if no minority value exists
499 for ( const auto &pair : std::as_const( map ) )
500 {
501 if ( minCount > pair.second )
502 {
503 result = pair.first;
504 minCount = pair.second;
505 multipleMinorities = false;
506 }
507 else if ( minCount == pair.second )
508 {
509 multipleMinorities = true;
510 }
511 }
512 return multipleMinorities ? noDataValue : result;
513 }
514}
515
516double QgsRasterAnalysisUtils::rangeFromCellValues( std::vector<double> &cellValues )
517{
518 const double max = *std::max_element( cellValues.begin(), cellValues.end() );
519 const double min = *std::min_element( cellValues.begin(), cellValues.end() );
520 return max - min;
521}
522
523double QgsRasterAnalysisUtils::varietyFromCellValues( std::vector<double> &cellValues )
524{
525 const std::unordered_set<double> uniqueValues( cellValues.begin(), cellValues.end() );
526 return uniqueValues.size();
527}
528
529double QgsRasterAnalysisUtils::nearestRankPercentile( std::vector<double> &cellValues, int stackSize, double percentile )
530{
531 //if percentile equals 0 -> pick the first element of the ordered list
532 std::sort( cellValues.begin(), cellValues.end() );
533
534 int i = 0;
535 if ( percentile > 0 )
536 {
537 i = std::ceil( percentile * static_cast<double>( stackSize ) ) - 1;
538 }
539
540 return cellValues[i];
541}
542
543double QgsRasterAnalysisUtils::interpolatedPercentileInc( std::vector<double> &cellValues, int stackSize, double percentile )
544{
545 std::sort( cellValues.begin(), cellValues.end() );
546
547 if ( qgsDoubleNear( percentile, 1.0 ) )
548 {
549 return cellValues[stackSize - 1];
550 }
551 else if ( qgsDoubleNear( percentile, 0.0 ) )
552 {
553 return cellValues[0];
554 }
555
556 const double x = ( percentile * ( stackSize - 1 ) );
557
558 const int i = static_cast<int>( std::floor( x ) );
559 const double xFraction = std::fmod( x, 1 );
560
561 if ( stackSize == 1 )
562 {
563 return cellValues[0];
564 }
565 else if ( stackSize == 2 )
566 {
567 return cellValues[0] + ( cellValues[1] - cellValues[0] ) * percentile;
568 }
569 else
570 {
571 return cellValues[i] + ( cellValues[i + 1] - cellValues[i] ) * xFraction;
572 }
573}
574
575double QgsRasterAnalysisUtils::interpolatedPercentileExc( std::vector<double> &cellValues, int stackSize, double percentile, double noDataValue )
576{
577 std::sort( cellValues.begin(), cellValues.end() );
578 const double x = ( percentile * ( stackSize + 1 ) );
579
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 );
584
585 if ( stackSize < 2 || ( ( percentile < lowerExcValue || percentile > upperExcValue ) ) )
586 {
587 return noDataValue;
588 }
589 else
590 {
591 return cellValues[i] + ( cellValues[i + 1] - cellValues[i] ) * xFraction;
592 }
593}
594
595double QgsRasterAnalysisUtils::interpolatedPercentRankInc( std::vector<double> &cellValues, int stackSize, double value, double noDataValue )
596{
597 std::sort( cellValues.begin(), cellValues.end() );
598
599 if ( value < cellValues[0] || value > cellValues[stackSize - 1] )
600 {
601 return noDataValue;
602 }
603 else
604 {
605 for ( int i = 0; i < stackSize - 1; i++ )
606 {
607 if ( cellValues[i] <= value && cellValues[i + 1] >= value )
608 {
609 double fraction = 0.0;
610
611 //make sure that next number in the distribution is not the same to prevent NaN fractions
612 if ( !qgsDoubleNear( cellValues[i], cellValues[i + 1] ) )
613 fraction = ( value - cellValues[i] ) / ( cellValues[i + 1] - cellValues[i] );
614
615 return ( fraction + i ) / ( stackSize - 1 );
616 }
617 }
618 return noDataValue;
619 }
620}
621
622double QgsRasterAnalysisUtils::interpolatedPercentRankExc( std::vector<double> &cellValues, int stackSize, double value, double noDataValue )
623{
624 std::sort( cellValues.begin(), cellValues.end() );
625
626 if ( value < cellValues[0] || value > cellValues[stackSize - 1] )
627 {
628 return noDataValue;
629 }
630 else
631 {
632 for ( int i = 0; i < stackSize - 1; i++ )
633 {
634 if ( cellValues[i] <= value && cellValues[i + 1] >= value )
635 {
636 double fraction = 0.0;
637
638 //make sure that next number in the distribution is not the same to prevent NaN fractions
639 if ( !qgsDoubleNear( cellValues[i], cellValues[i + 1] ) )
640 fraction = ( value - cellValues[i] ) / ( cellValues[i + 1] - cellValues[i] );
641
642 return ( ( i + 1 ) + fraction ) / ( stackSize + 1 );
643 }
644 }
645 return noDataValue;
646 }
647}
648
649double QgsRasterAnalysisUtils::neighborCellDistance( int direction, double cellSizeX, double cellSizeY )
650{
651 switch ( direction )
652 {
653 case 0: // north
654 case 4: // south
655 return cellSizeY;
656 case 2: // east
657 case 6: // west
658 return cellSizeX;
659 default: // diagonal
660 return std::hypot( cellSizeX, cellSizeY );
661 }
663}
664
665bool QgsRasterAnalysisUtils::neighborCellCoordinates( int direction, int row, int column, int &neighborRow, int &neighborColumn, int rows, int columns )
666{
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 };
669
670 neighborColumn = column + COL_DIRECTION_OFFSETS[direction];
671 neighborRow = row + ROW_DIRECTION_OFFSETS[direction];
672 return ( neighborColumn >= 0 && neighborColumn < columns && neighborRow >= 0 && neighborRow < rows );
673}
674
675int QgsRasterAnalysisUtils::steepestGradientDirection( const QgsRasterBlock *demBlock, int row, int column, double cellSizeX, double cellSizeY, bool down, bool noEdges )
676{
677 const int rows = demBlock->height();
678 const int columns = demBlock->width();
679
680 bool isCenterNoData = false;
681 const double z = demBlock->valueAndNoData( row, column, isCenterNoData );
682 if ( isCenterNoData )
683 return -3;
684
685 double maxGradient = 0.0;
686 int steepestDir = -1;
687
688 bool isNeighborNodata = false;
689 bool isFlat = true;
690 for ( int dir = 0; dir < 8; ++dir )
691 {
692 int nCol = 0;
693 int nRow = 0;
694 if ( neighborCellCoordinates( dir, row, column, nRow, nCol, rows, columns ) )
695 {
696 const double nZ = demBlock->valueAndNoData( nRow, nCol, isNeighborNodata );
697 if ( !isNeighborNodata )
698 {
699 const double grad = ( z - nZ ) / neighborCellDistance( dir, cellSizeX, cellSizeY );
700 isFlat &= qgsDoubleNear( grad, 0 );
701 if ( ( !down || grad > 0 ) && ( steepestDir == -1 || grad > maxGradient ) )
702 {
703 maxGradient = grad;
704 steepestDir = dir;
705 }
706 }
707 else if ( noEdges )
708 {
709 return -3;
710 }
711 }
712 else if ( noEdges )
713 {
714 return -3;
715 }
716 }
717 return isFlat ? -2 : steepestDir;
718}
719
DataType
Raster data types.
Definition qgis.h:393
@ CInt32
Complex Int32.
Definition qgis.h:404
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ CFloat64
Complex Float64.
Definition qgis.h:406
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:398
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:396
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:397
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:395
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:400
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:402
@ CFloat32
Complex Float32.
Definition qgis.h:405
@ CInt16
Complex Int16.
Definition qgis.h:403
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:399
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
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 &parameters=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...
Represents a 2D point.
Definition qgspointxy.h:62
Custom exception class for processing related exceptions.
Raster data container.
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.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
#define BUILTIN_UNREACHABLE
Definition qgis.h:8365
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...
Definition qgis.h:8310
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7693