QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
qgsrasteriterator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasteriterator.cpp
3 ---------------------
4 begin : July 2012
5 copyright : (C) 2012 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include "qgsrasteriterator.h"
16
18#include "qgsrasterinterface.h"
19
20#include <QString>
21
22using namespace Qt::StringLiterals;
23
25 : mInput( input )
26 , mTileOverlapPixels( tileOverlapPixels )
27 , mMaximumTileWidth( DEFAULT_MAXIMUM_TILE_WIDTH )
28 , mMaximumTileHeight( DEFAULT_MAXIMUM_TILE_HEIGHT )
29{
30 for ( QgsRasterInterface *ri = input; ri; ri = ri->input() )
31 {
32 QgsRasterDataProvider *rdp = dynamic_cast<QgsRasterDataProvider *>( ri );
33 if ( rdp )
34 {
35 mMaximumTileWidth = rdp->stepWidth() - 2 * mTileOverlapPixels;
36 mMaximumTileHeight = rdp->stepHeight() - 2 * mTileOverlapPixels;
37 break;
38 }
39 }
40}
41
42QgsRectangle QgsRasterIterator::subRegion( const QgsRectangle &rasterExtent, int rasterWidth, int rasterHeight, const QgsRectangle &subRegion, int &subRegionWidth, int &subRegionHeight, int &subRegionLeft, int &subRegionTop, int resamplingFactor )
43{
44 const double xRes = rasterExtent.width() / rasterWidth;
45 const double yRes = rasterExtent.height() / rasterHeight;
46
47 int top = 0;
48 int bottom = rasterHeight - 1;
49 int left = 0;
50 int right = rasterWidth - 1;
51
52 if ( subRegion.yMaximum() < rasterExtent.yMaximum() )
53 {
54 top = static_cast< int >( std::floor( ( rasterExtent.yMaximum() - subRegion.yMaximum() ) / yRes ) );
55 }
56 if ( subRegion.yMinimum() > rasterExtent.yMinimum() )
57 {
58 bottom = static_cast< int >( std::ceil( ( rasterExtent.yMaximum() - subRegion.yMinimum() ) / yRes ) - 1 );
59 }
60
61 if ( subRegion.xMinimum() > rasterExtent.xMinimum() )
62 {
63 left = static_cast< int >( std::floor( ( subRegion.xMinimum() - rasterExtent.xMinimum() ) / xRes ) );
64 }
65 if ( subRegion.xMaximum() < rasterExtent.xMaximum() )
66 {
67 right = static_cast< int >( std::ceil( ( subRegion.xMaximum() - rasterExtent.xMinimum() ) / xRes ) - 1 );
68 }
69
70 if ( resamplingFactor > 1 )
71 {
72 // Round up the starting boundaries to resampling grid
73 left = ( ( left + resamplingFactor - 1 ) / resamplingFactor ) * resamplingFactor;
74 top = ( ( top + resamplingFactor - 1 ) / resamplingFactor ) * resamplingFactor;
75
76 // Round down the ending boundaries to resampling grid
77 right = ( right / resamplingFactor ) * resamplingFactor - 1;
78 bottom = ( bottom / resamplingFactor ) * resamplingFactor - 1;
79 }
80
81 subRegionWidth = right - left + 1;
82 subRegionHeight = bottom - top + 1;
83
84 subRegionLeft = left;
85 subRegionTop = top;
86
87 return QgsRectangle( rasterExtent.xMinimum() + ( left * xRes ),
88 rasterExtent.yMaximum() - ( ( top + subRegionHeight ) * yRes ),
89 rasterExtent.xMinimum() + ( ( left + subRegionWidth ) * xRes ),
90 rasterExtent.yMaximum() - ( top * yRes ) );
91}
92
93void QgsRasterIterator::startRasterRead( int bandNumber, qgssize nCols, qgssize nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback )
94{
95 if ( !mInput )
96 {
97 return;
98 }
99
100 mExtent = extent;
101 mFeedback = feedback;
102
103 //remove any previous part on that band
104 removePartInfo( bandNumber );
105
106 //split raster into small portions if necessary
107 RasterPartInfo pInfo;
108 pInfo.nCols = nCols;
109 pInfo.nRows = nRows;
110 pInfo.currentCol = 0;
111 pInfo.currentRow = 0;
112 mRasterPartInfos.insert( bandNumber, pInfo );
113
114 mNumberBlocksWidth = static_cast< int >( std::ceil( static_cast< double >( nCols ) / mMaximumTileWidth ) );
115 mNumberBlocksHeight = static_cast< int >( std::ceil( static_cast< double >( nRows ) / mMaximumTileHeight ) );
116}
117
118bool QgsRasterIterator::next( int bandNumber, int &columns, int &rows, int &topLeftColumn, int &topLeftRow, QgsRectangle &blockExtent )
119{
120 int outTileColumns = 0;
121 int outTileRows = 0;
122 int outTileTopLeftColumn = 0;
123 int outTileTopLeftRow = 0;
124 return readNextRasterPartInternal( bandNumber, columns, rows, nullptr, topLeftColumn, topLeftRow, &blockExtent, outTileColumns, outTileRows, outTileTopLeftColumn, outTileTopLeftRow );
125}
126
128 int &nCols, int &nRows,
129 QgsRasterBlock **block,
130 int &topLeftCol, int &topLeftRow )
131{
132 *block = nullptr;
133 std::unique_ptr< QgsRasterBlock > nextBlock;
134 const bool result = readNextRasterPart( bandNumber, nCols, nRows, nextBlock, topLeftCol, topLeftRow );
135 if ( result )
136 *block = nextBlock.release();
137 return result;
138}
139
140bool QgsRasterIterator::readNextRasterPart( int bandNumber, int &nCols, int &nRows, std::unique_ptr<QgsRasterBlock> &block, int &topLeftCol, int &topLeftRow, QgsRectangle *blockExtent, int *tileColumns, int *tileRows, int *tileTopLeftColumn, int *tileTopLeftRow )
141{
142 int outTileColumns = 0;
143 int outTileRows = 0;
144 int outTileTopLeftColumn = 0;
145 int outTileTopLeftRow = 0;
146 const bool res = readNextRasterPartInternal( bandNumber, nCols, nRows, &block, topLeftCol, topLeftRow, blockExtent, outTileColumns, outTileRows, outTileTopLeftColumn, outTileTopLeftRow );
147
148 if ( tileColumns )
149 *tileColumns = outTileColumns;
150 if ( tileRows )
151 *tileRows = outTileRows;
152 if ( tileTopLeftColumn )
153 *tileTopLeftColumn = outTileTopLeftColumn;
154 if ( tileTopLeftRow )
155 *tileTopLeftRow = outTileTopLeftRow;
156
157 return res;
158}
159
160bool QgsRasterIterator::readNextRasterPartInternal( int bandNumber, int &nCols, int &nRows, std::unique_ptr<QgsRasterBlock> *block, int &topLeftCol, int &topLeftRow, QgsRectangle *blockExtent, int &tileColumns, int &tileRows, int &tileTopLeftColumn, int &tileTopLeftRow )
161{
162 QgsDebugMsgLevel( u"Entered"_s, 4 );
163 if ( block )
164 block->reset();
165 //get partinfo
166 const QMap<int, RasterPartInfo>::iterator partIt = mRasterPartInfos.find( bandNumber );
167 if ( partIt == mRasterPartInfos.end() )
168 {
169 return false;
170 }
171
172 RasterPartInfo &pInfo = partIt.value();
173
174 // If we started with zero cols or zero rows, just return (avoids divide by zero below)
175 if ( 0 == pInfo.nCols || 0 == pInfo.nRows )
176 {
177 return false;
178 }
179
180 //remove last data block
181
182 //already at end
183 if ( pInfo.currentCol == pInfo.nCols && pInfo.currentRow == pInfo.nRows )
184 {
185 return false;
186 }
187
188 //read data block
189 tileTopLeftColumn = pInfo.currentCol;
190 tileTopLeftRow = pInfo.currentRow;
191
192 tileColumns = static_cast< int >( std::min( static_cast< qgssize >( mMaximumTileWidth ), pInfo.nCols - tileTopLeftColumn ) );
193 tileRows = static_cast< int >( std::min( static_cast< qgssize >( mMaximumTileHeight ), pInfo.nRows - tileTopLeftRow ) );
194
195 if ( mSnapToPixelFactor > 1 )
196 {
197 // Round down tile dimensions to snap factor
198 tileColumns = ( tileColumns / mSnapToPixelFactor ) * mSnapToPixelFactor;
199 tileRows = ( tileRows / mSnapToPixelFactor ) * mSnapToPixelFactor;
200 }
201
202 pInfo.previousIteratedPixels = pInfo.iteratedPixels;
203 pInfo.currentBlockSize = static_cast< std::size_t>( tileColumns ) * static_cast< std::size_t >( tileRows );
204 pInfo.iteratedPixels += pInfo.currentBlockSize;
205
206 const qgssize tileRight = tileTopLeftColumn + tileColumns;
207 const qgssize tileBottom = tileTopLeftRow + tileRows;
208
209 const qgssize blockLeft = tileTopLeftColumn >= mTileOverlapPixels ? ( tileTopLeftColumn - mTileOverlapPixels ) : 0;
210 const qgssize blockTop = tileTopLeftRow >= mTileOverlapPixels ? ( tileTopLeftRow - mTileOverlapPixels ) : 0;
211 const qgssize blockRight = std::min< qgssize >( tileRight + mTileOverlapPixels, pInfo.nCols );
212 const qgssize blockBottom = std::min< qgssize >( tileBottom + mTileOverlapPixels, pInfo.nRows );
213
214 nCols = blockRight - blockLeft;
215 nRows = blockBottom - blockTop;
216
217 if ( mSnapToPixelFactor > 1 )
218 {
219 // Ensure overlap dimensions are also multiples of snap factor
220 nCols = ( nCols / mSnapToPixelFactor ) * mSnapToPixelFactor;
221 nRows = ( nRows / mSnapToPixelFactor ) * mSnapToPixelFactor;
222 if ( nCols == 0 || nRows == 0 )
223 return false;
224 }
225
226 QgsDebugMsgLevel( u"nCols = %1 nRows = %2"_s.arg( nCols ).arg( nRows ), 4 );
227
228 //get subrectangle
229 const QgsRectangle viewPortExtent = mExtent;
230 const double xmin = viewPortExtent.xMinimum() + blockLeft / static_cast< double >( pInfo.nCols ) * viewPortExtent.width();
231 const double xmax = blockLeft + nCols == pInfo.nCols ? viewPortExtent.xMaximum() : // avoid extra FP math if not necessary
232 viewPortExtent.xMinimum() + ( blockLeft + nCols ) / static_cast< double >( pInfo.nCols ) * viewPortExtent.width();
233 const double ymin = blockTop + nRows == pInfo.nRows ? viewPortExtent.yMinimum() : // avoid extra FP math if not necessary
234 viewPortExtent.yMaximum() - ( blockTop + nRows ) / static_cast< double >( pInfo.nRows ) * viewPortExtent.height();
235 const double ymax = viewPortExtent.yMaximum() - blockTop / static_cast< double >( pInfo.nRows ) * viewPortExtent.height();
236 const QgsRectangle blockRect( xmin, ymin, xmax, ymax );
237
238 if ( blockExtent )
239 *blockExtent = blockRect;
240
241 if ( block )
242 block->reset( mInput->block( bandNumber, blockRect, nCols, nRows, mFeedback ) );
243 topLeftCol = blockLeft;
244 topLeftRow = blockTop;
245
246 pInfo.currentCol = tileRight;
247 if ( pInfo.currentCol == pInfo.nCols && tileBottom == pInfo.nRows ) //end of raster
248 {
249 pInfo.currentRow = pInfo.nRows;
250 }
251 else if ( pInfo.currentCol == pInfo.nCols ) //start new row
252 {
253 pInfo.currentCol = 0;
254 pInfo.currentRow = tileBottom;
255 }
256
257 return true;
258}
259
261{
262 removePartInfo( bandNumber );
263}
264
265double QgsRasterIterator::progress( int bandNumber, double currentBlockProgress ) const
266{
267 if ( currentBlockProgress < 0 )
268 {
269 const auto partIt = mRasterPartInfos.find( bandNumber );
270 if ( partIt == mRasterPartInfos.constEnd() )
271 {
272 return 0;
273 }
274 const std::size_t totalSize = static_cast< std::size_t >( partIt->nCols ) * static_cast< std::size_t >( partIt->nRows );
275 return static_cast< double >( partIt->iteratedPixels ) / static_cast< double >( totalSize );
276 }
277 else
278 {
279 const auto partIt = mRasterPartInfos.find( bandNumber );
280 if ( partIt == mRasterPartInfos.constEnd() )
281 {
282 return 0;
283 }
284
285 const std::size_t totalSize = static_cast< std::size_t >( partIt->nCols ) * static_cast< std::size_t >( partIt->nRows );
286 const double startOfBlockProgress = static_cast< double >( partIt->previousIteratedPixels ) / static_cast< double >( totalSize );
287 const double currentBlockProgressFraction = currentBlockProgress * static_cast< double >( partIt->currentBlockSize ) / static_cast< double >( totalSize );
288
289 return startOfBlockProgress + currentBlockProgressFraction;
290 }
291}
292
293void QgsRasterIterator::removePartInfo( int bandNumber )
294{
295 const auto partIt = mRasterPartInfos.constFind( bandNumber );
296 if ( partIt != mRasterPartInfos.constEnd() )
297 {
298 mRasterPartInfos.remove( bandNumber );
299 }
300}
Feedback object tailored for raster block reading.
Raster data container.
Base class for raster data providers.
virtual int stepHeight() const
Step height for raster iterations.
virtual int stepWidth() const
Step width for raster iterations.
Base class for processing filters like renderers, reprojector, resampler etc.
static QgsRectangle subRegion(const QgsRectangle &rasterExtent, int rasterWidth, int rasterHeight, const QgsRectangle &subRegion, int &subRegionWidth, int &subRegionHeight, int &subRegionLeft, int &subRegionTop, int resamplingFactor=1)
Given an overall raster extent and width and height in pixels, calculates the sub region of the raste...
bool next(int bandNumber, int &columns, int &rows, int &topLeftColumn, int &topLeftRow, QgsRectangle &blockExtent)
Fetches details of the next part of the raster data.
QgsRasterIterator(QgsRasterInterface *input, int tileOverlapPixels=0)
Constructor for QgsRasterIterator, iterating over the specified input raster source.
const QgsRasterInterface * input() const
Returns the input raster interface which is being iterated over.
void stopRasterRead(int bandNumber)
Cancels the raster iteration and resets the iterator.
static const int DEFAULT_MAXIMUM_TILE_WIDTH
Default maximum tile width.
bool readNextRasterPart(int bandNumber, int &nCols, int &nRows, QgsRasterBlock **block, int &topLeftCol, int &topLeftRow)
Fetches next part of raster data, caller takes ownership of the block and caller should delete the bl...
static const int DEFAULT_MAXIMUM_TILE_HEIGHT
Default maximum tile height.
double progress(int bandNumber, double currentBlockProgress=-1) const
Returns the raster iteration progress as a fraction from 0 to 1.0, for the specified bandNumber.
void startRasterRead(int bandNumber, qgssize nCols, qgssize nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Start reading of raster band.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition qgis.h:7423
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63