Skip to content

Commit 8914e7d

Browse files
committed
Zonal statistics: avoid access to pixels outside the raster. Fixes ticket #6624
1 parent e899021 commit 8914e7d

File tree

1 file changed

+52
-131
lines changed

1 file changed

+52
-131
lines changed

src/analysis/vector/qgszonalstatistics.cpp

Lines changed: 52 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ int QgsZonalStatistics::calculateStatistics( QProgressDialog* p )
8787
mInputNodataValue = GDALGetRasterNoDataValue( rasterBand, NULL );
8888

8989
//get geometry info about raster layer
90-
int nCellsX = GDALGetRasterXSize( inputDataset );
91-
int nCellsY = GDALGetRasterYSize( inputDataset );
90+
int nCellsXGDAL = GDALGetRasterXSize( inputDataset );
91+
int nCellsYGDAL = GDALGetRasterYSize( inputDataset );
9292
double geoTransform[6];
9393
if ( GDALGetGeoTransform( inputDataset, geoTransform ) != CE_None )
9494
{
@@ -105,7 +105,8 @@ int QgsZonalStatistics::calculateStatistics( QProgressDialog* p )
105105
{
106106
cellsizeY = -cellsizeY;
107107
}
108-
QgsRectangle rasterBBox( geoTransform[0], geoTransform[3] - ( nCellsY * cellsizeY ), geoTransform[0] + ( nCellsX * cellsizeX ), geoTransform[3] );
108+
QgsRectangle rasterBBox( geoTransform[0], geoTransform[3] - ( nCellsYGDAL * cellsizeY ),
109+
geoTransform[0] + ( nCellsXGDAL * cellsizeX ), geoTransform[3] );
109110

110111
//add the new count, sum, mean fields to the provider
111112
QList<QgsField> newFieldList;
@@ -145,7 +146,6 @@ int QgsZonalStatistics::calculateStatistics( QProgressDialog* p )
145146

146147
while ( vectorProvider->nextFeature( f ) )
147148
{
148-
qWarning( "%d", featureCounter );
149149
if ( p )
150150
{
151151
p->setValue( featureCounter );
@@ -163,15 +163,32 @@ int QgsZonalStatistics::calculateStatistics( QProgressDialog* p )
163163
continue;
164164
}
165165

166+
QgsRectangle featureRect = featureGeometry->boundingBox().intersect( &rasterBBox );
167+
if ( featureRect.isEmpty() )
168+
{
169+
++featureCounter;
170+
continue;
171+
}
172+
166173
int offsetX, offsetY, nCellsX, nCellsY;
167-
if ( cellInfoForBBox( rasterBBox, featureGeometry->boundingBox(), cellsizeX, cellsizeY, offsetX, offsetY, nCellsX, nCellsY ) != 0 )
174+
if ( cellInfoForBBox( rasterBBox, featureRect, cellsizeX, cellsizeY, offsetX, offsetY, nCellsX, nCellsY ) != 0 )
168175
{
169176
++featureCounter;
170177
continue;
171178
}
172179

173-
statisticsFromMiddlePointTest_improved( rasterBand, featureGeometry, offsetX, offsetY, nCellsX, nCellsY, cellsizeX, cellsizeY,
174-
rasterBBox, sum, count );
180+
//avoid access to cells outside of the raster (may occur because of rounding)
181+
if (( offsetX + nCellsX ) > nCellsXGDAL )
182+
{
183+
nCellsX = nCellsXGDAL - offsetX;
184+
}
185+
if (( offsetY + nCellsY ) > nCellsYGDAL )
186+
{
187+
nCellsY = nCellsYGDAL - offsetY;
188+
}
189+
190+
statisticsFromMiddlePointTest( rasterBand, featureGeometry, offsetX, offsetY, nCellsX, nCellsY, cellsizeX, cellsizeY,
191+
rasterBBox, sum, count );
175192

176193
if ( count <= 1 )
177194
{
@@ -246,21 +263,44 @@ void QgsZonalStatistics::statisticsFromMiddlePointTest( void* band, QgsGeometry*
246263
int pixelOffsetY, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, double& sum, double& count )
247264
{
248265
double cellCenterX, cellCenterY;
249-
QgsPoint currentCellCenter;
250266

251267
float* scanLine = ( float * ) CPLMalloc( sizeof( float ) * nCellsX );
252268
cellCenterY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2;
253269
count = 0;
254270
sum = 0;
255271

272+
GEOSGeometry* polyGeos = poly->asGeos();
273+
if ( !polyGeos )
274+
{
275+
return;
276+
}
277+
278+
const GEOSPreparedGeometry* polyGeosPrepared = GEOSPrepare( poly->asGeos() );
279+
if ( !polyGeosPrepared )
280+
{
281+
return;
282+
}
283+
284+
GEOSCoordSequence* cellCenterCoords = 0;
285+
GEOSGeometry* currentCellCenter = 0;
286+
256287
for ( int i = 0; i < nCellsY; ++i )
257288
{
258-
GDALRasterIO( band, GF_Read, pixelOffsetX, pixelOffsetY + i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 );
289+
if ( GDALRasterIO( band, GF_Read, pixelOffsetX, pixelOffsetY + i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 )
290+
!= CPLE_None )
291+
{
292+
continue;
293+
}
259294
cellCenterX = rasterBBox.xMinimum() + pixelOffsetX * cellSizeX + cellSizeX / 2;
260295
for ( int j = 0; j < nCellsX; ++j )
261296
{
262-
currentCellCenter = QgsPoint( cellCenterX, cellCenterY );
263-
if ( poly->contains( &currentCellCenter ) )
297+
GEOSGeom_destroy( currentCellCenter );
298+
cellCenterCoords = GEOSCoordSeq_create( 1, 2 );
299+
GEOSCoordSeq_setX( cellCenterCoords, 0, cellCenterX );
300+
GEOSCoordSeq_setY( cellCenterCoords, 0, cellCenterY );
301+
currentCellCenter = GEOSGeom_createPoint( cellCenterCoords );
302+
303+
if ( GEOSPreparedContains( polyGeosPrepared, currentCellCenter ) )
264304
{
265305
if ( scanLine[j] != mInputNodataValue ) //don't consider nodata values
266306
{
@@ -273,6 +313,7 @@ void QgsZonalStatistics::statisticsFromMiddlePointTest( void* band, QgsGeometry*
273313
cellCenterY -= cellSizeY;
274314
}
275315
CPLFree( scanLine );
316+
GEOSPreparedGeom_destroy( polyGeosPrepared );
276317
}
277318

278319
void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, QgsGeometry* poly, int pixelOffsetX,
@@ -319,124 +360,4 @@ void QgsZonalStatistics::statisticsFromPreciseIntersection( void* band, QgsGeome
319360
CPLFree( pixelData );
320361
}
321362

322-
void QgsZonalStatistics::statisticsFromMiddlePointTest_improved( void* band, QgsGeometry* poly, int pixelOffsetX, int pixelOffsetY, int nCellsX, int nCellsY,
323-
double cellSizeX, double cellSizeY, const QgsRectangle& rasterBBox, double& sum, double& count )
324-
{
325-
double cellCenterX, cellCenterY;
326-
QgsPoint currentCellCenter;
327-
328-
float* scanLine = ( float * ) CPLMalloc( sizeof( float ) * nCellsX );
329-
cellCenterY = rasterBBox.yMaximum() - pixelOffsetY * cellSizeY - cellSizeY / 2;
330-
count = 0;
331-
sum = 0;
332-
333-
for ( int i = 0; i < nCellsY; ++i )
334-
{
335-
GDALRasterIO( band, GF_Read, pixelOffsetX, pixelOffsetY + i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 );
336-
cellCenterX = rasterBBox.xMinimum() + pixelOffsetX * cellSizeX + cellSizeX / 2;
337-
338-
//do intersection of scanline with geometry
339-
GEOSCoordSequence* scanLineSequence = GEOSCoordSeq_create( 2, 2 );
340-
GEOSCoordSeq_setX( scanLineSequence, 0, cellCenterX );
341-
GEOSCoordSeq_setY( scanLineSequence, 0, cellCenterY );
342-
GEOSCoordSeq_setX( scanLineSequence, 1, cellCenterX + nCellsX * cellSizeX );
343-
GEOSCoordSeq_setY( scanLineSequence, 1, cellCenterY );
344-
GEOSGeometry* scanLineGeos = GEOSGeom_createLineString( scanLineSequence ); //todo: delete
345-
GEOSGeometry* polyGeos = poly->asGeos();
346-
GEOSGeometry* scanLineIntersection = GEOSIntersection( scanLineGeos, polyGeos );
347-
GEOSGeom_destroy( scanLineGeos );
348-
if ( !scanLineIntersection )
349-
{
350-
cellCenterY -= cellSizeY;
351-
continue;
352-
}
353-
354-
//debug
355-
//char* scanLineIntersectionType = GEOSGeomType( scanLineIntersection );
356-
357-
int numGeoms = GEOSGetNumGeometries( scanLineIntersection );
358-
if ( numGeoms < 1 )
359-
{
360-
GEOSGeom_destroy( scanLineIntersection );
361-
cellCenterY -= cellSizeY;
362-
continue;
363-
}
364-
365-
QList<double> scanLineList;
366-
double currentValue;
367-
GEOSGeometry* currentGeom = 0;
368-
for ( int z = 0; z < numGeoms; ++z )
369-
{
370-
if ( numGeoms == 1 )
371-
{
372-
currentGeom = scanLineIntersection;
373-
}
374-
else
375-
{
376-
currentGeom = GEOSGeom_clone( GEOSGetGeometryN( scanLineIntersection, z ) );
377-
}
378-
const GEOSCoordSequence* scanLineCoordSequence = GEOSGeom_getCoordSeq( currentGeom );
379-
if ( !scanLineCoordSequence )
380-
{
381-
//error
382-
}
383-
unsigned int scanLineIntersectionSize;
384-
GEOSCoordSeq_getSize( scanLineCoordSequence, &scanLineIntersectionSize );
385-
if ( !scanLineCoordSequence || scanLineIntersectionSize < 2 || ( scanLineIntersectionSize & 1 ) )
386-
{
387-
//error
388-
}
389-
for ( unsigned int k = 0; k < scanLineIntersectionSize; ++k )
390-
{
391-
GEOSCoordSeq_getX( scanLineCoordSequence, k, &currentValue );
392-
scanLineList.push_back( currentValue );
393-
}
394-
395-
if ( numGeoms != 1 )
396-
{
397-
GEOSGeom_destroy( currentGeom );
398-
}
399-
}
400-
GEOSGeom_destroy( scanLineIntersection );
401-
qSort( scanLineList );
402-
403-
if ( scanLineList.size() < 1 )
404-
{
405-
cellCenterY -= cellSizeY;
406-
continue;
407-
}
408-
409-
int listPlace = -1;
410-
for ( int j = 0; j < nCellsX; ++j )
411-
{
412-
//currentCellCenter = QgsPoint( cellCenterX, cellCenterY );
413-
414-
//instead of doing a contained test every time, find the place of scanLineList and check if even / odd
415-
if ( listPlace >= scanLineList.size() - 1 )
416-
{
417-
break;
418-
}
419-
if ( cellCenterX >= scanLineList.at( listPlace + 1 ) )
420-
{
421-
++listPlace;
422-
if ( listPlace >= scanLineList.size() )
423-
{
424-
break;
425-
}
426-
}
427-
if ( listPlace >= 0 && listPlace < ( scanLineList.size() - 1 ) && !( listPlace & 1 ) )
428-
{
429-
if ( scanLine[j] != mInputNodataValue ) //don't consider nodata values
430-
{
431-
sum += scanLine[j];
432-
++count;
433-
}
434-
}
435-
cellCenterX += cellSizeX;
436-
}
437-
cellCenterY -= cellSizeY;
438-
}
439-
CPLFree( scanLine );
440-
}
441-
442363

0 commit comments

Comments
 (0)