QGIS API Documentation 3.99.0-Master (26c88405ac0)
Loading...
Searching...
No Matches
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
19
21#include "qgsrasterresampler.h"
23
24//resamplers
27
28#include <QDomDocument>
29#include <QDomElement>
30#include <QImage>
31#include <QPainter>
32#include <memory>
33
38
40{
41 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
42 QgsRasterResampleFilter *resampler = new QgsRasterResampleFilter( nullptr );
44 {
45 resampler->setZoomedInResampler( mZoomedInResampler->clone() );
46 }
48 {
49 resampler->setZoomedOutResampler( mZoomedOutResampler->clone() );
50 }
52 return resampler;
53}
54
56{
57 if ( mOn ) return 1;
58
59 if ( mInput ) return mInput->bandCount();
60
61 return 0;
62}
63
65{
67
68 if ( mInput ) return mInput->dataType( bandNo );
69
71}
72
74{
75 QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
76
77 // Resampler can only work with single band ARGB32_Premultiplied
78 if ( !input )
79 {
80 QgsDebugError( QStringLiteral( "No input" ) );
81 return false;
82 }
83
84 if ( !mOn )
85 {
86 // In off mode we can connect to anything
87 QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
88 mInput = input;
89 return true;
90 }
91
92 if ( input->bandCount() < 1 )
93 {
94 QgsDebugError( QStringLiteral( "No input band" ) );
95 return false;
96 }
97
98 if ( input->dataType( 1 ) != Qgis::DataType::ARGB32_Premultiplied &&
99 input->dataType( 1 ) != Qgis::DataType::ARGB32 )
100 {
101 QgsDebugError( QStringLiteral( "Unknown input data type" ) );
102 return false;
103 }
104
105 mInput = input;
106 QgsDebugMsgLevel( QStringLiteral( "OK" ), 4 );
107 return true;
108}
109
114
119
120QgsRasterBlock *QgsRasterResampleFilter::block( int bandNo, QgsRectangle const &extent, int width, int height, QgsRasterBlockFeedback *feedback )
121{
122 if ( !mOn && mInput )
123 return mInput->block( bandNo, extent, width, height, feedback );
124
125 const int bandNumber = 1;
126
127 QgsDebugMsgLevel( QStringLiteral( "width = %1 height = %2 extent = %3" ).arg( width ).arg( height ).arg( extent.toString() ), 4 );
128 auto outputBlock = std::make_unique<QgsRasterBlock>();
129 if ( !mInput )
130 return outputBlock.release();
131
132 double oversampling = 1.0; // approximate global oversampling factor
133 double outputXRes;
134 double providerXRes = 0;
136 {
137 QgsRasterDataProvider *provider = dynamic_cast<QgsRasterDataProvider *>( mInput->sourceInput() );
138 if ( provider && ( provider->capabilities() & Qgis::RasterInterfaceCapability::Size ) )
139 {
140 outputXRes = extent.width() / width;
141 providerXRes = provider->extent().width() / provider->xSize();
142 const double pixelRatio = outputXRes / providerXRes;
143 oversampling = ( pixelRatio > mMaxOversampling ) ? mMaxOversampling : pixelRatio;
144 QgsDebugMsgLevel( QStringLiteral( "xRes = %1 providerXRes = %2 pixelRatio = %3 oversampling = %4" ).arg( outputXRes ).arg( providerXRes ).arg( pixelRatio ).arg( oversampling ), 4 );
145 }
146 else
147 {
148 // We don't know exact data source resolution (WMS) so we expect that
149 // server data have higher resolution (which is not always true) and use
150 // mMaxOversampling
151 oversampling = mMaxOversampling;
152 }
153 }
154
155 QgsDebugMsgLevel( QStringLiteral( "oversampling %1" ).arg( oversampling ), 4 );
156
157 // Do no oversampling if no resampler for zoomed in / zoomed out (nearest neighbour)
158 // We do mZoomedInResampler if oversampling == 1 (otherwise for example reprojected
159 // zoom in rasters are never resampled because projector limits resolution.
160 if ( ( ( oversampling < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) && !mZoomedInResampler ) || ( oversampling > 1.0 && !mZoomedOutResampler ) )
161 {
162 QgsDebugMsgLevel( QStringLiteral( "No oversampling." ), 4 );
163 return mInput->block( bandNumber, extent, width, height, feedback );
164 }
165
166 //effective oversampling factors are different to global one because of rounding
167 const double oversamplingX = ( static_cast< double >( width ) * oversampling ) / width;
168 const double oversamplingY = ( static_cast< double >( height ) * oversampling ) / height;
169
170 // we must also increase the extent to get correct result on borders of parts
171 int tileBufferPixels = 0;
172 if ( providerXRes != 0 )
173 {
174 if ( mZoomedInResampler && ( oversamplingX < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) )
175 {
176 tileBufferPixels = static_cast< int >( std::ceil( mZoomedInResampler->tileBufferPixels() * oversampling ) );
177 }
178 else if ( mZoomedOutResampler && oversamplingX > 1.0 )
179 {
180 tileBufferPixels = static_cast< int >( std::ceil( mZoomedOutResampler->tileBufferPixels() * oversampling ) );
181 }
182 }
183 const double sourceTileBufferSize = providerXRes * tileBufferPixels;
184
185 const QgsRectangle bufferedExtent( extent.xMinimum() - sourceTileBufferSize,
186 extent.yMinimum() - sourceTileBufferSize,
187 extent.xMaximum() + sourceTileBufferSize,
188 extent.yMaximum() + sourceTileBufferSize
189 );
190
191 const int resWidth = static_cast< int >( std::round( width * oversamplingX ) ) + 2 * tileBufferPixels;
192 const int resHeight = static_cast< int >( std::round( height * oversamplingY ) ) + 2 * tileBufferPixels;
193
194 std::unique_ptr< QgsRasterBlock > inputBlock( mInput->block( bandNumber, bufferedExtent, resWidth, resHeight, feedback ) );
195 if ( !inputBlock || inputBlock->isEmpty() )
196 {
197 QgsDebugError( QStringLiteral( "No raster data!" ) );
198 return outputBlock.release();
199 }
200
201 if ( !outputBlock->reset( Qgis::DataType::ARGB32_Premultiplied, width, height ) )
202 {
203 return outputBlock.release();
204 }
205
206 //resample image
207 const QImage img = inputBlock->image();
208
209 const int resampleWidth = static_cast< int >( std::round( width * ( bufferedExtent.width() / extent.width() ) ) );
210 const int resampleHeight = static_cast< int >( std::round( height * ( bufferedExtent.height() / extent.height() ) ) );
211
212 QImage dstImg;
213 if ( mZoomedInResampler && ( oversamplingX < 1.0 || qgsDoubleNear( oversampling, 1.0 ) ) )
214 {
215 QgsDebugMsgLevel( QStringLiteral( "zoomed in resampling" ), 4 );
216
217 if ( QgsRasterResamplerV2 *resamplerV2 = dynamic_cast< QgsRasterResamplerV2 * >( mZoomedInResampler.get( ) ) )
218 {
219 dstImg = resamplerV2->resampleV2( img, QSize( resampleWidth, resampleHeight ) );
220 }
221 else
222 {
223 // old inefficient interface
225 QImage dstImg = QImage( resampleWidth, resampleHeight, QImage::Format_ARGB32_Premultiplied );
226 mZoomedInResampler->resample( img, dstImg );
228 }
229 }
230 else if ( mZoomedOutResampler && oversamplingX > 1.0 )
231 {
232 QgsDebugMsgLevel( QStringLiteral( "zoomed out resampling" ), 4 );
233
234 if ( QgsRasterResamplerV2 *resamplerV2 = dynamic_cast< QgsRasterResamplerV2 * >( mZoomedOutResampler.get( ) ) )
235 {
236 dstImg = resamplerV2->resampleV2( img, QSize( resampleWidth, resampleHeight ) );
237 }
238 else
239 {
240 // old inefficient interface
242 QImage dstImg = QImage( resampleWidth, resampleHeight, QImage::Format_ARGB32_Premultiplied );
243 mZoomedOutResampler->resample( img, dstImg );
245 }
246 }
247 else
248 {
249 // Should not happen
250 QgsDebugError( QStringLiteral( "Unexpected resampling" ) );
251 dstImg = img.scaled( width, height );
252 }
253
254 // extract desired part of dstImage
255 const QImage cropped = tileBufferPixels > 0 ? dstImg.copy( ( resampleWidth - width ) / 2, ( resampleHeight - height ) / 2, width, height )
256 : dstImg; // otherwise implicit copy, nice and cheap
257 outputBlock->setImage( &cropped );
258
259 return outputBlock.release(); // No resampling
260}
261
262void QgsRasterResampleFilter::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
263{
264 if ( parentElem.isNull() )
265 {
266 return;
267 }
268
269 QDomElement rasterRendererElem = doc.createElement( QStringLiteral( "rasterresampler" ) );
270
271 rasterRendererElem.setAttribute( QStringLiteral( "maxOversampling" ), QString::number( mMaxOversampling ) );
272 if ( mZoomedInResampler )
273 {
274 rasterRendererElem.setAttribute( QStringLiteral( "zoomedInResampler" ), mZoomedInResampler->type() );
275 }
277 {
278 rasterRendererElem.setAttribute( QStringLiteral( "zoomedOutResampler" ), mZoomedOutResampler->type() );
279 }
280 parentElem.appendChild( rasterRendererElem );
281}
282
283void QgsRasterResampleFilter::readXml( const QDomElement &filterElem )
284{
285 if ( filterElem.isNull() )
286 {
287 return;
288 }
289
290 mMaxOversampling = filterElem.attribute( QStringLiteral( "maxOversampling" ), QStringLiteral( "2.0" ) ).toDouble();
291
292 const QString zoomedInResamplerType = filterElem.attribute( QStringLiteral( "zoomedInResampler" ) );
293 if ( zoomedInResamplerType == QLatin1String( "bilinear" ) )
294 {
295 mZoomedInResampler = std::make_unique<QgsBilinearRasterResampler>( );
296 }
297 else if ( zoomedInResamplerType == QLatin1String( "cubic" ) )
298 {
299 mZoomedInResampler = std::make_unique<QgsCubicRasterResampler>( );
300 }
301
302 const QString zoomedOutResamplerType = filterElem.attribute( QStringLiteral( "zoomedOutResampler" ) );
303 if ( zoomedOutResamplerType == QLatin1String( "bilinear" ) )
304 {
305 mZoomedOutResampler = std::make_unique<QgsBilinearRasterResampler>( );
306 }
307 else if ( zoomedOutResamplerType == QLatin1String( "cubic" ) )
308 {
309 mZoomedOutResampler = std::make_unique<QgsCubicRasterResampler>( );
310 }
311}
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
Definition qgis.h:4855
DataType
Raster data types.
Definition qgis.h:372
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition qgis.h:387
@ UnknownDataType
Unknown or unspecified type.
Definition qgis.h:373
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition qgis.h:386
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.
virtual Qgis::RasterInterfaceCapabilities capabilities() const
Returns the capabilities supported by the interface.
virtual int xSize() const
Gets raster size.
QgsRasterInterface(QgsRasterInterface *input=nullptr)
QgsRasterInterface * mInput
virtual QgsRectangle extent() const
Gets the extent of the interface.
virtual QgsRasterInterface * input() const
Current input.
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.
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7170
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7169
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57