QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgszonalstatistics.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgszonalstatistics.cpp - description
3  ----------------------------
4  begin : August 29th, 2009
5  copyright : (C) 2009 by Marco Hugentobler
6  email : marco at hugis dot net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgszonalstatistics.h"
19 
20 #include "qgsfeatureiterator.h"
21 #include "qgsfeedback.h"
22 #include "qgsgeometry.h"
23 #include "qgsvectordataprovider.h"
24 #include "qgsvectorlayer.h"
26 #include "qgsrasterdataprovider.h"
27 #include "qgsrasterlayer.h"
28 #include "qgslogger.h"
29 #include "qgsproject.h"
30 
31 #include <QFile>
32 
33 QgsZonalStatistics::QgsZonalStatistics( QgsVectorLayer *polygonLayer, QgsRasterLayer *rasterLayer, const QString &attributePrefix, int rasterBand, QgsZonalStatistics::Statistics stats )
34  : QgsZonalStatistics( polygonLayer,
35  rasterLayer ? rasterLayer->dataProvider() : nullptr,
36  rasterLayer ? rasterLayer->crs() : QgsCoordinateReferenceSystem(),
37  rasterLayer ? rasterLayer->rasterUnitsPerPixelX() : 0,
38  rasterLayer ? rasterLayer->rasterUnitsPerPixelY() : 0,
39  attributePrefix,
40  rasterBand,
41  stats )
42 {
43 }
44 
46  const QgsCoordinateReferenceSystem &rasterCrs, double rasterUnitsPerPixelX, double rasterUnitsPerPixelY, const QString &attributePrefix, int rasterBand, QgsZonalStatistics::Statistics stats )
47  : mRasterInterface( rasterInterface )
48  , mRasterCrs( rasterCrs )
49  , mCellSizeX( std::fabs( rasterUnitsPerPixelX ) )
50  , mCellSizeY( std::fabs( rasterUnitsPerPixelY ) )
51  , mRasterBand( rasterBand )
52  , mPolygonLayer( polygonLayer )
53  , mAttributePrefix( attributePrefix )
54  , mStatistics( stats )
55 {
56 }
57 
59 {
60  if ( !mRasterInterface )
61  {
62  return RasterInvalid;
63  }
64 
65  if ( mRasterInterface->bandCount() < mRasterBand )
66  {
67  return RasterBandInvalid;
68  }
69 
70  if ( !mPolygonLayer || mPolygonLayer->geometryType() != QgsWkbTypes::PolygonGeometry )
71  {
72  return LayerTypeWrong;
73  }
74 
75  QgsVectorDataProvider *vectorProvider = mPolygonLayer->dataProvider();
76  if ( !vectorProvider )
77  {
78  return LayerInvalid;
79  }
80 
81  QMap<QgsZonalStatistics::Statistic, int> statFieldIndexes;
82 
83  //add the new fields to the provider
84  int oldFieldCount = vectorProvider->fields().count();
85  QList<QgsField> newFieldList;
87  {
100  } )
101  {
102  if ( mStatistics & stat )
103  {
104  QString fieldName = getUniqueFieldName( mAttributePrefix + QgsZonalStatistics::shortName( stat ), newFieldList );
105  QgsField field( fieldName, QVariant::Double, QStringLiteral( "double precision" ) );
106  newFieldList.push_back( field );
107  statFieldIndexes.insert( stat, oldFieldCount + newFieldList.count() - 1 );
108  }
109  }
110 
111  vectorProvider->addAttributes( newFieldList );
112 
113  long featureCount = vectorProvider->featureCount();
114 
115  QgsFeatureRequest request;
116  request.setNoAttributes();
117 
118  request.setDestinationCrs( mRasterCrs, QgsProject::instance()->transformContext() );
119  QgsFeatureIterator fi = vectorProvider->getFeatures( request );
120  QgsFeature feature;
121 
122  int featureCounter = 0;
123 
124  QgsChangedAttributesMap changeMap;
125  while ( fi.nextFeature( feature ) )
126  {
127  ++featureCounter;
128  if ( feedback && feedback->isCanceled() )
129  {
130  break;
131  }
132 
133  if ( feedback )
134  {
135  feedback->setProgress( 100.0 * static_cast< double >( featureCounter ) / featureCount );
136  }
137 
138  QgsGeometry featureGeometry = feature.geometry();
139 
140  QMap<QgsZonalStatistics::Statistic, QVariant> results = calculateStatistics( mRasterInterface, featureGeometry, mCellSizeX, mCellSizeY, mRasterBand, mStatistics );
141 
142  if ( results.empty() )
143  continue;
144 
145  QgsAttributeMap changeAttributeMap;
146  for ( const auto &result : results.toStdMap() )
147  {
148  changeAttributeMap.insert( statFieldIndexes.value( result.first ), result.second );
149  }
150 
151  changeMap.insert( feature.id(), changeAttributeMap );
152  }
153 
154  vectorProvider->changeAttributeValues( changeMap );
155  mPolygonLayer->updateFields();
156 
157  if ( feedback )
158  {
159  if ( feedback->isCanceled() )
160  return Canceled;
161 
162  feedback->setProgress( 100 );
163  }
164 
165  return Success;
166 }
167 
168 QString QgsZonalStatistics::getUniqueFieldName( const QString &fieldName, const QList<QgsField> &newFields )
169 {
170  QgsVectorDataProvider *dp = mPolygonLayer->dataProvider();
171 
172  if ( !dp->storageType().contains( QLatin1String( "ESRI Shapefile" ) ) )
173  {
174  return fieldName;
175  }
176 
177  QList<QgsField> allFields = dp->fields().toList();
178  allFields.append( newFields );
179  QString shortName = fieldName.mid( 0, 10 );
180 
181  bool found = false;
182  for ( const QgsField &field : qgis::as_const( allFields ) )
183  {
184  if ( shortName == field.name() )
185  {
186  found = true;
187  break;
188  }
189  }
190 
191  if ( !found )
192  {
193  return shortName;
194  }
195 
196  int n = 1;
197  shortName = QStringLiteral( "%1_%2" ).arg( fieldName.mid( 0, 8 ) ).arg( n );
198  found = true;
199  while ( found )
200  {
201  found = false;
202  for ( const QgsField &field : qgis::as_const( allFields ) )
203  {
204  if ( shortName == field.name() )
205  {
206  n += 1;
207  if ( n < 9 )
208  {
209  shortName = QStringLiteral( "%1_%2" ).arg( fieldName.mid( 0, 8 ) ).arg( n );
210  }
211  else
212  {
213  shortName = QStringLiteral( "%1_%2" ).arg( fieldName.mid( 0, 7 ) ).arg( n );
214  }
215  found = true;
216  }
217  }
218  }
219  return shortName;
220 }
221 
223 {
224  switch ( statistic )
225  {
226  case Count:
227  return QObject::tr( "Count" );
228  case Sum:
229  return QObject::tr( "Sum" );
230  case Mean:
231  return QObject::tr( "Mean" );
232  case Median:
233  return QObject::tr( "Median" );
234  case StDev:
235  return QObject::tr( "St dev" );
236  case Min:
237  return QObject::tr( "Minimum" );
238  case Max:
239  return QObject::tr( "Maximum" );
240  case Range:
241  return QObject::tr( "Range" );
242  case Minority:
243  return QObject::tr( "Minority" );
244  case Majority:
245  return QObject::tr( "Majority" );
246  case Variety:
247  return QObject::tr( "Variety" );
248  case Variance:
249  return QObject::tr( "Variance" );
250  case All:
251  return QString();
252  }
253  return QString();
254 }
255 
257 {
258  switch ( statistic )
259  {
260  case Count:
261  return QStringLiteral( "count" );
262  case Sum:
263  return QStringLiteral( "sum" );
264  case Mean:
265  return QStringLiteral( "mean" );
266  case Median:
267  return QStringLiteral( "median" );
268  case StDev:
269  return QStringLiteral( "stdev" );
270  case Min:
271  return QStringLiteral( "min" );
272  case Max:
273  return QStringLiteral( "max" );
274  case Range:
275  return QStringLiteral( "range" );
276  case Minority:
277  return QStringLiteral( "minority" );
278  case Majority:
279  return QStringLiteral( "majority" );
280  case Variety:
281  return QStringLiteral( "variety" );
282  case Variance:
283  return QStringLiteral( "variance" );
284  case All:
285  return QString();
286  }
287  return QString();
288 }
289 
290 QMap<QgsZonalStatistics::Statistic, QVariant> QgsZonalStatistics::calculateStatistics( QgsRasterInterface *rasterInterface, const QgsGeometry &geometry, double cellSizeX, double cellSizeY, int rasterBand, QgsZonalStatistics::Statistics statistics )
291 {
292  QMap<QgsZonalStatistics::Statistic, QVariant> results;
293 
294  if ( geometry.isEmpty() )
295  return results;
296 
297  QgsRectangle rasterBBox = rasterInterface->extent();
298 
299  QgsRectangle featureRect = geometry.boundingBox().intersect( rasterBBox );
300 
301  if ( featureRect.isEmpty() )
302  return results;
303 
304  bool statsStoreValues = ( statistics & QgsZonalStatistics::Median ) ||
305  ( statistics & QgsZonalStatistics::StDev ) ||
306  ( statistics & QgsZonalStatistics::Variance );
307  bool statsStoreValueCount = ( statistics & QgsZonalStatistics::Minority ) ||
308  ( statistics & QgsZonalStatistics::Majority );
309 
310  FeatureStats featureStats( statsStoreValues, statsStoreValueCount );
311 
312  int nCellsXProvider = rasterInterface->xSize();
313  int nCellsYProvider = rasterInterface->ySize();
314 
315  int nCellsX, nCellsY;
316  QgsRectangle rasterBlockExtent;
317  QgsRasterAnalysisUtils::cellInfoForBBox( rasterBBox, featureRect, cellSizeX, cellSizeY, nCellsX, nCellsY, nCellsXProvider, nCellsYProvider, rasterBlockExtent );
318 
319  featureStats.reset();
320  QgsRasterAnalysisUtils::statisticsFromMiddlePointTest( rasterInterface, rasterBand, geometry, nCellsX, nCellsY, cellSizeX, cellSizeY, rasterBlockExtent, [ &featureStats ]( double value ) { featureStats.addValue( value ); } );
321 
322  if ( featureStats.count <= 1 )
323  {
324  //the cell resolution is probably larger than the polygon area. We switch to precise pixel - polygon intersection in this case
325  featureStats.reset();
326  QgsRasterAnalysisUtils::statisticsFromPreciseIntersection( rasterInterface, rasterBand, geometry, nCellsX, nCellsY, cellSizeX, cellSizeY, rasterBlockExtent, [ &featureStats ]( double value, double weight ) { featureStats.addValue( value, weight ); } );
327  }
328 
329  // calculate the statistics
330  QgsAttributeMap changeAttributeMap;
331  if ( statistics & QgsZonalStatistics::Count )
332  results.insert( QgsZonalStatistics::Count, QVariant( featureStats.count ) );
333  if ( statistics & QgsZonalStatistics::Sum )
334  results.insert( QgsZonalStatistics::Sum, QVariant( featureStats.sum ) );
335  if ( featureStats.count > 0 )
336  {
337  double mean = featureStats.sum / featureStats.count;
338  if ( statistics & QgsZonalStatistics::Mean )
339  results.insert( QgsZonalStatistics::Mean, QVariant( mean ) );
340  if ( statistics & QgsZonalStatistics::Median )
341  {
342  std::sort( featureStats.values.begin(), featureStats.values.end() );
343  int size = featureStats.values.count();
344  bool even = ( size % 2 ) < 1;
345  double medianValue;
346  if ( even )
347  {
348  medianValue = ( featureStats.values.at( size / 2 - 1 ) + featureStats.values.at( size / 2 ) ) / 2;
349  }
350  else //odd
351  {
352  medianValue = featureStats.values.at( ( size + 1 ) / 2 - 1 );
353  }
354  results.insert( QgsZonalStatistics::Median, QVariant( medianValue ) );
355  }
356  if ( statistics & QgsZonalStatistics::StDev || statistics & QgsZonalStatistics::Variance )
357  {
358  double sumSquared = 0;
359  for ( int i = 0; i < featureStats.values.count(); ++i )
360  {
361  double diff = featureStats.values.at( i ) - mean;
362  sumSquared += diff * diff;
363  }
364  double variance = sumSquared / featureStats.values.count();
365  if ( statistics & QgsZonalStatistics::StDev )
366  {
367  double stdev = std::pow( variance, 0.5 );
368  results.insert( QgsZonalStatistics::StDev, QVariant( stdev ) );
369  }
370  if ( statistics & QgsZonalStatistics::Variance )
371  results.insert( QgsZonalStatistics::Variance, QVariant( variance ) );
372  }
373  if ( statistics & QgsZonalStatistics::Min )
374  results.insert( QgsZonalStatistics::Min, QVariant( featureStats.min ) );
375  if ( statistics & QgsZonalStatistics::Max )
376  results.insert( QgsZonalStatistics::Max, QVariant( featureStats.max ) );
377  if ( statistics & QgsZonalStatistics::Range )
378  results.insert( QgsZonalStatistics::Range, QVariant( featureStats.max - featureStats.min ) );
379  if ( statistics & QgsZonalStatistics::Minority || statistics & QgsZonalStatistics::Majority )
380  {
381  QList<int> vals = featureStats.valueCount.values();
382  std::sort( vals.begin(), vals.end() );
383  if ( statistics & QgsZonalStatistics::Minority )
384  {
385  double minorityKey = featureStats.valueCount.key( vals.first() );
386  results.insert( QgsZonalStatistics::Minority, QVariant( minorityKey ) );
387  }
388  if ( statistics & QgsZonalStatistics::Majority )
389  {
390  double majKey = featureStats.valueCount.key( vals.last() );
391  results.insert( QgsZonalStatistics::Majority, QVariant( majKey ) );
392  }
393  }
394  if ( statistics & QgsZonalStatistics::Variety )
395  results.insert( QgsZonalStatistics::Variety, QVariant( featureStats.valueCount.count() ) );
396  }
397 
398  return results;
399 }
QgsZonalStatistics::displayName
static QString displayName(QgsZonalStatistics::Statistic statistic)
Returns the friendly display name for a statistic.
Definition: qgszonalstatistics.cpp:222
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:62
QgsFeature::id
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
qgsrasterlayer.h
QgsZonalStatistics::calculateStatistics
QgsZonalStatistics::Result calculateStatistics(QgsFeedback *feedback)
Runs the calculation.
Definition: qgszonalstatistics.cpp:58
QgsVectorLayer::updateFields
void updateFields()
Will regenerate the fields property of this layer by obtaining all fields from the dataProvider,...
Definition: qgsvectorlayer.cpp:3771
QgsZonalStatistics::RasterInvalid
@ RasterInvalid
Raster layer is invalid.
Definition: qgszonalstatistics.h:75
QgsZonalStatistics::All
@ All
Definition: qgszonalstatistics.h:65
QgsZonalStatistics::Canceled
@ Canceled
Algorithm was canceled.
Definition: qgszonalstatistics.h:78
QgsVectorDataProvider::fields
QgsFields fields() const override=0
Returns the fields associated with this data provider.
QgsZonalStatistics
A class that calculates raster statistics (count, sum, mean) for a polygon or multipolygon layer and ...
Definition: qgszonalstatistics.h:47
crs
const QgsCoordinateReferenceSystem & crs
Definition: qgswfsgetfeature.cpp:51
QgsZonalStatistics::Mean
@ Mean
Mean of pixel values.
Definition: qgszonalstatistics.h:55
QgsZonalStatistics::Majority
@ Majority
Majority of pixel values.
Definition: qgszonalstatistics.h:62
QgsVectorDataProvider::changeAttributeValues
virtual bool changeAttributeValues(const QgsChangedAttributesMap &attr_map)
Changes attribute values of existing features.
Definition: qgsvectordataprovider.cpp:137
qgsfeatureiterator.h
QgsFields::count
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
QgsZonalStatistics::StDev
@ StDev
Standard deviation of pixel values.
Definition: qgszonalstatistics.h:57
QgsFeature::geometry
QgsGeometry geometry
Definition: qgsfeature.h:67
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:468
QgsChangedAttributesMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap
Definition: qgsfeature.h:569
field
const QgsField & field
Definition: qgsfield.h:456
qgsrasteranalysisutils.h
QgsZonalStatistics::Count
@ Count
Pixel count.
Definition: qgszonalstatistics.h:53
QgsZonalStatistics::Min
@ Min
Min of pixel values.
Definition: qgszonalstatistics.h:58
QgsRectangle::intersect
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Definition: qgsrectangle.h:312
QgsField::name
QString name
Definition: qgsfield.h:59
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsWkbTypes::PolygonGeometry
@ PolygonGeometry
Definition: qgswkbtypes.h:144
QgsZonalStatistics::LayerTypeWrong
@ LayerTypeWrong
Layer is not a polygon layer.
Definition: qgszonalstatistics.h:73
QgsFields::toList
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
Definition: qgsfields.cpp:212
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:76
QgsZonalStatistics::Median
@ Median
Median of pixel values.
Definition: qgszonalstatistics.h:56
QgsVectorDataProvider::featureCount
long featureCount() const override=0
Number of features in the layer.
QgsZonalStatistics::LayerInvalid
@ LayerInvalid
Layer is invalid.
Definition: qgszonalstatistics.h:74
QgsRasterInterface::xSize
virtual int xSize() const
Gets raster size.
Definition: qgsrasterinterface.h:241
QgsFeedback
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
QgsZonalStatistics::Max
@ Max
Max of pixel values.
Definition: qgszonalstatistics.h:59
QgsZonalStatistics::shortName
static QString shortName(QgsZonalStatistics::Statistic statistic)
Returns a short, friendly display name for a statistic, suitable for use in a field name.
Definition: qgszonalstatistics.cpp:256
qgsvectordataprovider.h
qgszonalstatistics.h
QgsAttributeMap
QMap< int, QVariant > QgsAttributeMap
Definition: qgsattributes.h:38
QgsVectorDataProvider::storageType
virtual QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
Definition: qgsvectordataprovider.cpp:46
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
QgsZonalStatistics::Sum
@ Sum
Sum of pixel values.
Definition: qgszonalstatistics.h:54
QgsZonalStatistics::Result
Result
Error codes for calculation.
Definition: qgszonalstatistics.h:71
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:71
QgsFeatureRequest::setNoAttributes
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
Definition: qgsfeaturerequest.cpp:192
QgsVectorDataProvider::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override=0
Query the provider for features specified in request.
QgsZonalStatistics::RasterBandInvalid
@ RasterBandInvalid
The raster band does not exist on the raster layer.
Definition: qgszonalstatistics.h:76
QgsCoordinateReferenceSystem
This class represents a coordinate reference system (CRS).
Definition: qgscoordinatereferencesystem.h:206
QgsRasterInterface::ySize
virtual int ySize() const
Definition: qgsrasterinterface.h:242
QgsZonalStatistics::QgsZonalStatistics
QgsZonalStatistics(QgsVectorLayer *polygonLayer, QgsRasterLayer *rasterLayer, const QString &attributePrefix=QString(), int rasterBand=1, QgsZonalStatistics::Statistics stats=QgsZonalStatistics::Statistics(QgsZonalStatistics::Count|QgsZonalStatistics::Sum|QgsZonalStatistics::Mean))
Convenience constructor for QgsZonalStatistics, using an input raster layer.
Definition: qgszonalstatistics.cpp:33
qgsvectorlayer.h
QgsZonalStatistics::Success
@ Success
Success.
Definition: qgszonalstatistics.h:72
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
QgsRasterInterface
Base class for processing filters like renderers, reprojector, resampler etc.
Definition: qgsrasterinterface.h:117
qgsgeometry.h
QgsFeatureIterator::nextFeature
bool nextFeature(QgsFeature &f)
Definition: qgsfeatureiterator.h:374
QgsZonalStatistics::Variance
@ Variance
Variance of pixel values.
Definition: qgszonalstatistics.h:64
QgsGeometry
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:124
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsZonalStatistics::Range
@ Range
Range of pixel values (max - min)
Definition: qgszonalstatistics.h:60
QgsVectorLayer::dataProvider
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
Definition: qgsvectorlayer.cpp:627
QgsVectorDataProvider::addAttributes
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes to the provider.
Definition: qgsvectordataprovider.cpp:119
QgsGeometry::boundingBox
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Definition: qgsgeometry.cpp:996
QgsVectorDataProvider
This is the base class for vector data providers.
Definition: qgsvectordataprovider.h:59
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsFeatureRequest::setDestinationCrs
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
Definition: qgsfeaturerequest.cpp:258
QgsRasterInterface::bandCount
virtual int bandCount() const =0
Gets number of bands.
qgslogger.h
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:265
QgsVectorLayer::geometryType
Q_INVOKABLE QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Definition: qgsvectorlayer.cpp:659
qgsfeedback.h
QgsRectangle::isEmpty
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:437
QgsZonalStatistics::Statistic
Statistic
Enumeration of flags that specify statistics to be calculated.
Definition: qgszonalstatistics.h:52
qgsproject.h
QgsZonalStatistics::Variety
@ Variety
Variety (count of distinct) pixel values.
Definition: qgszonalstatistics.h:63
QgsZonalStatistics::Minority
@ Minority
Minority of pixel values.
Definition: qgszonalstatistics.h:61
QgsRasterInterface::extent
virtual QgsRectangle extent() const
Gets the extent of the interface.
Definition: qgsrasterinterface.h:229
qgsrasterdataprovider.h
QgsField
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:50