Skip to content

Commit

Permalink
Allow QgsRasterIterator to iterate over a raster layer, WITHOUT
Browse files Browse the repository at this point in the history
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 d57c182
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 2 deletions.
22 changes: 22 additions & 0 deletions python/core/auto_generated/raster/qgsrasteriterator.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
16 changes: 14 additions & 2 deletions src/core/raster/qgsrasteriterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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() )
Expand Down Expand Up @@ -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;

Expand Down
22 changes: 22 additions & 0 deletions src/core/raster/qgsrasteriterator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
121 changes: 121 additions & 0 deletions tests/src/core/testqgsrasteriterator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class TestQgsRasterIterator : public QObject
void cleanup() {} // will be called after every testfunction.

void testBasic();
void testNoBlock();

private:

Expand Down Expand Up @@ -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 )

Expand Down

0 comments on commit d57c182

Please sign in to comment.