QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
21#include "qgsfeatureiterator.h"
22#include "qgsfeedback.h"
23#include "qgsgeometry.h"
24#include "qgsproject.h"
25#include "qgsrasterlayer.h"
27#include "qgsvectorlayer.h"
28
29#include <QFile>
30
31QgsZonalStatistics::QgsZonalStatistics( QgsVectorLayer *polygonLayer, QgsRasterLayer *rasterLayer, const QString &attributePrefix, int rasterBand, Qgis::ZonalStatistics stats )
32 : QgsZonalStatistics( polygonLayer, rasterLayer ? rasterLayer->dataProvider() : nullptr, rasterLayer ? rasterLayer->crs() : QgsCoordinateReferenceSystem(), rasterLayer ? rasterLayer->rasterUnitsPerPixelX() : 0, rasterLayer ? rasterLayer->rasterUnitsPerPixelY() : 0, attributePrefix, rasterBand, stats )
33{
34}
35
36QgsZonalStatistics::QgsZonalStatistics( QgsVectorLayer *polygonLayer, QgsRasterInterface *rasterInterface, const QgsCoordinateReferenceSystem &rasterCrs, double rasterUnitsPerPixelX, double rasterUnitsPerPixelY, const QString &attributePrefix, int rasterBand, Qgis::ZonalStatistics stats )
37 : mRasterInterface( rasterInterface )
38 , mRasterCrs( rasterCrs )
39 , mCellSizeX( std::fabs( rasterUnitsPerPixelX ) )
40 , mCellSizeY( std::fabs( rasterUnitsPerPixelY ) )
41 , mRasterBand( rasterBand )
42 , mPolygonLayer( polygonLayer )
43 , mAttributePrefix( attributePrefix )
44 , mStatistics( stats )
45{
46}
47
49{
50 if ( !mRasterInterface )
51 {
53 }
54
55 if ( mRasterInterface->bandCount() < mRasterBand )
56 {
58 }
59
60 if ( !mPolygonLayer || mPolygonLayer->geometryType() != Qgis::GeometryType::Polygon )
61 {
63 }
64
65 QgsVectorDataProvider *vectorProvider = mPolygonLayer->dataProvider();
66 if ( !vectorProvider )
67 {
69 }
70
71 QMap<Qgis::ZonalStatistic, int> statFieldIndexes;
72
73 //add the new fields to the provider
74 int oldFieldCount = vectorProvider->fields().count();
75 QList<QgsField> newFieldList;
76 for ( Qgis::ZonalStatistic stat :
77 {
90 } )
91 {
92 if ( mStatistics & stat )
93 {
94 QString fieldName = getUniqueFieldName( mAttributePrefix + QgsZonalStatistics::shortName( stat ), newFieldList );
95 QgsField field( fieldName, QMetaType::Type::Double, QStringLiteral( "double precision" ) );
96 newFieldList.push_back( field );
97 statFieldIndexes.insert( stat, oldFieldCount + newFieldList.count() - 1 );
98 }
99 }
100
101 vectorProvider->addAttributes( newFieldList );
102
103 long featureCount = vectorProvider->featureCount();
104
105 QgsFeatureRequest request;
106 request.setNoAttributes();
107
108 request.setDestinationCrs( mRasterCrs, QgsProject::instance()->transformContext() );
109 QgsFeatureIterator fi = vectorProvider->getFeatures( request );
110 QgsFeature feature;
111
112 int featureCounter = 0;
113
114 QgsChangedAttributesMap changeMap;
115 while ( fi.nextFeature( feature ) )
116 {
117 ++featureCounter;
118 if ( feedback && feedback->isCanceled() )
119 {
120 break;
121 }
122
123 if ( feedback )
124 {
125 feedback->setProgress( 100.0 * static_cast<double>( featureCounter ) / featureCount );
126 }
127
128 QgsGeometry featureGeometry = feature.geometry();
129
130 QMap<Qgis::ZonalStatistic, QVariant> results = calculateStatistics( mRasterInterface, featureGeometry, mCellSizeX, mCellSizeY, mRasterBand, mStatistics );
131
132 if ( results.empty() )
133 continue;
134
135 QgsAttributeMap changeAttributeMap;
136 for ( const auto &result : results.toStdMap() )
137 {
138 changeAttributeMap.insert( statFieldIndexes.value( result.first ), result.second );
139 }
140
141 changeMap.insert( feature.id(), changeAttributeMap );
142 }
143
144 vectorProvider->changeAttributeValues( changeMap );
145 mPolygonLayer->updateFields();
146
147 if ( feedback )
148 {
149 if ( feedback->isCanceled() )
151
152 feedback->setProgress( 100 );
153 }
154
156}
157
158QString QgsZonalStatistics::getUniqueFieldName( const QString &fieldName, const QList<QgsField> &newFields )
159{
160 QgsVectorDataProvider *dp = mPolygonLayer->dataProvider();
161
162 if ( !dp->storageType().contains( QLatin1String( "ESRI Shapefile" ) ) )
163 {
164 return fieldName;
165 }
166
167 QList<QgsField> allFields = dp->fields().toList();
168 allFields.append( newFields );
169 QString shortName = fieldName.mid( 0, 10 );
170
171 bool found = false;
172 for ( const QgsField &field : std::as_const( allFields ) )
173 {
174 if ( shortName == field.name() )
175 {
176 found = true;
177 break;
178 }
179 }
180
181 if ( !found )
182 {
183 return shortName;
184 }
185
186 int n = 1;
187 shortName = QStringLiteral( "%1_%2" ).arg( fieldName.mid( 0, 8 ) ).arg( n );
188 found = true;
189 while ( found )
190 {
191 found = false;
192 for ( const QgsField &field : std::as_const( allFields ) )
193 {
194 if ( shortName == field.name() )
195 {
196 n += 1;
197 if ( n < 9 )
198 {
199 shortName = QStringLiteral( "%1_%2" ).arg( fieldName.mid( 0, 8 ) ).arg( n );
200 }
201 else
202 {
203 shortName = QStringLiteral( "%1_%2" ).arg( fieldName.mid( 0, 7 ) ).arg( n );
204 }
205 found = true;
206 }
207 }
208 }
209 return shortName;
210}
211
213{
214 switch ( statistic )
215 {
217 return QObject::tr( "Count" );
219 return QObject::tr( "Sum" );
221 return QObject::tr( "Mean" );
223 return QObject::tr( "Median" );
225 return QObject::tr( "St dev" );
227 return QObject::tr( "Minimum" );
229 return QObject::tr( "Maximum" );
231 return QObject::tr( "Minimum point" );
233 return QObject::tr( "Maximum point" );
235 return QObject::tr( "Range" );
237 return QObject::tr( "Minority" );
239 return QObject::tr( "Majority" );
241 return QObject::tr( "Variety" );
243 return QObject::tr( "Variance" );
246 return QString();
247 }
248 return QString();
249}
250
252{
253 switch ( statistic )
254 {
256 return QStringLiteral( "count" );
258 return QStringLiteral( "sum" );
260 return QStringLiteral( "mean" );
262 return QStringLiteral( "median" );
264 return QStringLiteral( "stdev" );
266 return QStringLiteral( "min" );
268 return QStringLiteral( "max" );
270 return QStringLiteral( "minpoint" );
272 return QStringLiteral( "maxpoint" );
274 return QStringLiteral( "range" );
276 return QStringLiteral( "minority" );
278 return QStringLiteral( "majority" );
280 return QStringLiteral( "variety" );
282 return QStringLiteral( "variance" );
285 return QString();
286 }
287 return QString();
288}
289
291QMap<int, QVariant> QgsZonalStatistics::calculateStatisticsInt( QgsRasterInterface *rasterInterface, const QgsGeometry &geometry, double cellSizeX, double cellSizeY, int rasterBand, Qgis::ZonalStatistics statistics )
292{
293 const auto result { QgsZonalStatistics::calculateStatistics( rasterInterface, geometry, cellSizeX, cellSizeY, rasterBand, statistics ) };
294 QMap<int, QVariant> pyResult;
295 for ( auto it = result.constBegin(); it != result.constEnd(); ++it )
296 {
297 pyResult.insert( static_cast<int>( it.key() ), it.value() );
298 }
299 return pyResult;
300}
302
303QMap<Qgis::ZonalStatistic, QVariant> QgsZonalStatistics::calculateStatistics( QgsRasterInterface *rasterInterface, const QgsGeometry &geometry, double cellSizeX, double cellSizeY, int rasterBand, Qgis::ZonalStatistics statistics )
304{
305 QMap<Qgis::ZonalStatistic, QVariant> results;
306
307 if ( geometry.isEmpty() )
308 return results;
309
310 QgsRectangle rasterBBox = rasterInterface->extent();
311
312 QgsRectangle featureRect = geometry.boundingBox().intersect( rasterBBox );
313
314 if ( featureRect.isEmpty() )
315 return results;
316
317 bool statsStoreValues = ( statistics & Qgis::ZonalStatistic::Median ) || ( statistics & Qgis::ZonalStatistic::StDev ) || ( statistics & Qgis::ZonalStatistic::Variance );
318 bool statsStoreValueCount = ( statistics & Qgis::ZonalStatistic::Minority ) || ( statistics & Qgis::ZonalStatistic::Majority );
319
320 FeatureStats featureStats( statsStoreValues, statsStoreValueCount );
321
322 int nCellsXProvider = rasterInterface->xSize();
323 int nCellsYProvider = rasterInterface->ySize();
324
325 int nCellsX, nCellsY;
326 QgsRectangle rasterBlockExtent;
327 QgsRasterAnalysisUtils::cellInfoForBBox( rasterBBox, featureRect, cellSizeX, cellSizeY, nCellsX, nCellsY, nCellsXProvider, nCellsYProvider, rasterBlockExtent );
328
329 featureStats.reset();
330 QgsRasterAnalysisUtils::statisticsFromMiddlePointTest( rasterInterface, rasterBand, geometry, nCellsX, nCellsY, cellSizeX, cellSizeY, rasterBlockExtent, [&featureStats]( double value, const QgsPointXY &point ) { featureStats.addValue( value, point ); } );
331
332 if ( featureStats.count <= 1 )
333 {
334 //the cell resolution is probably larger than the polygon area. We switch to precise pixel - polygon intersection in this case
335 featureStats.reset();
336 QgsRasterAnalysisUtils::statisticsFromPreciseIntersection( rasterInterface, rasterBand, geometry, nCellsX, nCellsY, cellSizeX, cellSizeY, rasterBlockExtent, [&featureStats]( double value, double weight, const QgsPointXY &point ) { featureStats.addValue( value, point, weight ); } );
337 }
338
339 // calculate the statistics
340
341 if ( statistics & Qgis::ZonalStatistic::Count )
342 results.insert( Qgis::ZonalStatistic::Count, QVariant( featureStats.count ) );
343 if ( statistics & Qgis::ZonalStatistic::Sum )
344 results.insert( Qgis::ZonalStatistic::Sum, QVariant( featureStats.sum ) );
345 if ( featureStats.count > 0 )
346 {
347 double mean = featureStats.sum / featureStats.count;
348 if ( statistics & Qgis::ZonalStatistic::Mean )
349 results.insert( Qgis::ZonalStatistic::Mean, QVariant( mean ) );
350 if ( statistics & Qgis::ZonalStatistic::Median )
351 {
352 std::sort( featureStats.values.begin(), featureStats.values.end() );
353 int size = featureStats.values.count();
354 bool even = ( size % 2 ) < 1;
355 double medianValue;
356 if ( even )
357 {
358 medianValue = ( featureStats.values.at( size / 2 - 1 ) + featureStats.values.at( size / 2 ) ) / 2;
359 }
360 else //odd
361 {
362 medianValue = featureStats.values.at( ( size + 1 ) / 2 - 1 );
363 }
364 results.insert( Qgis::ZonalStatistic::Median, QVariant( medianValue ) );
365 }
366 if ( statistics & Qgis::ZonalStatistic::StDev || statistics & Qgis::ZonalStatistic::Variance )
367 {
368 double sumSquared = 0;
369 for ( int i = 0; i < featureStats.values.count(); ++i )
370 {
371 double diff = featureStats.values.at( i ) - mean;
372 sumSquared += diff * diff;
373 }
374 double variance = sumSquared / featureStats.values.count();
375 if ( statistics & Qgis::ZonalStatistic::StDev )
376 {
377 double stdev = std::pow( variance, 0.5 );
378 results.insert( Qgis::ZonalStatistic::StDev, QVariant( stdev ) );
379 }
380 if ( statistics & Qgis::ZonalStatistic::Variance )
381 results.insert( Qgis::ZonalStatistic::Variance, QVariant( variance ) );
382 }
383 if ( statistics & Qgis::ZonalStatistic::Min )
384 results.insert( Qgis::ZonalStatistic::Min, QVariant( featureStats.min ) );
385 if ( statistics & Qgis::ZonalStatistic::Max )
386 results.insert( Qgis::ZonalStatistic::Max, QVariant( featureStats.max ) );
387 if ( statistics & Qgis::ZonalStatistic::MinimumPoint )
388 results.insert( Qgis::ZonalStatistic::MinimumPoint, QVariant( featureStats.minPoint ) );
389 if ( statistics & Qgis::ZonalStatistic::MaximumPoint )
390 results.insert( Qgis::ZonalStatistic::MaximumPoint, QVariant( featureStats.maxPoint ) );
391 if ( statistics & Qgis::ZonalStatistic::Range )
392 results.insert( Qgis::ZonalStatistic::Range, QVariant( featureStats.max - featureStats.min ) );
393 if ( statistics & Qgis::ZonalStatistic::Minority || statistics & Qgis::ZonalStatistic::Majority )
394 {
395 QList<int> vals = featureStats.valueCount.values();
396 std::sort( vals.begin(), vals.end() );
397 if ( statistics & Qgis::ZonalStatistic::Minority )
398 {
399 double minorityKey = featureStats.valueCount.key( vals.first() );
400 results.insert( Qgis::ZonalStatistic::Minority, QVariant( minorityKey ) );
401 }
402 if ( statistics & Qgis::ZonalStatistic::Majority )
403 {
404 double majKey = featureStats.valueCount.key( vals.last() );
405 results.insert( Qgis::ZonalStatistic::Majority, QVariant( majKey ) );
406 }
407 }
408 if ( statistics & Qgis::ZonalStatistic::Variety )
409 results.insert( Qgis::ZonalStatistic::Variety, QVariant( featureStats.valueCount.count() ) );
410 }
411
412 return results;
413}
ZonalStatistic
Statistics to be calculated during a zonal statistics operation.
Definition qgis.h:5763
@ StDev
Standard deviation of pixel values.
Definition qgis.h:5768
@ Mean
Mean of pixel values.
Definition qgis.h:5766
@ Median
Median of pixel values.
Definition qgis.h:5767
@ Max
Max of pixel values.
Definition qgis.h:5770
@ Variance
Variance of pixel values.
Definition qgis.h:5775
@ MinimumPoint
Pixel centroid for minimum pixel value.
Definition qgis.h:5776
@ Min
Min of pixel values.
Definition qgis.h:5769
@ Default
Default statistics.
Definition qgis.h:5780
@ Range
Range of pixel values (max - min).
Definition qgis.h:5771
@ Sum
Sum of pixel values.
Definition qgis.h:5765
@ Minority
Minority of pixel values.
Definition qgis.h:5772
@ All
All statistics. For QGIS 3.x this includes ONLY numeric statistics, but for 4.0 this will be extended...
Definition qgis.h:5778
@ Majority
Majority of pixel values.
Definition qgis.h:5773
@ Variety
Variety (count of distinct) pixel values.
Definition qgis.h:5774
@ MaximumPoint
Pixel centroid for maximum pixel value.
Definition qgis.h:5777
@ Count
Pixel count.
Definition qgis.h:5764
@ Polygon
Polygons.
Definition qgis.h:361
ZonalStatisticResult
Zonal statistics result codes.
Definition qgis.h:5798
@ Canceled
Algorithm was canceled.
Definition qgis.h:5805
@ LayerTypeWrong
Layer is not a polygon layer.
Definition qgis.h:5800
@ RasterBandInvalid
The raster band does not exist on the raster layer.
Definition qgis.h:5803
@ RasterInvalid
Raster layer is invalid.
Definition qgis.h:5802
@ LayerInvalid
Layer is invalid.
Definition qgis.h:5801
QFlags< ZonalStatistic > ZonalStatistics
Statistics to be calculated during a zonal statistics operation.
Definition qgis.h:5789
Represents a coordinate reference system (CRS).
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setDestinationCrs(const QgsCoordinateReferenceSystem &crs, const QgsCoordinateTransformContext &context)
Sets the destination crs for feature's geometries.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
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:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:54
int count
Definition qgsfields.h:50
QList< QgsField > toList() const
Utility function to return a list of QgsField instances.
A geometry is the spatial representation of a feature.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
Represents a 2D point.
Definition qgspointxy.h:60
static QgsProject * instance()
Returns the QgsProject singleton instance.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual int xSize() const
Gets raster size.
virtual int ySize() const
virtual QgsRectangle extent() const
Gets the extent of the interface.
Represents a raster layer.
A rectangle specified with double values.
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Base class for vector data providers.
long long featureCount() const override=0
Number of features in the layer.
virtual QString storageType() const
Returns the permanent storage type for this layer as a friendly name.
virtual bool changeAttributeValues(const QgsChangedAttributesMap &attr_map)
Changes attribute values of existing features.
QgsFields fields() const override=0
Returns the fields associated with this data provider.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override=0
Query the provider for features specified in request.
virtual bool addAttributes(const QList< QgsField > &attributes)
Adds new attributes to the provider.
Represents a vector layer which manages a vector based dataset.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
QgsZonalStatistics(QgsVectorLayer *polygonLayer, QgsRasterLayer *rasterLayer, const QString &attributePrefix=QString(), int rasterBand=1, Qgis::ZonalStatistics stats=Qgis::ZonalStatistic::Default)
Convenience constructor for QgsZonalStatistics, using an input raster layer.
Qgis::ZonalStatisticResult calculateStatistics(QgsFeedback *feedback)
Runs the calculation.
static QString displayName(Qgis::ZonalStatistic statistic)
Returns the friendly display name for a statistic.
static QString shortName(Qgis::ZonalStatistic statistic)
Returns a short, friendly display name for a statistic, suitable for use in a field name.
QMap< int, QVariant > QgsAttributeMap
QMap< QgsFeatureId, QgsAttributeMap > QgsChangedAttributesMap