QGIS API Documentation 4.0.0-Norrköping (1ddcee3d0e4)
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
43 const QgsRectangle &rasterExtent, int rasterWidth, int rasterHeight, const QgsRectangle &subRegion, int &subRegionWidth, int &subRegionHeight, int &subRegionLeft, int &subRegionTop, int resamplingFactor
44)
45{
46 const double xRes = rasterExtent.width() / rasterWidth;
47 const double yRes = rasterExtent.height() / rasterHeight;
48
49 int top = 0;
50 int bottom = rasterHeight - 1;
51 int left = 0;
52 int right = rasterWidth - 1;
53
54 if ( subRegion.yMaximum() < rasterExtent.yMaximum() )
55 {
56 top = static_cast< int >( std::floor( ( rasterExtent.yMaximum() - subRegion.yMaximum() ) / yRes ) );
57 }
58 if ( subRegion.yMinimum() > rasterExtent.yMinimum() )
59 {
60 bottom = static_cast< int >( std::ceil( ( rasterExtent.yMaximum() - subRegion.yMinimum() ) / yRes ) - 1 );
61 }
62
63 if ( subRegion.xMinimum() > rasterExtent.xMinimum() )
64 {
65 left = static_cast< int >( std::floor( ( subRegion.xMinimum() - rasterExtent.xMinimum() ) / xRes ) );
66 }
67 if ( subRegion.xMaximum() < rasterExtent.xMaximum() )
68 {
69 right = static_cast< int >( std::ceil( ( subRegion.xMaximum() - rasterExtent.xMinimum() ) / xRes ) - 1 );
70 }
71
72 if ( resamplingFactor > 1 )
73 {
74 // Round up the starting boundaries to resampling grid
75 left = ( ( left + resamplingFactor - 1 ) / resamplingFactor ) * resamplingFactor;
76 top = ( ( top + resamplingFactor - 1 ) / resamplingFactor ) * resamplingFactor;
77
78 // Round down the ending boundaries to resampling grid
79 right = ( right / resamplingFactor ) * resamplingFactor - 1;
80 bottom = ( bottom / resamplingFactor ) * resamplingFactor - 1;
81 }
82
83 subRegionWidth = right - left + 1;
84 subRegionHeight = bottom - top + 1;
85
86 subRegionLeft = left;
87 subRegionTop = top;
88
89 return QgsRectangle(
90 rasterExtent.xMinimum() + ( left * xRes ),
91 rasterExtent.yMaximum() - ( ( top + subRegionHeight ) * yRes ),
92 rasterExtent.xMinimum() + ( ( left + subRegionWidth ) * xRes ),
93 rasterExtent.yMaximum() - ( top * yRes )
94 );
95}
96
97void QgsRasterIterator::startRasterRead( int bandNumber, qgssize nCols, qgssize nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback )
98{
99 if ( !mInput )
100 {
101 return;
102 }
103
104 mExtent = extent;
105 mFeedback = feedback;
106
107 //remove any previous part on that band
108 removePartInfo( bandNumber );
109
110 //split raster into small portions if necessary
111 RasterPartInfo pInfo;
112 pInfo.nCols = nCols;
113 pInfo.nRows = nRows;
114 pInfo.currentCol = 0;
115 pInfo.currentRow = 0;
116 mRasterPartInfos.insert( bandNumber, pInfo );
117
118 mNumberBlocksWidth = static_cast< int >( std::ceil( static_cast< double >( nCols ) / mMaximumTileWidth ) );
119 mNumberBlocksHeight = static_cast< int >( std::ceil( static_cast< double >( nRows ) / mMaximumTileHeight ) );
120}
121
122bool QgsRasterIterator::next( int bandNumber, int &columns, int &rows, int &topLeftColumn, int &topLeftRow, QgsRectangle &blockExtent )
123{
124 int outTileColumns = 0;
125 int outTileRows = 0;
126 int outTileTopLeftColumn = 0;
127 int outTileTopLeftRow = 0;
128 return readNextRasterPartInternal( bandNumber, columns, rows, nullptr, topLeftColumn, topLeftRow, &blockExtent, outTileColumns, outTileRows, outTileTopLeftColumn, outTileTopLeftRow );
129}
130
131bool QgsRasterIterator::readNextRasterPart( int bandNumber, int &nCols, int &nRows, QgsRasterBlock **block, int &topLeftCol, int &topLeftRow )
132{
133 *block = nullptr;
134 std::unique_ptr< QgsRasterBlock > nextBlock;
135 const bool result = readNextRasterPart( bandNumber, nCols, nRows, nextBlock, topLeftCol, topLeftRow );
136 if ( result )
137 *block = nextBlock.release();
138 return result;
139}
140
142 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
143)
144{
145 int outTileColumns = 0;
146 int outTileRows = 0;
147 int outTileTopLeftColumn = 0;
148 int outTileTopLeftRow = 0;
149 const bool res = readNextRasterPartInternal( bandNumber, nCols, nRows, &block, topLeftCol, topLeftRow, blockExtent, outTileColumns, outTileRows, outTileTopLeftColumn, outTileTopLeftRow );
150
151 if ( tileColumns )
152 *tileColumns = outTileColumns;
153 if ( tileRows )
154 *tileRows = outTileRows;
155 if ( tileTopLeftColumn )
156 *tileTopLeftColumn = outTileTopLeftColumn;
157 if ( tileTopLeftRow )
158 *tileTopLeftRow = outTileTopLeftRow;
159
160 return res;
161}
162
163bool QgsRasterIterator::readNextRasterPartInternal(
164 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
165)
166{
167 QgsDebugMsgLevel( u"Entered"_s, 4 );
168 if ( block )
169 block->reset();
170 //get partinfo
171 const QMap<int, RasterPartInfo>::iterator partIt = mRasterPartInfos.find( bandNumber );
172 if ( partIt == mRasterPartInfos.end() )
173 {
174 return false;
175 }
176
177 RasterPartInfo &pInfo = partIt.value();
178
179 // If we started with zero cols or zero rows, just return (avoids divide by zero below)
180 if ( 0 == pInfo.nCols || 0 == pInfo.nRows )
181 {
182 return false;
183 }
184
185 //remove last data block
186
187 //already at end
188 if ( pInfo.currentCol == pInfo.nCols && pInfo.currentRow == pInfo.nRows )
189 {
190 return false;
191 }
192
193 //read data block
194 tileTopLeftColumn = pInfo.currentCol;
195 tileTopLeftRow = pInfo.currentRow;
196
197 tileColumns = static_cast< int >( std::min( static_cast< qgssize >( mMaximumTileWidth ), pInfo.nCols - tileTopLeftColumn ) );
198 tileRows = static_cast< int >( std::min( static_cast< qgssize >( mMaximumTileHeight ), pInfo.nRows - tileTopLeftRow ) );
199
200 if ( mSnapToPixelFactor > 1 )
201 {
202 // Round down tile dimensions to snap factor
203 tileColumns = ( tileColumns / mSnapToPixelFactor ) * mSnapToPixelFactor;
204 tileRows = ( tileRows / mSnapToPixelFactor ) * mSnapToPixelFactor;
205 }
206
207 pInfo.previousIteratedPixels = pInfo.iteratedPixels;
208 pInfo.currentBlockSize = static_cast< std::size_t>( tileColumns ) * static_cast< std::size_t >( tileRows );
209 pInfo.iteratedPixels += pInfo.currentBlockSize;
210
211 const qgssize tileRight = tileTopLeftColumn + tileColumns;
212 const qgssize tileBottom = tileTopLeftRow + tileRows;
213
214 const qgssize blockLeft = tileTopLeftColumn >= mTileOverlapPixels ? ( tileTopLeftColumn - mTileOverlapPixels ) : 0;
215 const qgssize blockTop = tileTopLeftRow >= mTileOverlapPixels ? ( tileTopLeftRow - mTileOverlapPixels ) : 0;
216 const qgssize blockRight = std::min< qgssize >( tileRight + mTileOverlapPixels, pInfo.nCols );
217 const qgssize blockBottom = std::min< qgssize >( tileBottom + mTileOverlapPixels, pInfo.nRows );
218
219 nCols = blockRight - blockLeft;
220 nRows = blockBottom - blockTop;
221
222 if ( mSnapToPixelFactor > 1 )
223 {
224 // Ensure overlap dimensions are also multiples of snap factor
225 nCols = ( nCols / mSnapToPixelFactor ) * mSnapToPixelFactor;
226 nRows = ( nRows / mSnapToPixelFactor ) * mSnapToPixelFactor;
227 if ( nCols == 0 || nRows == 0 )
228 return false;
229 }
230
231 QgsDebugMsgLevel( u"nCols = %1 nRows = %2"_s.arg( nCols ).arg( nRows ), 4 );
232
233 //get subrectangle
234 const QgsRectangle viewPortExtent = mExtent;
235 const double xmin = viewPortExtent.xMinimum() + blockLeft / static_cast< double >( pInfo.nCols ) * viewPortExtent.width();
236 const double xmax = blockLeft + nCols == pInfo.nCols ? viewPortExtent.xMaximum() : // avoid extra FP math if not necessary
237 viewPortExtent.xMinimum() + ( blockLeft + nCols ) / static_cast< double >( pInfo.nCols ) * viewPortExtent.width();
238 const double ymin = blockTop + nRows == pInfo.nRows ? viewPortExtent.yMinimum() : // avoid extra FP math if not necessary
239 viewPortExtent.yMaximum() - ( blockTop + nRows ) / static_cast< double >( pInfo.nRows ) * viewPortExtent.height();
240 const double ymax = viewPortExtent.yMaximum() - blockTop / static_cast< double >( pInfo.nRows ) * viewPortExtent.height();
241 const QgsRectangle blockRect( xmin, ymin, xmax, ymax );
242
243 if ( blockExtent )
244 *blockExtent = blockRect;
245
246 if ( block )
247 block->reset( mInput->block( bandNumber, blockRect, nCols, nRows, mFeedback ) );
248 topLeftCol = blockLeft;
249 topLeftRow = blockTop;
250
251 pInfo.currentCol = tileRight;
252 if ( pInfo.currentCol == pInfo.nCols && tileBottom == pInfo.nRows ) //end of raster
253 {
254 pInfo.currentRow = pInfo.nRows;
255 }
256 else if ( pInfo.currentCol == pInfo.nCols ) //start new row
257 {
258 pInfo.currentCol = 0;
259 pInfo.currentRow = tileBottom;
260 }
261
262 return true;
263}
264
266{
267 removePartInfo( bandNumber );
268}
269
270double QgsRasterIterator::progress( int bandNumber, double currentBlockProgress ) const
271{
272 if ( currentBlockProgress < 0 )
273 {
274 const auto partIt = mRasterPartInfos.find( bandNumber );
275 if ( partIt == mRasterPartInfos.constEnd() )
276 {
277 return 0;
278 }
279 const std::size_t totalSize = static_cast< std::size_t >( partIt->nCols ) * static_cast< std::size_t >( partIt->nRows );
280 return static_cast< double >( partIt->iteratedPixels ) / static_cast< double >( totalSize );
281 }
282 else
283 {
284 const auto partIt = mRasterPartInfos.find( bandNumber );
285 if ( partIt == mRasterPartInfos.constEnd() )
286 {
287 return 0;
288 }
289
290 const std::size_t totalSize = static_cast< std::size_t >( partIt->nCols ) * static_cast< std::size_t >( partIt->nRows );
291 const double startOfBlockProgress = static_cast< double >( partIt->previousIteratedPixels ) / static_cast< double >( totalSize );
292 const double currentBlockProgressFraction = currentBlockProgress * static_cast< double >( partIt->currentBlockSize ) / static_cast< double >( totalSize );
293
294 return startOfBlockProgress + currentBlockProgressFraction;
295 }
296}
297
298void QgsRasterIterator::removePartInfo( int bandNumber )
299{
300 const auto partIt = mRasterPartInfos.constFind( bandNumber );
301 if ( partIt != mRasterPartInfos.constEnd() )
302 {
303 mRasterPartInfos.remove( bandNumber );
304 }
305}
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:7485
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63