Skip to content
Permalink
Browse files

Allow QgsRasterIterator to iterate over a raster layer, WITHOUT

actually fetching the raster block data

This allows for efficient iteration over a "reference" layer, where
you require the block extent/origin/pixel size/etc (but not the
reference layer block data itself!), in order to fetch a block from
a DIFFERENT set of rasters (but keeping these pixel-aligned to the
reference raster).
  • Loading branch information
nyalldawson committed Jan 21, 2019
1 parent b391c08 commit d57c182d8b4e93ab671ff9b2905391ce85897fd2
@@ -33,6 +33,28 @@ Start reading of raster band. Raster data can then be retrieved by calling readN
:param nRows: number of rows
:param extent: area to read
:param feedback: optional raster feedback object for cancelation/preview. Added in QGIS 3.0.
%End

bool next( int bandNumber, int &columns /Out/, int &rows /Out/, int &topLeftColumn /Out/, int &topLeftRow /Out/, QgsRectangle &blockExtent /Out/ );
%Docstring
Fetches details of the next part of the raster data. This method does NOT actually fetch the raster
data itself, rather it calculates and iterates over the details of the raster alone.

It's useful for iterating over several layers using a target "reference" layer. E.g. summing
the pixels in n rasters whilst aligning the result to a reference layer which is not being summed.

Note that calling this method also advances the iterator, just like calling readNextRasterPart().

:param bandNumber: band to read
:param rows: number of rows on output device
:param topLeftColumn: top left column
:param topLeftRow: top left row
:param blockExtent: exact extent of returned raster block

:return: - false if the last part was already returned
- columns: number of columns on output device

.. versionadded:: 3.6
%End

