Skip to content

Commit de38bf0

Browse files
committed
Use a raster iterator for zonal stats, to optimise zonal stats
calculation of large zones with lots of corresponding raster pixels
1 parent b637c7f commit de38bf0

File tree

1 file changed

+63
-34
lines changed

1 file changed

+63
-34
lines changed

src/analysis/vector/qgszonalstatistics.cpp

+63-34
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,6 @@ void QgsZonalStatistics::cellInfoForBBox( const QgsRectangle &rasterBBox, const
389389

390390
void QgsZonalStatistics::statisticsFromMiddlePointTest( const QgsGeometry &poly, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, FeatureStats &stats )
391391
{
392-
double cellCenterX, cellCenterY;
393-
394-
cellCenterY = rasterBBox.yMaximum() - cellSizeY / 2;
395392
stats.reset();
396393

397394
std::unique_ptr< QgsGeometryEngine > polyEngine( QgsGeometry::createGeometryEngine( poly.constGet( ) ) );
@@ -401,32 +398,48 @@ void QgsZonalStatistics::statisticsFromMiddlePointTest( const QgsGeometry &poly,
401398
}
402399
polyEngine->prepareGeometry();
403400

404-
std::unique_ptr< QgsRasterBlock > block( mRasterInterface->block( mRasterBand, rasterBBox, nCellsX, nCellsY ) );
405-
for ( int i = 0; i < nCellsY; ++i )
401+
const int maxWidth = 4000;
402+
const int maxHeight = 4000;
403+
404+
QgsRasterIterator iter( mRasterInterface );
405+
iter.setMaximumTileWidth( maxWidth );
406+
iter.setMaximumTileHeight( maxHeight );
407+
iter.startRasterRead( mRasterBand, nCellsX, nCellsY, rasterBBox );
408+
409+
QgsRasterBlock *block = nullptr;
410+
int iterLeft = 0;
411+
int iterTop = 0;
412+
int iterCols = 0;
413+
int iterRows = 0;
414+
while ( iter.readNextRasterPart( mRasterBand, iterCols, iterRows, &block, iterLeft, iterTop ) )
406415
{
407-
cellCenterX = rasterBBox.xMinimum() + cellSizeX / 2;
408-
for ( int j = 0; j < nCellsX; ++j )
416+
double cellCenterY = rasterBBox.yMinimum() + ( iterTop + iterRows - 0.5 ) * cellSizeY;
417+
for ( int row = 0; row < iterRows; ++row )
409418
{
410-
double pixelValue = block->value( i, j );
411-
if ( validPixel( pixelValue ) && !block->isNoData( i, j ) )
419+
double cellCenterX = rasterBBox.xMinimum() + ( iterLeft + 0.5 ) * cellSizeX;
420+
for ( int col = 0; col < iterCols; ++col )
412421
{
413-
QgsPoint cellCenter( cellCenterX, cellCenterY );
414-
if ( polyEngine->contains( &cellCenter ) )
422+
double pixelValue = block->value( row, col );
423+
if ( validPixel( pixelValue ) && !block->isNoData( row, col ) )
415424
{
416-
stats.addValue( pixelValue );
425+
QgsPoint cellCenter( cellCenterX, cellCenterY );
426+
if ( polyEngine->contains( &cellCenter ) )
427+
{
428+
stats.addValue( pixelValue );
429+
}
417430
}
431+
cellCenterX += cellSizeX;
418432
}
419-
cellCenterX += cellSizeX;
433+
cellCenterY -= cellSizeY;
420434
}
421-
cellCenterY -= cellSizeY;
435+
delete block;
422436
}
423437
}
424438

425439
void QgsZonalStatistics::statisticsFromPreciseIntersection( const QgsGeometry &poly, int nCellsX, int nCellsY, double cellSizeX, double cellSizeY, const QgsRectangle &rasterBBox, FeatureStats &stats )
426440
{
427441
stats.reset();
428442

429-
double currentY = rasterBBox.yMaximum() - cellSizeY / 2;
430443
QgsGeometry pixelRectGeometry;
431444

432445
double hCellSizeX = cellSizeX / 2.0;
@@ -441,36 +454,52 @@ void QgsZonalStatistics::statisticsFromPreciseIntersection( const QgsGeometry &p
441454
}
442455
polyEngine->prepareGeometry();
443456

444-
std::unique_ptr< QgsRasterBlock > block( mRasterInterface->block( mRasterBand, rasterBBox, nCellsX, nCellsY ) );
445-
for ( int i = 0; i < nCellsY; ++i )
457+
const int maxWidth = 4000;
458+
const int maxHeight = 4000;
459+
460+
QgsRasterIterator iter( mRasterInterface );
461+
iter.setMaximumTileWidth( maxWidth );
462+
iter.setMaximumTileHeight( maxHeight );
463+
iter.startRasterRead( mRasterBand, nCellsX, nCellsY, rasterBBox );
464+
465+
QgsRasterBlock *block = nullptr;
466+
int iterLeft = 0;
467+
int iterTop = 0;
468+
int iterCols = 0;
469+
int iterRows = 0;
470+
while ( iter.readNextRasterPart( mRasterBand, iterCols, iterRows, &block, iterLeft, iterTop ) )
446471
{
447-
double currentX = rasterBBox.xMinimum() + cellSizeX / 2.0;
448-
for ( int j = 0; j < nCellsX; ++j )
472+
double currentY = rasterBBox.yMinimum() + ( iterTop + iterRows - 0.5 ) * cellSizeY;
473+
for ( int row = 0; row < iterRows; ++row )
449474
{
450-
double pixelValue = block->value( i, j );
451-
if ( validPixel( pixelValue ) && !block->isNoData( i, j ) )
475+
double currentX = rasterBBox.xMinimum() + ( iterLeft + 0.5 ) * cellSizeX;
476+
for ( int col = 0; col < iterCols; ++col )
452477
{
453-
pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
454-
// GEOS intersects tests on prepared geometry is MAGNITUDES faster than calculating the intersection itself,
455-
// so we first test to see if there IS an intersection before doing the actual calculation
456-
if ( !pixelRectGeometry.isNull() && polyEngine->intersects( pixelRectGeometry.constGet() ) )
478+
double pixelValue = block->value( row, col );
479+
if ( validPixel( pixelValue ) && !block->isNoData( row, col ) )
457480
{
458-
//intersection
459-
QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly );
460-
if ( !intersectGeometry.isEmpty() )
481+
pixelRectGeometry = QgsGeometry::fromRect( QgsRectangle( currentX - hCellSizeX, currentY - hCellSizeY, currentX + hCellSizeX, currentY + hCellSizeY ) );
482+
// GEOS intersects tests on prepared geometry is MAGNITUDES faster than calculating the intersection itself,
483+
// so we first test to see if there IS an intersection before doing the actual calculation
484+
if ( !pixelRectGeometry.isNull() && polyEngine->intersects( pixelRectGeometry.constGet() ) )
461485
{
462-
double intersectionArea = intersectGeometry.area();
463-
if ( intersectionArea > 0.0 )
486+
//intersection
487+
QgsGeometry intersectGeometry = pixelRectGeometry.intersection( poly );
488+
if ( !intersectGeometry.isEmpty() )
464489
{
465-
weight = intersectionArea / pixelArea;
466-
stats.addValue( pixelValue, weight );
490+
double intersectionArea = intersectGeometry.area();
491+
if ( intersectionArea > 0.0 )
492+
{
493+
weight = intersectionArea / pixelArea;
494+
stats.addValue( pixelValue, weight );
495+
}
467496
}
468497
}
469498
}
499+
currentX += cellSizeX;
470500
}
471-
currentX += cellSizeX;
501+
currentY -= cellSizeY;
472502
}
473-
currentY -= cellSizeY;
474503
}
475504
}
476505

0 commit comments

Comments
 (0)