QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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 
16 #include "qgsrasteranalysisutils.h"
17 
18 #include "qgsfeedback.h"
19 #include "qgsrasterblock.h"
20 #include "qgsrasteriterator.h"
21 #include "qgsgeos.h"
23 #include <map>
24 #include <cmath>
26 
27 void QgsRasterAnalysisUtils::cellInfoForBBox( const QgsRectangle &rasterBBox, const QgsRectangle &featureBBox, double cellSizeX, double cellSizeY,
28  int &nCellsX, int &nCellsY, int rasterWidth, int rasterHeight, QgsRectangle &rasterBlockExtent )
29 {
30  //get intersecting bbox
31  QgsRectangle intersectBox = rasterBBox.intersect( featureBBox );
32  if ( intersectBox.isEmpty() )
33  {
34  nCellsX = 0;
35  nCellsY = 0;
36  rasterBlockExtent = QgsRectangle();
37  return;
38  }
39 
40  //get offset in pixels in x- and y- direction
41  int offsetX = static_cast< int >( std::floor( ( intersectBox.xMinimum() - rasterBBox.xMinimum() ) / cellSizeX ) );
42  int offsetY = static_cast< int >( std::floor( ( rasterBBox.yMaximum() - intersectBox.yMaximum() ) / cellSizeY ) );
43 
44  int maxColumn = static_cast< int >( std::floor( ( intersectBox.xMaximum() - rasterBBox.xMinimum() ) / cellSizeX ) ) + 1;
45  int maxRow = static_cast< int >( std::floor( ( rasterBBox.yMaximum() - intersectBox.yMinimum() ) / cellSizeY ) ) + 1;
46 
47  nCellsX = maxColumn - offsetX;
48  nCellsY = maxRow - offsetY;
49 
50  //avoid access to cells outside of the raster (may occur because of rounding)
51  nCellsX = std::min( offsetX + nCellsX, rasterWidth ) - offsetX;
52  nCellsY = std::min( offsetY + nCellsY, rasterHeight ) - offsetY;
53 
54  rasterBlockExtent = QgsRectangle( rasterBBox.xMinimum() + offsetX * cellSizeX,
55  rasterBBox.yMaximum() - offsetY * cellSizeY,
56  rasterBBox.xMinimum() + ( nCellsX + offsetX ) * cellSizeX,
57  rasterBBox.yMaximum() - ( nCellsY + offsetY ) * cellSizeY );
58 }
59 
60 void QgsRasterAnalysisUtils::statisticsFromMiddlePointTest( QgsRasterInterface *rasterInterface, int rasterBand, const QgsGeometry &poly, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, const std::function<void( double )> &addValue, bool skipNodata )
61 {
62  std::unique_ptr< QgsGeometryEngine > polyEngine( QgsGeometry::createGeometryEngine( poly.constGet( ) ) );
63  if ( !polyEngine )
64  {
65  return;
66  }
67  polyEngine->prepareGeometry();
68 
69  QgsRasterIterator iter( rasterInterface );
70  iter.startRasterRead( rasterBand, nCellsX, nCellsY, rasterBBox );
71 
72  std::unique_ptr< QgsRasterBlock > block;
73  int iterLeft = 0;
74  int iterTop = 0;
75  int iterCols = 0;
76  int iterRows = 0;
77  QgsRectangle blockExtent;
78  bool isNoData = false;
79  while ( iter.readNextRasterPart( rasterBand, iterCols, iterRows, block, iterLeft, iterTop, &blockExtent ) )
80  {
81  double cellCenterY = blockExtent.yMaximum() - 0.5 * cellSizeY;
82 
83  for ( int row = 0; row < iterRows; ++row )
84  {
85  double cellCenterX = blockExtent.xMinimum() + 0.5 * cellSizeX;
86  for ( int col = 0; col < iterCols; ++col )
87  {
88  const double pixelValue = block->valueAndNoData( row, col, isNoData );
89  if ( validPixel( pixelValue ) && ( !skipNodata || !isNoData ) )
90  {
91  QgsPoint cellCenter( cellCenterX, cellCenterY );
92  if ( polyEngine->contains( &cellCenter ) )
93  {
94  addValue( pixelValue );
95  }
96  }
97  cellCenterX += cellSizeX;
98  }
99  cellCenterY -= cellSizeY;
100  }
101  }
102 }
103 
104 void QgsRasterAnalysisUtils::statisticsFromPreciseIntersection( QgsRasterInterface *rasterInterface, int rasterBand, const QgsGeometry &poly, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, const std::function<void( double, double )> &addValue, bool skipNodata )
105 {
106  QgsGeometry pixelRectGeometry;
107 
108  double hCellSizeX = cellSizeX / 2.0;
109  double hCellSizeY = cellSizeY / 2.0;
110  double pixelArea = cellSizeX * cellSizeY;
111  double weight = 0;
112 
113  std::unique_ptr< QgsGeometryEngine > polyEngine( QgsGeometry::createGeometryEngine( poly.constGet( ) ) );
114  if ( !polyEngine )
115  {
116  return;
117  }
118  polyEngine->prepareGeometry();
119 
120  QgsRasterIterator iter( rasterInterface );
121  iter.startRasterRead( rasterBand, nCellsX, nCellsY, rasterBBox );
122 
123  std::unique_ptr< QgsRasterBlock > block;
124  int iterLeft = 0;
125  int iterTop = 0;
126  int iterCols = 0;
127  int iterRows = 0;
128  QgsRectangle blockExtent;
129  bool isNoData = false;
130  while ( iter.readNextRasterPart( rasterBand, iterCols, iterRows, block, iterLeft, iterTop, &blockExtent ) )
131  {
132  double currentY = blockExtent.yMaximum() - 0.5 * cellSizeY;
133  for ( int row = 0; row < iterRows; ++row )
134  {
135  double currentX = blockExtent.xMinimum() + 0.5 * cellSizeX;
136  for ( int col = 0; col < iterCols; ++col )
137  {
138  const double pixelValue = block->valueAndNoData( row, col, isNoData );
139  if ( validPixel( pixelValue ) && ( !skipNodata || !isNoData ) )
140  {
141  pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
142  // GEOS intersects tests on prepared geometry is MAGNITUDES faster than calculating the intersection itself,
143  // so we first test to see if there IS an intersection before doing the actual calculation
144  if ( !pixelRectGeometry.isNull() && polyEngine->intersects( pixelRectGeometry.constGet() ) )
145  {
146  //intersection
147  QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly );
148  if ( !intersectGeometry.isEmpty() )
149  {
150  double intersectionArea = intersectGeometry.area();
151  if ( intersectionArea > 0.0 )
152  {
153  weight = intersectionArea / pixelArea;
154  addValue( pixelValue, weight );
155  }
156  }
157  }
158  }
159  currentX += cellSizeX;
160  }
161  currentY -= cellSizeY;
162  }
163  }
164 }
165 
166 bool QgsRasterAnalysisUtils::validPixel( double value )
167 {
168  return !std::isnan( value );
169 }
170 
171 void QgsRasterAnalysisUtils::mapToPixel( const double x, const double y, const QgsRectangle bounds, const double unitsPerPixelX, const double unitsPerPixelY, int &px, int &py )
172 {
173  px = trunc( ( x - bounds.xMinimum() ) / unitsPerPixelX );
174  py = trunc( ( y - bounds.yMaximum() ) / -unitsPerPixelY );
175 }
176 
177 void QgsRasterAnalysisUtils::pixelToMap( const int px, const int py, const QgsRectangle bounds, const double unitsPerPixelX, const double unitsPerPixelY, double &x, double &y )
178 {
179  x = bounds.xMinimum() + ( px + 0.5 ) * unitsPerPixelX;
180  y = bounds.yMaximum() - ( py + 0.5 ) * unitsPerPixelY;
181 }
182 
183 static QVector< QPair< QString, Qgis::DataType > > sDataTypes;
184 
185 void populateDataTypes()
186 {
187  if ( sDataTypes.empty() )
188  {
189  sDataTypes.append( qMakePair( QStringLiteral( "Byte" ), Qgis::Byte ) );
190  sDataTypes.append( qMakePair( QStringLiteral( "Int16" ), Qgis::Int16 ) );
191  sDataTypes.append( qMakePair( QStringLiteral( "UInt16" ), Qgis::UInt16 ) );
192  sDataTypes.append( qMakePair( QStringLiteral( "Int32" ), Qgis::Int32 ) );
193  sDataTypes.append( qMakePair( QStringLiteral( "UInt32" ), Qgis::UInt32 ) );
194  sDataTypes.append( qMakePair( QStringLiteral( "Float32" ), Qgis::Float32 ) );
195  sDataTypes.append( qMakePair( QStringLiteral( "Float64" ), Qgis::Float64 ) );
196  sDataTypes.append( qMakePair( QStringLiteral( "CInt16" ), Qgis::CInt16 ) );
197  sDataTypes.append( qMakePair( QStringLiteral( "CInt32" ), Qgis::CInt32 ) );
198  sDataTypes.append( qMakePair( QStringLiteral( "CFloat32" ), Qgis::CFloat32 ) );
199  sDataTypes.append( qMakePair( QStringLiteral( "CFloat64" ), Qgis::CFloat64 ) );
200  }
201 }
202 
203 std::unique_ptr<QgsProcessingParameterDefinition> QgsRasterAnalysisUtils::createRasterTypeParameter( const QString &name, const QString &description, Qgis::DataType defaultType )
204 {
205  populateDataTypes();
206 
207  QStringList names;
208  int defaultChoice = 0;
209  int i = 0;
210  for ( auto it = sDataTypes.constBegin(); it != sDataTypes.constEnd(); ++it )
211  {
212  names.append( it->first );
213  if ( it->second == defaultType )
214  defaultChoice = i;
215  i++;
216  }
217 
218  return qgis::make_unique< QgsProcessingParameterEnum >( name, description, names, false, defaultChoice );
219 }
220 
221 Qgis::DataType QgsRasterAnalysisUtils::rasterTypeChoiceToDataType( int choice )
222 {
223  if ( choice < 0 || choice >= sDataTypes.count() )
224  return Qgis::Float32;
225 
226  return sDataTypes.value( choice ).second;
227 }
228 
229 void QgsRasterAnalysisUtils::applyRasterLogicOperator( const std::vector< QgsRasterAnalysisUtils::RasterLogicInput > &inputs, QgsRasterDataProvider *destinationRaster, double outputNoDataValue, const bool treatNoDataAsFalse,
230  int width, int height, const QgsRectangle &extent, QgsFeedback *feedback,
231  std::function<void( const std::vector< std::unique_ptr< QgsRasterBlock > > &, bool &, bool &, int, int, bool )> &applyLogicFunc,
232  qgssize &noDataCount, qgssize &trueCount, qgssize &falseCount )
233 {
236  int nbBlocksWidth = static_cast< int>( std::ceil( 1.0 * width / maxWidth ) );
237  int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * height / maxHeight ) );
238  int nbBlocks = nbBlocksWidth * nbBlocksHeight;
239 
240  destinationRaster->setEditable( true );
241  QgsRasterIterator outputIter( destinationRaster );
242  outputIter.startRasterRead( 1, width, height, extent );
243 
244  int iterLeft = 0;
245  int iterTop = 0;
246  int iterCols = 0;
247  int iterRows = 0;
248  QgsRectangle blockExtent;
249  std::unique_ptr< QgsRasterBlock > outputBlock;
250  while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
251  {
252  std::vector< std::unique_ptr< QgsRasterBlock > > inputBlocks;
253  for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : inputs )
254  {
255  for ( int band : i.bands )
256  {
257  std::unique_ptr< QgsRasterBlock > b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
258  inputBlocks.emplace_back( std::move( b ) );
259  }
260  }
261 
262  feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
263  for ( int row = 0; row < iterRows; row++ )
264  {
265  if ( feedback->isCanceled() )
266  break;
267 
268  for ( int column = 0; column < iterCols; column++ )
269  {
270  bool res = false;
271  bool resIsNoData = false;
272  applyLogicFunc( inputBlocks, res, resIsNoData, row, column, treatNoDataAsFalse );
273  if ( resIsNoData )
274  noDataCount++;
275  else if ( res )
276  trueCount++;
277  else
278  falseCount++;
279 
280  outputBlock->setValue( row, column, resIsNoData ? outputNoDataValue : ( res ? 1 : 0 ) );
281  }
282  }
283  destinationRaster->writeBlock( outputBlock.get(), 1, iterLeft, iterTop );
284  }
285  destinationRaster->setEditable( false );
286 }
287 
289 
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:75
Qgis::Float32
@ Float32
Thirty two bit floating point (float)
Definition: qgis.h:122
Qgis::DataType
DataType
Raster data types.
Definition: qgis.h:114
Qgis::UInt32
@ UInt32
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:120
QgsPoint
Point geometry type, with support for z-dimension and m-values.
Definition: qgspoint.h:37
QgsRectangle::xMaximum
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:162
QgsRasterDataProvider::setEditable
virtual bool setEditable(bool enabled)
Turns on/off editing mode of the provider.
Definition: qgsrasterdataprovider.h:448
qgsrasteranalysisutils.h
QgsRectangle::intersect
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:312
QgsRectangle
Definition: qgsrectangle.h:41
Qgis::CFloat32
@ CFloat32
Complex Float32.
Definition: qgis.h:126
QgsGeometry::intersection
QgsGeometry intersection(const QgsGeometry &geometry) const
Returns a geometry representing the points shared by this geometry and other.
Definition: qgsgeometry.cpp:2395
QgsGeometry::fromRect
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
Definition: qgsgeometry.cpp:229
qgsrasteriterator.h
Qgis::CFloat64
@ CFloat64
Complex Float64.
Definition: qgis.h:127
Qgis::CInt16
@ CInt16
Complex Int16.
Definition: qgis.h:124
qgsrasterblock.h
QgsRasterDataProvider::writeBlock
bool writeBlock(QgsRasterBlock *block, int band, int xOffset=0, int yOffset=0)
Writes pixel data from a raster block into the provider data source.
Definition: qgsrasterdataprovider.cpp:339
QgsFeedback
Definition: qgsfeedback.h:43
QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT
static const int DEFAULT_MAXIMUM_TILE_HEIGHT
Default maximum tile height.
Definition: qgsrasteriterator.h:151
QgsGeometry::isEmpty
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
Definition: qgsgeometry.cpp:367
QgsGeometry::isNull
bool isNull
Definition: qgsgeometry.h:125
QgsGeometry::constGet
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Definition: qgsgeometry.cpp:128
QgsRasterIterator
Definition: qgsrasteriterator.h:34
Qgis::UInt16
@ UInt16
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:118
QgsRectangle::yMaximum
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:172
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:66
qgsprocessingparameters.h
QgsGeometry::createGeometryEngine
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
Definition: qgsgeometry.cpp:3659
QgsRasterInterface
Definition: qgsrasterinterface.h:116
QgsGeometry
Definition: qgsgeometry.h:122
QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH
static const int DEFAULT_MAXIMUM_TILE_WIDTH
Default maximum tile width.
Definition: qgsrasteriterator.h:148
Qgis::Int16
@ Int16
Sixteen bit signed integer (qint16)
Definition: qgis.h:119
QgsRectangle::yMinimum
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:177
Qgis::Int32
@ Int32
Thirty two bit signed integer (qint32)
Definition: qgis.h:121
Qgis::CInt32
@ CInt32
Complex Int32.
Definition: qgis.h:125
Qgis::Byte
@ Byte
Eight bit unsigned integer (quint8)
Definition: qgis.h:117
qgsfeedback.h
QgsRectangle::isEmpty
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:437
QgsGeometry::area
double area() const
Returns the planar, 2-dimensional area of the geometry.
Definition: qgsgeometry.cpp:1775
QgsRasterDataProvider
Definition: qgsrasterdataprovider.h:88
QgsRectangle::xMinimum
double xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:167
qgsgeos.h
qgssize
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:723
Qgis::Float64
@ Float64
Sixty four bit floating point (double)
Definition: qgis.h:123