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