Skip to content

Commit 7f5ac8c

Browse files
author
mhugent
committed
Added zonal statistics class to analysis lib
git-svn-id: http://svn.osgeo.org/qgis/trunk/qgis@11931 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent b63be1d commit 7f5ac8c

File tree

3 files changed

+308
-1
lines changed

3 files changed

+308
-1
lines changed

src/analysis/CMakeLists.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ SET(QGIS_ANALYSIS_SRCS
2525
raster/qgsaspectfilter.cpp
2626
raster/qgstotalcurvaturefilter.cpp
2727
vector/qgsgeometryanalyzer.cpp
28+
vector/qgszonalstatistics.cpp
2829
)
2930

3031
SET(QGIS_ANALYSIS_MOC_HDRS
@@ -93,7 +94,7 @@ INSTALL(TARGETS qgis_analysis
9394

9495
# Added by Tim to install headers
9596

96-
SET(QGIS_ANALYSIS_HDRS vector/qgsgeometryanalyzer.h
97+
SET(QGIS_ANALYSIS_HDRS vector/qgsgeometryanalyzer.h vector/qgszonalstatistics.h
9798
)
9899

99100
INSTALL(CODE "MESSAGE(\"Installing ANALYSIS headers...\")")
+252
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
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+
#include "qgsgeometry.h"
20+
#include "qgsvectordataprovider.h"
21+
#include "qgsvectorlayer.h"
22+
#include "gdal.h"
23+
#include "cpl_string.h"
24+
#include <QProgressDialog>
25+
26+
QgsZonalStatistics::QgsZonalStatistics(QgsVectorLayer* polygonLayer, const QString& rasterFile, const QString& attributePrefix, int rasterBand): \
27+
mRasterFilePath(rasterFile), mRasterBand(rasterBand), mPolygonLayer(polygonLayer), mAttributePrefix(attributePrefix), mInputNodataValue( -1 )
28+
{
29+
30+
}
31+
32+
QgsZonalStatistics::QgsZonalStatistics(): mRasterBand(0), mPolygonLayer(0)
33+
{
34+
35+
}
36+
37+
QgsZonalStatistics::~QgsZonalStatistics()
38+
{
39+
40+
}
41+
42+
int QgsZonalStatistics::calculateStatistics(QProgressDialog* p)
43+
{
44+
if(!mPolygonLayer || !mPolygonLayer->geometryType() == QGis::Polygon)
45+
{
46+
return 1;
47+
}
48+
49+
QgsVectorDataProvider* vectorProvider = mPolygonLayer->dataProvider();
50+
if(!vectorProvider)
51+
{
52+
return 2;
53+
}
54+
55+
//open the raster layer and the raster band
56+
GDALAllRegister();
57+
GDALDatasetH inputDataset = GDALOpen(mRasterFilePath.toLocal8Bit().data(), GA_ReadOnly );
58+
if ( inputDataset == NULL )
59+
{
60+
return 3;
61+
}
62+
63+
if ( GDALGetRasterCount( inputDataset ) < (mRasterBand - 1) )
64+
{
65+
GDALClose( inputDataset );
66+
return 4;
67+
}
68+
69+
GDALRasterBandH rasterBand = GDALGetRasterBand( inputDataset, mRasterBand );
70+
if ( rasterBand == NULL )
71+
{
72+
GDALClose( inputDataset );
73+
return 5;
74+
}
75+
mInputNodataValue = GDALGetRasterNoDataValue( rasterBand, NULL );
76+
77+
//get geometry info about raster layer
78+
int nCellsX = GDALGetRasterXSize( inputDataset );
79+
int nCellsY = GDALGetRasterYSize( inputDataset );
80+
double geoTransform[6];
81+
if ( GDALGetGeoTransform( inputDataset, geoTransform ) != CE_None )
82+
{
83+
GDALClose( inputDataset );
84+
return 6;
85+
}
86+
double cellsizeX = geoTransform[1];
87+
if(cellsizeX < 0)
88+
{
89+
cellsizeX = -cellsizeX;
90+
}
91+
double cellsizeY = geoTransform[5];
92+
if(cellsizeY < 0)
93+
{
94+
cellsizeY = -cellsizeY;
95+
}
96+
QgsRectangle rasterBBox(geoTransform[0], geoTransform[3] - (nCellsY * cellsizeY), geoTransform[0] + (nCellsX * cellsizeX), geoTransform[3]);
97+
98+
//add the new count, sum, mean fields to the provider
99+
QList<QgsField> newFieldList;
100+
QgsField countField(mAttributePrefix + "count", QVariant::Int);
101+
QgsField sumField(mAttributePrefix + "sum", QVariant::Double);
102+
QgsField meanField(mAttributePrefix + "mean", QVariant::Double);
103+
newFieldList.push_back(countField);
104+
newFieldList.push_back(sumField);
105+
newFieldList.push_back(meanField);
106+
if(!vectorProvider->addAttributes( newFieldList ))
107+
{
108+
return 7;
109+
}
110+
111+
//index of the new fields
112+
int countIndex = vectorProvider->fieldNameIndex(mAttributePrefix + "count");
113+
int sumIndex = vectorProvider->fieldNameIndex(mAttributePrefix + "sum");
114+
int meanIndex = vectorProvider->fieldNameIndex(mAttributePrefix + "mean");
115+
116+
if (countIndex == -1 || sumIndex == -1 || meanIndex == -1)
117+
{
118+
return 8;
119+
}
120+
121+
//progress dialog
122+
long featureCount = vectorProvider->featureCount();
123+
if(p)
124+
{
125+
p->setMaximum(featureCount);
126+
}
127+
128+
129+
//iterate over each polygon
130+
vectorProvider->select(QgsAttributeList(), QgsRectangle(), true, false);
131+
vectorProvider->rewind();
132+
QgsFeature f;
133+
double count = 0;
134+
double sum = 0;
135+
double mean = 0;
136+
float* scanLine;
137+
int featureCounter = 0;
138+
//x- and y- coordinate of current cell
139+
double cellCenterX = 0;
140+
double cellCenterY = 0;
141+
QgsPoint currentCellCenter;
142+
143+
while(vectorProvider->nextFeature(f))
144+
{
145+
if(p)
146+
{
147+
p->setValue(featureCounter);
148+
}
149+
150+
if(p && p->wasCanceled())
151+
{
152+
break;
153+
}
154+
155+
QgsGeometry* featureGeometry = f.geometry();
156+
if(!featureGeometry)
157+
{
158+
++featureCounter;
159+
continue;
160+
}
161+
162+
int offsetX, offsetY, nCellsX, nCellsY;
163+
if(cellInfoForBBox(rasterBBox, featureGeometry->boundingBox(), cellsizeX, cellsizeY, offsetX, offsetY, nCellsX, nCellsY) != 0)
164+
{
165+
++featureCounter;
166+
continue;
167+
}
168+
169+
scanLine = ( float * ) CPLMalloc( sizeof( float ) * nCellsX );
170+
cellCenterY = rasterBBox.yMaximum() - offsetY * cellsizeY - cellsizeY / 2;
171+
count = 0;
172+
sum = 0;
173+
float currentValue;
174+
175+
for(int i = 0; i < nCellsY; ++i)
176+
{
177+
GDALRasterIO( rasterBand, GF_Read, offsetX, offsetY + i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 );
178+
cellCenterX = rasterBBox.xMinimum() + offsetX * cellsizeX + cellsizeX / 2;
179+
for(int j = 0; j < nCellsX; ++j)
180+
{
181+
currentCellCenter = QgsPoint(cellCenterX, cellCenterY);
182+
if(featureGeometry->contains(&currentCellCenter))
183+
{
184+
if(scanLine[j] != mInputNodataValue) //don't consider nodata values
185+
{
186+
sum += scanLine[j];
187+
++count;
188+
}
189+
}
190+
cellCenterX += cellsizeX;
191+
}
192+
cellCenterY -= cellsizeY;
193+
}
194+
195+
if(count == 0)
196+
{
197+
mean = 0;
198+
}
199+
else
200+
{
201+
mean = sum / count;
202+
}
203+
204+
//write the new AEY value to the vector data provider
205+
QgsChangedAttributesMap changeMap;
206+
QgsAttributeMap changeAttributeMap;
207+
changeAttributeMap.insert(countIndex, QVariant(count));
208+
changeAttributeMap.insert(sumIndex, QVariant(sum));
209+
changeAttributeMap.insert(meanIndex, QVariant(mean));
210+
changeMap.insert(f.id(), changeAttributeMap);
211+
vectorProvider->changeAttributeValues(changeMap);
212+
213+
CPLFree(scanLine);
214+
++featureCounter;
215+
}
216+
217+
if(p)
218+
{
219+
p->setValue(featureCount);
220+
}
221+
222+
GDALClose( inputDataset );
223+
return 0;
224+
}
225+
226+
int QgsZonalStatistics::cellInfoForBBox(const QgsRectangle& rasterBBox, const QgsRectangle& featureBBox, double cellSizeX, double cellSizeY, \
227+
int& offsetX, int& offsetY, int& nCellsX, int& nCellsY) const
228+
{
229+
//get intersecting bbox
230+
QgsRectangle intersectBox = rasterBBox.intersect(&featureBBox);
231+
if(intersectBox.isEmpty())
232+
{
233+
nCellsX = 0; nCellsY = 0; offsetX = 0; offsetY = 0;
234+
return 0;
235+
}
236+
237+
//get offset in pixels in x- and y- direction
238+
offsetX = (int)( (intersectBox.xMinimum() - rasterBBox.xMinimum()) / cellSizeX);
239+
offsetY = (int)( (rasterBBox.yMaximum() - intersectBox.yMaximum()) / cellSizeY);
240+
241+
int maxColumn = (int) ( (intersectBox.xMaximum() - rasterBBox.xMinimum()) / cellSizeX) + 1;
242+
int maxRow = (int) ( (rasterBBox.yMaximum() - intersectBox.yMinimum()) / cellSizeY ) + 1;
243+
244+
nCellsX = maxColumn - offsetX;
245+
nCellsY = maxRow - offsetY;
246+
247+
return 0;
248+
}
249+
250+
251+
252+
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/***************************************************************************
2+
qgszonalstatistics.h - 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+
#ifndef QGSZONALSTATISTICS_H
19+
#define QGSZONALSTATISTICS_H
20+
21+
#include "qgsrectangle.h"
22+
#include <QString>
23+
24+
class QgsVectorLayer;
25+
class QProgressDialog;
26+
27+
/**A class that calculates raster statistics (count, sum, mean) for a polygon or multipolygon layer and appends the results as attributes*/
28+
class ANALYSIS_EXPORT QgsZonalStatistics
29+
{
30+
public:
31+
QgsZonalStatistics(QgsVectorLayer* polygonLayer, const QString& rasterFile, const QString& attributePrefix = "", int rasterBand = 1);
32+
~QgsZonalStatistics();
33+
34+
/**Starts the calculation
35+
@return 0 in case of success*/
36+
int calculateStatistics(QProgressDialog* p);
37+
38+
private:
39+
QgsZonalStatistics();
40+
/**Analysis what cells need to be considered to cover the bounding box of a feature
41+
@return 0 in case of success*/
42+
int cellInfoForBBox(const QgsRectangle& rasterBBox, const QgsRectangle& featureBBox, double cellSizeX, double cellSizeY, \
43+
int& offsetX, int& offsetY, int& nCellsX, int& nCellsY) const;
44+
45+
QString mRasterFilePath;
46+
/**Raster band to calculate statistics from (defaults to 1)*/
47+
int mRasterBand;
48+
QgsVectorLayer* mPolygonLayer;
49+
QString mAttributePrefix;
50+
/**The nodata value of the input layer*/
51+
float mInputNodataValue;
52+
};
53+
54+
#endif // QGSZONALSTATISTICS_H

0 commit comments

Comments
 (0)