bool readNextRasterPart( int bandNumber,
@@ -57,6 +57,11 @@ void QgsRasterIterator::startRasterRead( int bandNumber, int nCols, int nRows, c
mRasterPartInfos.insert( bandNumber, pInfo );
}

bool QgsRasterIterator::next( int bandNumber, int &columns, int &rows, int &topLeftColumn, int &topLeftRow, QgsRectangle &blockExtent )
{
return readNextRasterPartInternal( bandNumber, columns, rows, nullptr, topLeftColumn, topLeftRow, &blockExtent );
}

bool QgsRasterIterator::readNextRasterPart( int bandNumber,
int &nCols, int &nRows,
QgsRasterBlock **block,
@@ -71,9 +76,15 @@ bool QgsRasterIterator::readNextRasterPart( int bandNumber,
}

bool QgsRasterIterator::readNextRasterPart( int bandNumber, int &nCols, int &nRows, std::unique_ptr<QgsRasterBlock> &block, int &topLeftCol, int &topLeftRow, QgsRectangle *blockExtent )
{
return readNextRasterPartInternal( bandNumber, nCols, nRows, &block, topLeftCol, topLeftRow, blockExtent );
}

bool QgsRasterIterator::readNextRasterPartInternal( int bandNumber, int &nCols, int &nRows, std::unique_ptr<QgsRasterBlock> *block, int &topLeftCol, int &topLeftRow, QgsRectangle *blockExtent )
{
QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
block.reset();
if ( block )
block->reset();
//get partinfo
QMap<int, RasterPartInfo>::iterator partIt = mRasterPartInfos.find( bandNumber );
if ( partIt == mRasterPartInfos.end() )
@@ -115,7 +126,8 @@ bool QgsRasterIterator::readNextRasterPart( int bandNumber, int &nCols, int &nRo
if ( blockExtent )
*blockExtent = blockRect;

block.reset( mInput->block( bandNumber, blockRect, nCols, nRows, mFeedback ) );
if ( block )
block->reset( mInput->block( bandNumber, blockRect, nCols, nRows, mFeedback ) );
topLeftCol = pInfo.currentCol;
topLeftRow = pInfo.currentRow;

@@ -49,6 +49,27 @@ class CORE_EXPORT QgsRasterIterator
*/
void startRasterRead( int bandNumber, int nCols, int nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback = nullptr );

/**
* Fetches details of the next part of the raster data. This method does NOT actually fetch the raster
* data itself, rather it calculates and iterates over the details of the raster alone.
*
* It's useful for iterating over several layers using a target "reference" layer. E.g. summing
* the pixels in n rasters whilst aligning the result to a reference layer which is not being summed.
*
* Note that calling this method also advances the iterator, just like calling readNextRasterPart().
*
* \param bandNumber band to read
* \param columns number of columns on output device
* \param rows number of rows on output device
* \param topLeftColumn top left column
* \param topLeftRow top left row
* \param blockExtent exact extent of returned raster block
* \returns false if the last part was already returned
*
* \since QGIS 3.6
*/
bool next( int bandNumber, int &columns SIP_OUT, int &rows SIP_OUT, int &topLeftColumn SIP_OUT, int &topLeftRow SIP_OUT, QgsRectangle &blockExtent SIP_OUT );

/**
* Fetches next part of raster data, caller takes ownership of the block and
* caller should delete the block.
@@ -148,6 +169,7 @@ class CORE_EXPORT QgsRasterIterator

//! Remove part into and release memory
void removePartInfo( int bandNumber );
bool readNextRasterPartInternal( int bandNumber, int &nCols, int &nRows, std::unique_ptr<QgsRasterBlock> *block, int &topLeftCol, int &topLeftRow, QgsRectangle *blockExtent );
};

#endif // QGSRASTERITERATOR_H
@@ -39,6 +39,7 @@ class TestQgsRasterIterator : public QObject
void cleanup() {} // will be called after every testfunction.

void testBasic();
void testNoBlock();

private:

@@ -232,6 +233,126 @@ void TestQgsRasterIterator::testBasic()
QVERIFY( !block.get() );
}

void TestQgsRasterIterator::testNoBlock()
{
// test iterating with no block
QgsRasterDataProvider *provider = mpRasterLayer->dataProvider();
QVERIFY( provider );
QgsRasterIterator it( provider );

QCOMPARE( it.input(), provider );

it.setMaximumTileHeight( 2500 );
QCOMPARE( it.maximumTileHeight(), 2500 );

it.setMaximumTileWidth( 3000 );
QCOMPARE( it.maximumTileWidth(), 3000 );

it.startRasterRead( 1, mpRasterLayer->width(), mpRasterLayer->height(), mpRasterLayer->extent() );

int nCols;
int nRows;
int topLeftCol;
int topLeftRow;
QgsRectangle blockExtent;

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 3000 );
QCOMPARE( nRows, 2500 );
QCOMPARE( topLeftCol, 0 );
QCOMPARE( topLeftRow, 0 );
QCOMPARE( blockExtent.xMinimum(), 497470.0 );
QCOMPARE( blockExtent.xMaximum(), 497770.0 );
QCOMPARE( blockExtent.yMinimum(), 7050880.0 );
QCOMPARE( blockExtent.yMaximum(), 7051130.0 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - nRows * mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 3000 );
QCOMPARE( nRows, 2500 );
QCOMPARE( topLeftCol, 3000 );
QCOMPARE( topLeftRow, 0 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - nRows * mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 1200 );
QCOMPARE( nRows, 2500 );
QCOMPARE( topLeftCol, 6000 );
QCOMPARE( topLeftRow, 0 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - nRows * mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 3000 );
QCOMPARE( nRows, 2500 );
QCOMPARE( topLeftCol, 0 );
QCOMPARE( topLeftRow, 2500 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - ( nRows + topLeftRow )* mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 3000 );
QCOMPARE( nRows, 2500 );
QCOMPARE( topLeftCol, 3000 );
QCOMPARE( topLeftRow, 2500 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - ( nRows + topLeftRow )* mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 1200 );
QCOMPARE( nRows, 2500 );
QCOMPARE( topLeftCol, 6000 );
QCOMPARE( topLeftRow, 2500 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - ( nRows + topLeftRow )* mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 3000 );
QCOMPARE( nRows, 450 );
QCOMPARE( topLeftCol, 0 );
QCOMPARE( topLeftRow, 5000 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - ( nRows + topLeftRow )* mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 3000 );
QCOMPARE( nRows, 450 );
QCOMPARE( topLeftCol, 3000 );
QCOMPARE( topLeftRow, 5000 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - ( nRows + topLeftRow )* mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
QCOMPARE( nCols, 1200 );
QCOMPARE( nRows, 450 );
QCOMPARE( topLeftCol, 6000 );
QCOMPARE( topLeftRow, 5000 );
QCOMPARE( blockExtent.xMinimum(), mpRasterLayer->extent().xMinimum() + topLeftCol * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.yMinimum(), mpRasterLayer->extent().yMaximum() - ( nRows + topLeftRow )* mpRasterLayer->rasterUnitsPerPixelY() );
QCOMPARE( blockExtent.width(), nCols * mpRasterLayer->rasterUnitsPerPixelX() );
QCOMPARE( blockExtent.height(), nRows * mpRasterLayer->rasterUnitsPerPixelY() );

QVERIFY( !it.next( 1, nCols, nRows, topLeftCol, topLeftRow, blockExtent ) );
}


QGSTEST_MAIN( TestQgsRasterIterator )

0 comments on commit d57c182

Please sign in to comment.
You can’t perform that action at this time.