QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsrasterresamplefilter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterresamplefilter.cpp
3 ---------------------
4 begin : December 2011
5 copyright : (C) 2011 by Marco Hugentobler
6 email : marco at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
20#include "qgsrasterresampler.h"
21#include "qgsrasterprojector.h"
23#include "qgsrasterviewport.h"
24#include "qgsmaptopixel.h"
25
26//resamplers
29
30#include <QDomDocument>
31#include <QDomElement>
32#include <QImage>
33#include <QPainter>
34
36 : QgsRasterInterface( input )
37{
38}
39
41{
42 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
43 QgsRasterResampleFilter *resampler = new QgsRasterResampleFilter( nullptr );
45 {
46 resampler->setZoomedInResampler( mZoomedInResampler->clone() );
47 }
49 {
50 resampler->setZoomedOutResampler( mZoomedOutResampler->clone() );
51 }
53 return resampler;
54}
55
57{
58 if ( mOn ) return 1;
59
60 if ( mInput ) return mInput->bandCount();
61
62 return 0;
63}
64
66{
68
69 if ( mInput ) return mInput->dataType( bandNo );
70
72}
73
75{
76 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
77
78 // Resampler can only work with single band ARGB32_Premultiplied
79 if ( !input )
80 {
81 QgsDebugMsg( QStringLiteral( "No input" ) );
82 return false;
83 }
84
85 if ( !mOn )
86 {
87 // In off mode we can connect to anything
88 QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
89 mInput = input;
90 return true;
91 }
92
93 if ( input->bandCount() < 1 )
94 {
95 QgsDebugMsg( QStringLiteral( "No input band" ) );
96 return false;
97 }
98
101 {
102 QgsDebugMsg( QStringLiteral( "Unknown input data type" ) );
103 return false;
104 }
105
106 mInput = input;
107 QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
108 return true;
109}
110
112{
113 mZoomedInResampler.reset( r );
114}
115
117{
118 mZoomedOutResampler.reset( r );
119}
120
121QgsRasterBlock *QgsRasterResampleFilter::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback )
122{
123 if ( !mOn && mInput )
124 return mInput->block( bandNo, extent, width, height, feedback );
125
126 const int bandNumber = 1;
127
128 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ), 4 );
129 std::unique_ptr< QgsRasterBlock > outputBlock( new QgsRasterBlock() );
130 if ( !mInput )
131 return outputBlock.release();
132
133 double oversampling = 1.0; // approximate global oversampling factor
134 double outputXRes;
135 double providerXRes = 0;
137 {
138 QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider *>( mInput->sourceInput() );
139 if ( provider && ( provider->capabilities() & QgsRasterDataProvider::Size ) )
140 {
141 outputXRes = extent.width() / width;
142 providerXRes = provider->extent().width() / provider->xSize();
143 const double pixelRatio = outputXRes / providerXRes;
144 oversampling = ( pixelRatio > mMaxOversampling ) ? mMaxOversampling : pixelRatio;
145 QgsDebugMsgLevel( QStringLiteral( "xRes = %1 providerXRes = %2 pixelRatio = %3 oversampling = %4" ).arg( outputXRes ).arg( providerXRes ).arg( pixelRatio ).arg( oversampling ), 4 );
146 }
147 else
148 {
149 // We don't know exact data source resolution (WMS) so we expect that
150 // server data have higher resolution (which is not always true) and use
151 // mMaxOversampling
152 oversampling = mMaxOversampling;
153 }
154 }
155
156 QgsDebugMsgLevel( QStringLiteral( "oversampling %1" ).arg( oversampling ), 4 );
157
158 // Do no oversampling if no resampler for zoomed in / zoomed out (nearest neighbour)
159 // We do mZoomedInResampler if oversampling == 1 (otherwise for example reprojected
160 // zoom in rasters are never resampled because projector limits resolution.
161 if ( ( ( oversampling < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) && !mZoomedInResampler ) || ( oversampling > 1.0 && !mZoomedOutResampler ) )
162 {
163 QgsDebugMsgLevel( QStringLiteral( "No oversampling." ), 4 );
164 return mInput->block( bandNumber, extent, width, height, feedback );
165 }
166
167 //effective oversampling factors are different to global one because of rounding
168 const double oversamplingX = ( static_cast< double >( width ) * oversampling ) / width;
169 const double oversamplingY = ( static_cast< double >( height ) * oversampling ) / height;
170
171 // we must also increase the extent to get correct result on borders of parts
172 int tileBufferPixels = 0;
173 if ( providerXRes != 0 )
174 {
175 if ( mZoomedInResampler && ( oversamplingX < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) )
176 {
177 tileBufferPixels = static_cast< int >( std::ceil( mZoomedInResampler->tileBufferPixels() * oversampling ) );
178 }
179 else if ( mZoomedOutResampler && oversamplingX > 1.0 )
180 {
181 tileBufferPixels = static_cast< int >( std::ceil( mZoomedOutResampler->tileBufferPixels() * oversampling ) );
182 }
183 }
184 const double sourceTileBufferSize = providerXRes * tileBufferPixels;
185
186 const QgsRectangle bufferedExtent( extent.xMinimum() - sourceTileBufferSize,
187 extent.yMinimum() - sourceTileBufferSize,
188 extent.xMaximum() + sourceTileBufferSize,
189 extent.yMaximum() + sourceTileBufferSize
190 );
191
192 const int resWidth = static_cast< int >( std::round( width * oversamplingX ) ) + 2 * tileBufferPixels;
193 const int resHeight = static_cast< int >( std::round( height * oversamplingY ) ) + 2 * tileBufferPixels;
194
195 std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNumber, bufferedExtent, resWidth, resHeight, feedback ) );
196 if ( !inputBlock || inputBlock->isEmpty() )
197 {
198 QgsDebugMsg( QStringLiteral( "No raster data!" ) );
199 return outputBlock.release();
200 }
201
202 if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
203 {
204 return outputBlock.release();
205 }
206
207 //resample image
208 const QImage img = inputBlock->image();
209
210 const int resampleWidth = static_cast< int >( std::round( width * ( bufferedExtent.width() / extent.width() ) ) );
211 const int resampleHeight = static_cast< int >( std::round( height * ( bufferedExtent.height() / extent.height() ) ) );
212
213 QImage dstImg;
214 if ( mZoomedInResampler && ( oversamplingX < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) )
215 {
216 QgsDebugMsgLevel( QStringLiteral( "zoomed in resampling" ), 4 );
217
218 if ( QgsRasterResamplerV2 *resamplerV2 = dynamic_cast< QgsRasterResamplerV2 * >( mZoomedInResampler.get( ) ) )
219 {
220 dstImg = resamplerV2->resampleV2( img, QSize( resampleWidth, resampleHeight ) );
221 }
222 else
223 {
224 // old inefficient interface
226 QImage dstImg = QImage( resampleWidth, resampleHeight, QImage::Format_ARGB32_Premultiplied );
227 mZoomedInResampler->resample( img, dstImg );
229 }
230 }
231 else if ( mZoomedOutResampler && oversamplingX > 1.0 )
232 {
233 QgsDebugMsgLevel( QStringLiteral( "zoomed out resampling" ), 4 );
234
235 if ( QgsRasterResamplerV2 *resamplerV2 = dynamic_cast< QgsRasterResamplerV2 * >( mZoomedOutResampler.get( ) ) )
236 {
237 dstImg = resamplerV2->resampleV2( img, QSize( resampleWidth, resampleHeight ) );
238 }
239 else
240 {
241 // old inefficient interface
243 QImage dstImg = QImage( resampleWidth, resampleHeight, QImage::Format_ARGB32_Premultiplied );
244 mZoomedOutResampler->resample( img, dstImg );
246 }
247 }
248 else
249 {
250 // Should not happen
251 QgsDebugMsg( QStringLiteral( "Unexpected resampling" ) );
252 dstImg = img.scaled( width, height );
253 }
254
255 // extract desired part of dstImage
256 const QImage cropped = tileBufferPixels > 0 ? dstImg.copy( ( resampleWidth - width ) / 2, ( resampleHeight - height ) / 2, width, height )
257 : dstImg; // otherwise implicit copy, nice and cheap
258 outputBlock->setImage( &cropped );
259
260 return outputBlock.release(); // No resampling
261}
262
263void QgsRasterResampleFilter::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
264{
265 if ( parentElem.isNull() )
266 {
267 return;
268 }
269
270 QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterresampler" ) );
271
272 rasterRendererElem.setAttribute( QStringLiteral( "maxOversampling" ), QString::number( mMaxOversampling ) );
273 if ( mZoomedInResampler )
274 {
275 rasterRendererElem.setAttribute( QStringLiteral( "zoomedInResampler" ), mZoomedInResampler->type() );
276 }
278 {
279 rasterRendererElem.setAttribute( QStringLiteral( "zoomedOutResampler" ), mZoomedOutResampler->type() );
280 }
281 parentElem.appendChild( rasterRendererElem );
282}
283
284void QgsRasterResampleFilter::readXml( const QDomElement &filterElem )
285{
286 if ( filterElem.isNull() )
287 {
288 return;
289 }
290
291 mMaxOversampling = filterElem.attribute( QStringLiteral( "maxOversampling" ), QStringLiteral( "2.0" ) ).toDouble();
292
293 const QString zoomedInResamplerType = filterElem.attribute( QStringLiteral( "zoomedInResampler" ) );
294 if ( zoomedInResamplerType == QLatin1String( "bilinear" ) )
295 {
297 }
298 else if ( zoomedInResamplerType == QLatin1String( "cubic" ) )
299 {
301 }
302
303 const QString zoomedOutResamplerType = filterElem.attribute( QStringLiteral( "zoomedOutResampler" ) );
304 if ( zoomedOutResamplerType == QLatin1String( "bilinear" ) )
305 {
307 }
308 else if ( zoomedOutResamplerType == QLatin1String( "cubic" ) )
309 {
311 }
312}
DataType
Raster data types.
Definition: qgis.h:129
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Bilinear Raster Resampler.
Cubic Raster Resampler.
Feedback object tailored for raster block reading.
Raster data container.
Base class for raster data providers.
QgsRectangle extent() const override=0
Returns the extent of the layer.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr)=0
Read block of data using given extent and size.
virtual Qgis::DataType dataType(int bandNo) const =0
Returns data type for the band specified by number.
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
virtual int xSize() const
Gets raster size.
virtual int bandCount() const =0
Gets number of bands.
QgsRasterInterface * mInput
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
virtual const QgsRasterInterface * sourceInput() const
Gets source / raw input, the first in pipe, usually provider.
Resample filter pipe for rasters.
double mMaxOversampling
Maximum boundary for oversampling (to avoid too much data traffic). Default: 2.0.
bool setInput(QgsRasterInterface *input) override
Set input.
void setZoomedOutResampler(QgsRasterResampler *r)
Sets resampler for zoomed out scales. Takes ownership of the object.
void writeXml(QDomDocument &doc, QDomElement &parentElem) const override
Write base class members to xml.
Qgis::DataType dataType(int bandNo) const override
Returns data type for the band specified by number.
std::unique_ptr< QgsRasterResampler > mZoomedInResampler
Resampler used if screen resolution is higher than raster resolution (zoomed in). 0 means no resampli...
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
std::unique_ptr< QgsRasterResampler > mZoomedOutResampler
Resampler used if raster resolution is higher than raster resolution (zoomed out)....
QgsRasterResampleFilter(QgsRasterInterface *input=nullptr)
int bandCount() const override
Gets number of bands.
QgsRasterResampleFilter * clone() const override
Clone itself, create deep copy.
void setZoomedInResampler(QgsRasterResampler *r)
Sets resampler for zoomed in scales. Takes ownership of the object.
QgsRasterBlock * block(int bandNo, const QgsRectangle &extent, int width, int height, QgsRasterBlockFeedback *feedback=nullptr) override
Read block of data using given extent and size.
Interface for resampling rasters (V2) (e.g.
Interface for resampling rasters (e.g.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMaximum() const SIP_HOLDGIL
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:183
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double yMinimum() const SIP_HOLDGIL
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:198
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:3061
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:3060
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:2527
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38