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