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
***************************************************************************/
9
/***************************************************************************
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. *
16
***************************************************************************/
18
#include "qgszonalstatistics.h"
19
#include "qgsgeometry.h"
20
#include "qgsvectordataprovider.h"
21
#include "qgsvectorlayer.h"
23
#include "cpl_string.h"
24
#include <QProgressDialog>
26
QgsZonalStatistics::QgsZonalStatistics( QgsVectorLayer* polygonLayer, const QString& rasterFile, const QString& attributePrefix, int rasterBand ) :
27
mRasterFilePath( rasterFile ), mRasterBand( rasterBand ), mPolygonLayer( polygonLayer ), mAttributePrefix( attributePrefix ), mInputNodataValue( -1 )
32
QgsZonalStatistics::QgsZonalStatistics(): mRasterBand( 0 ), mPolygonLayer( 0 )
37
QgsZonalStatistics::~QgsZonalStatistics()
42
int QgsZonalStatistics::calculateStatistics( QProgressDialog* p )
44
if ( !mPolygonLayer || mPolygonLayer->geometryType() != QGis::Polygon )
49
QgsVectorDataProvider* vectorProvider = mPolygonLayer->dataProvider();
50
if ( !vectorProvider )
55
//open the raster layer and the raster band
57
GDALDatasetH inputDataset = GDALOpen( mRasterFilePath.toLocal8Bit().data(), GA_ReadOnly );
58
if ( inputDataset == NULL )
63
if ( GDALGetRasterCount( inputDataset ) < ( mRasterBand - 1 ) )
65
GDALClose( inputDataset );
69
GDALRasterBandH rasterBand = GDALGetRasterBand( inputDataset, mRasterBand );
70
if ( rasterBand == NULL )
72
GDALClose( inputDataset );
75
mInputNodataValue = GDALGetRasterNoDataValue( rasterBand, NULL );
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 )
83
GDALClose( inputDataset );
86
double cellsizeX = geoTransform[1];
89
cellsizeX = -cellsizeX;
91
double cellsizeY = geoTransform[5];
94
cellsizeY = -cellsizeY;
96
QgsRectangle rasterBBox( geoTransform[0], geoTransform[3] - ( nCellsY * cellsizeY ), geoTransform[0] + ( nCellsX * cellsizeX ), geoTransform[3] );
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 ) )
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" );
116
if ( countIndex == -1 || sumIndex == -1 || meanIndex == -1 )
122
long featureCount = vectorProvider->featureCount();
125
p->setMaximum( featureCount );
129
//iterate over each polygon
130
vectorProvider->select( QgsAttributeList(), QgsRectangle(), true, false );
131
vectorProvider->rewind();
137
int featureCounter = 0;
138
//x- and y- coordinate of current cell
139
double cellCenterX = 0;
140
double cellCenterY = 0;
141
QgsPoint currentCellCenter;
143
while ( vectorProvider->nextFeature( f ) )
147
p->setValue( featureCounter );
150
if ( p && p->wasCanceled() )
155
QgsGeometry* featureGeometry = f.geometry();
156
if ( !featureGeometry )
162
int offsetX, offsetY, nCellsX, nCellsY;
163
if ( cellInfoForBBox( rasterBBox, featureGeometry->boundingBox(), cellsizeX, cellsizeY, offsetX, offsetY, nCellsX, nCellsY ) != 0 )
169
scanLine = ( float * ) CPLMalloc( sizeof( float ) * nCellsX );
170
cellCenterY = rasterBBox.yMaximum() - offsetY * cellsizeY - cellsizeY / 2;
174
for ( int i = 0; i < nCellsY; ++i )
176
GDALRasterIO( rasterBand, GF_Read, offsetX, offsetY + i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 );
177
cellCenterX = rasterBBox.xMinimum() + offsetX * cellsizeX + cellsizeX / 2;
178
for ( int j = 0; j < nCellsX; ++j )
180
currentCellCenter = QgsPoint( cellCenterX, cellCenterY );
181
if ( featureGeometry->contains( ¤tCellCenter ) )
183
if ( scanLine[j] != mInputNodataValue ) //don't consider nodata values
189
cellCenterX += cellsizeX;
191
cellCenterY -= cellsizeY;
203
//write the new AEY value to the vector data provider
204
QgsChangedAttributesMap changeMap;
205
QgsAttributeMap changeAttributeMap;
206
changeAttributeMap.insert( countIndex, QVariant( count ) );
207
changeAttributeMap.insert( sumIndex, QVariant( sum ) );
208
changeAttributeMap.insert( meanIndex, QVariant( mean ) );
209
changeMap.insert( f.id(), changeAttributeMap );
210
vectorProvider->changeAttributeValues( changeMap );
218
p->setValue( featureCount );
221
GDALClose( inputDataset );
225
int QgsZonalStatistics::cellInfoForBBox( const QgsRectangle& rasterBBox, const QgsRectangle& featureBBox, double cellSizeX, double cellSizeY,
226
int& offsetX, int& offsetY, int& nCellsX, int& nCellsY ) const
228
//get intersecting bbox
229
QgsRectangle intersectBox = rasterBBox.intersect( &featureBBox );
230
if ( intersectBox.isEmpty() )
232
nCellsX = 0; nCellsY = 0; offsetX = 0; offsetY = 0;
236
//get offset in pixels in x- and y- direction
237
offsetX = ( int )(( intersectBox.xMinimum() - rasterBBox.xMinimum() ) / cellSizeX );
238
offsetY = ( int )(( rasterBBox.yMaximum() - intersectBox.yMaximum() ) / cellSizeY );
240
int maxColumn = ( int )(( intersectBox.xMaximum() - rasterBBox.xMinimum() ) / cellSizeX ) + 1;
241
int maxRow = ( int )(( rasterBBox.yMaximum() - intersectBox.yMinimum() ) / cellSizeY ) + 1;
243
nCellsX = maxColumn - offsetX;
244
nCellsY = maxRow - offsetY;