|
Quantum GIS API Documentation
master-693a1fe
|
00001 /*************************************************************************** 00002 qgszonalstatistics.cpp - description 00003 ---------------------------- 00004 begin : August 29th, 2009 00005 copyright : (C) 2009 by Marco Hugentobler 00006 email : marco at hugis dot net 00007 ***************************************************************************/ 00008 00009 /*************************************************************************** 00010 * * 00011 * This program is free software; you can redistribute it and/or modify * 00012 * it under the terms of the GNU General Public License as published by * 00013 * the Free Software Foundation; either version 2 of the License, or * 00014 * (at your option) any later version. * 00015 * * 00016 ***************************************************************************/ 00017 00018 #include "qgszonalstatistics.h" 00019 #include "qgsgeometry.h" 00020 #include "qgsvectordataprovider.h" 00021 #include "qgsvectorlayer.h" 00022 #include "gdal.h" 00023 #include "cpl_string.h" 00024 #include <QProgressDialog> 00025 00026 #if defined(GDAL_VERSION_NUM) && GDAL_VERSION_NUM >= 1800 00027 #define TO8(x) (x).toUtf8().constData() 00028 #else 00029 #define TO8(x) (x).toLocal8Bit().constData() 00030 #endif 00031 00032 QgsZonalStatistics::QgsZonalStatistics( QgsVectorLayer* polygonLayer, const QString& rasterFile, const QString& attributePrefix, int rasterBand ) 00033 : mRasterFilePath( rasterFile ) 00034 , mRasterBand( rasterBand ) 00035 , mPolygonLayer( polygonLayer ) 00036 , mAttributePrefix( attributePrefix ) 00037 , mInputNodataValue( -1 ) 00038 { 00039 00040 } 00041 00042 QgsZonalStatistics::QgsZonalStatistics() 00043 : mRasterBand( 0 ) 00044 , mPolygonLayer( 0 ) 00045 { 00046 00047 } 00048 00049 QgsZonalStatistics::~QgsZonalStatistics() 00050 { 00051 00052 } 00053 00054 int QgsZonalStatistics::calculateStatistics( QProgressDialog* p ) 00055 { 00056 if ( !mPolygonLayer || mPolygonLayer->geometryType() != QGis::Polygon ) 00057 { 00058 return 1; 00059 } 00060 00061 QgsVectorDataProvider* vectorProvider = mPolygonLayer->dataProvider(); 00062 if ( !vectorProvider ) 00063 { 00064 return 2; 00065 } 00066 00067 //open the raster layer and the raster band 00068 GDALAllRegister(); 00069 GDALDatasetH inputDataset = GDALOpen( TO8( mRasterFilePath ), GA_ReadOnly ); 00070 if ( inputDataset == NULL ) 00071 { 00072 return 3; 00073 } 00074 00075 if ( GDALGetRasterCount( inputDataset ) < ( mRasterBand - 1 ) ) 00076 { 00077 GDALClose( inputDataset ); 00078 return 4; 00079 } 00080 00081 GDALRasterBandH rasterBand = GDALGetRasterBand( inputDataset, mRasterBand ); 00082 if ( rasterBand == NULL ) 00083 { 00084 GDALClose( inputDataset ); 00085 return 5; 00086 } 00087 mInputNodataValue = GDALGetRasterNoDataValue( rasterBand, NULL ); 00088 00089 //get geometry info about raster layer 00090 int nCellsXGDAL = GDALGetRasterXSize( inputDataset ); 00091 int nCellsYGDAL = GDALGetRasterYSize( inputDataset ); 00092 double geoTransform[6]; 00093 if ( GDALGetGeoTransform( inputDataset, geoTransform ) != CE_None ) 00094 { 00095 GDALClose( inputDataset ); 00096 return 6; 00097 } 00098 double cellsizeX = geoTransform[1]; 00099 if ( cellsizeX < 0 ) 00100 { 00101 cellsizeX = -cellsizeX; 00102 } 00103 double cellsizeY = geoTransform[5]; 00104 if ( cellsizeY < 0 ) 00105 { 00106 cellsizeY = -cellsizeY; 00107 } 00108 QgsRectangle rasterBBox( geoTransform[0], geoTransform[3] - ( nCellsYGDAL * cellsizeY ), 00109 geoTransform[0] + ( nCellsXGDAL * cellsizeX ), geoTransform[3] ); 00110 00111 //add the new count, sum, mean fields to the provider 00112 QList<QgsField> newFieldList; 00113 QgsField countField( mAttributePrefix + "count", QVariant::Double, "double precision" ); 00114 QgsField sumField( mAttributePrefix + "sum", QVariant::Double, "double precision" ); 00115 QgsField meanField( mAttributePrefix + "mean", QVariant::Double, "double precision" ); 00116 newFieldList.push_back( countField ); 00117 newFieldList.push_back( sumField ); 00118 newFieldList.push_back( meanField ); 00119 vectorProvider->addAttributes( newFieldList ); 00120 00121 //index of the new fields 00122 int countIndex = vectorProvider->fieldNameIndex( mAttributePrefix + "count" ); 00123 int sumIndex = vectorProvider->fieldNameIndex( mAttributePrefix + "sum" ); 00124 int meanIndex = vectorProvider->fieldNameIndex( mAttributePrefix + "mean" ); 00125 00126 if ( countIndex == -1 || sumIndex == -1 || meanIndex == -1 ) 00127 { 00128 return 8; 00129 } 00130 00131 //progress dialog 00132 long featureCount = vectorProvider->featureCount(); 00133 if ( p ) 00134 { 00135 p->setMaximum( featureCount ); 00136 } 00137 00138 00139 //iterate over each polygon 00140 QgsFeatureRequest request; 00141 request.setSubsetOfAttributes( QgsAttributeList() ); 00142 QgsFeatureIterator fi = vectorProvider->getFeatures( request ); 00143 QgsFeature f; 00144 double count = 0; 00145 double sum = 0; 00146 double mean = 0; 00147 int featureCounter = 0; 00148 00149 while ( fi.nextFeature( f ) ) 00150 { 00151 if ( p ) 00152 { 00153 p->setValue( featureCounter ); 00154 } 00155 00156 if ( p && p->wasCanceled() ) 00157 { 00158 break; 00159 } 00160 00161 QgsGeometry* featureGeometry = f.geometry(); 00162 if ( !featureGeometry ) 00163 { 00164 ++featureCounter; 00165 continue; 00166 } 00167 00168 QgsRectangle featureRect = featureGeometry->boundingBox().intersect( &rasterBBox ); 00169 if ( featureRect.isEmpty() ) 00170 { 00171 ++featureCounter; 00172 continue; 00173 } 00174 00175 int offsetX, offsetY, nCellsX, nCellsY; 00176 if ( cellInfoForBBox( rasterBBox, featureRect, cellsizeX, cellsizeY, offsetX, offsetY, nCellsX, nCellsY ) != 0 ) 00177 { 00178 ++featureCounter; 00179 continue; 00180 } 00181 00182 //avoid access to cells outside of the raster (may occur because of rounding) 00183 if (( offsetX + nCellsX ) > nCellsXGDAL ) 00184 { 00185 nCellsX = nCellsXGDAL - offsetX; 00186 } 00187 if (( offsetY + nCellsY ) > nCellsYGDAL ) 00188 { 00189 nCellsY = nCellsYGDAL - offsetY; 00190 } 00191 00192 statisticsFromMiddlePointTest( rasterBand, featureGeometry, offsetX, offsetY, nCellsX, nCellsY, cellsizeX, cellsizeY, 00193 rasterBBox, sum, count ); 00194 00195 if ( count <= 1 ) 00196 { 00197 //the cell resolution is probably larger than the polygon area. We switch to precise pixel - polygon intersection in this case 00198 statisticsFromPreciseIntersection( rasterBand, featureGeometry, offsetX, offsetY, nCellsX, nCellsY, cellsizeX, cellsizeY, 00199 rasterBBox, sum, count ); 00200 } 00201 00202 00203 if ( count == 0 ) 00204 { 00205 mean = 0; 00206 } 00207 else 00208 { 00209 mean = sum / count; 00210 } 00211 00212 //write the statistics value to the vector data provider 00213 QgsChangedAttributesMap changeMap; 00214 QgsAttributeMap changeAttributeMap; 00215 changeAttributeMap.insert( countIndex, QVariant( count ) ); 00216 changeAttributeMap.insert( sumIndex, QVariant( sum ) ); 00217 changeAttributeMap.insert( meanIndex, QVariant( mean ) ); 00218 changeMap.insert( f.id(), changeAttributeMap ); 00219 vectorProvider->changeAttributeValues( changeMap ); 00220 00221 ++featureCounter; 00222 } 00223 00224 if ( p ) 00225 { 00226 p->setValue( featureCount ); 00227 } 00228 00229 GDALClose( inputDataset ); 00230 00231 if ( p && p->wasCanceled() ) 00232 { 00233 return 9; 00234 } 00235 00236 return 0; 00237 } 00238 00239 int QgsZonalStatistics::cellInfoForBBox( const QgsRectangle& rasterBBox, const QgsRectangle& featureBBox, double cellSizeX, double cellSizeY, 00240 int& offsetX, int& offsetY, int& nCellsX, int& nCellsY ) const 00241 { 00242 //get intersecting bbox 00243 QgsRectangle intersectBox = rasterBBox.intersect( &featureBBox ); 00244 if ( intersectBox.isEmpty() ) 00245 { 00246 nCellsX = 0; nCellsY = 0; offsetX = 0; offsetY = 0; 00247 return 0; 00248 } 00249 00250 //get offset in pixels in x- and y- direction 00251 offsetX = ( int )(( intersectBox.xMinimum() - rasterBBox.xMinimum() ) / cellSizeX ); 00252 offsetY = ( int )(( rasterBBox.yMaximum() - intersectBox.yMaximum() ) / cellSizeY ); 00253 00254 int maxColumn = ( int )(( intersectBox.xMaximum() - rasterBBox.xMinimum() ) / cellSizeX ) + 1; 00255 int maxRow = ( int )(( rasterBBox.yMaximum() - intersectBox.yMinimum() ) / cellSizeY ) + 1; 00256 00257 nCellsX = maxColumn - offsetX; 00258 nCellsY = maxRow - offsetY; 00259 00260 return 0; 00261 } 00262 00263 void QgsZonalStatistics::statisticsFromMiddlePointTest( void* band, QgsGeometry* poly, int pixelOffsetX, 00264 int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, double& sum, double& count ) 00265 { 00266 double cellCenterX, cellCenterY; 00267 00268 float* scanLine = ( float * ) CPLMalloc( sizeof( float ) * nCellsX ); 00269 cellCenterY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; 00270 count = 0; 00271 sum = 0; 00272 00273 GEOSGeometry* polyGeos = poly->asGeos(); 00274 if ( !polyGeos ) 00275 { 00276 return; 00277 } 00278 00279 const GEOSPreparedGeometry* polyGeosPrepared = GEOSPrepare( poly->asGeos() ); 00280 if ( !polyGeosPrepared ) 00281 { 00282 return; 00283 } 00284 00285 GEOSCoordSequence* cellCenterCoords = 0; 00286 GEOSGeometry* currentCellCenter = 0; 00287 00288 for ( int i = 0; i < nCellsY; ++i ) 00289 { 00290 if ( GDALRasterIO( band, GF_Read, pixelOffsetX, pixelOffsetY + i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 ) 00291 != CPLE_None ) 00292 { 00293 continue; 00294 } 00295 cellCenterX = rasterBBox.xMinimum() + pixelOffsetX * cellSizeX + cellSizeX / 2; 00296 for ( int j = 0; j < nCellsX; ++j ) 00297 { 00298 GEOSGeom_destroy( currentCellCenter ); 00299 cellCenterCoords = GEOSCoordSeq_create( 1, 2 ); 00300 GEOSCoordSeq_setX( cellCenterCoords, 0, cellCenterX ); 00301 GEOSCoordSeq_setY( cellCenterCoords, 0, cellCenterY ); 00302 currentCellCenter = GEOSGeom_createPoint( cellCenterCoords ); 00303 00304 if ( GEOSPreparedContains( polyGeosPrepared, currentCellCenter ) ) 00305 { 00306 if ( scanLine[j] != mInputNodataValue ) //don't consider nodata values 00307 { 00308 sum += scanLine[j]; 00309 ++count; 00310 } 00311 } 00312 cellCenterX += cellSizeX; 00313 } 00314 cellCenterY -= cellSizeY; 00315 } 00316 CPLFree( scanLine ); 00317 GEOSPreparedGeom_destroy( polyGeosPrepared ); 00318 } 00319 00320 void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, QgsGeometry* poly, int pixelOffsetX, 00321 int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, double& sum, double& count ) 00322 { 00323 sum = 0; 00324 count = 0; 00325 double currentY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2; 00326 float* pixelData = ( float * ) CPLMalloc( sizeof( float ) ); 00327 QgsGeometry* pixelRectGeometry = 0; 00328 00329 double hCellSizeX = cellSizeX / 2.0; 00330 double hCellSizeY = cellSizeY / 2.0; 00331 double pixelArea = cellSizeX * cellSizeY; 00332 double weight = 0; 00333 00334 for ( int row = 0; row < nCellsY; ++row ) 00335 { 00336 double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0 + pixelOffsetX * cellSizeX; 00337 for ( int col = 0; col < nCellsX; ++col ) 00338 { 00339 GDALRasterIO( band, GF_Read, pixelOffsetX + col, pixelOffsetY + row, nCellsX, 1, pixelData, 1, 1, GDT_Float32, 0, 0 ); 00340 pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) ); 00341 if ( pixelRectGeometry ) 00342 { 00343 //intersection 00344 QgsGeometry *intersectGeometry = pixelRectGeometry->intersection( poly ); 00345 if ( intersectGeometry ) 00346 { 00347 double intersectionArea = intersectGeometry->area(); 00348 if ( intersectionArea >= 0.0 ) 00349 { 00350 weight = intersectionArea / pixelArea; 00351 count += weight; 00352 sum += *pixelData * weight; 00353 } 00354 delete intersectGeometry; 00355 } 00356 } 00357 currentX += cellSizeX; 00358 } 00359 currentY -= cellSizeY; 00360 } 00361 CPLFree( pixelData ); 00362 } 00363 00364