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