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