QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
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 
18 #include "qgsrasterdataprovider.h"
20 #include "qgsrasterresampler.h"
21 #include "qgsrasterprojector.h"
22 #include "qgsrastertransparency.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 );
44  if ( mZoomedInResampler )
45  {
46  resampler->setZoomedInResampler( mZoomedInResampler->clone() );
47  }
48  if ( mZoomedOutResampler )
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  QgsDebugError( 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  QgsDebugError( QStringLiteral( "No input band" ) );
96  return false;
97  }
98 
101  {
102  QgsDebugError( 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 
121 QgsRasterBlock *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  QgsDebugError( 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  QgsDebugError( 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 
263 void 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  }
277  if ( mZoomedOutResampler )
278  {
279  rasterRendererElem.setAttribute( QStringLiteral( "zoomedOutResampler" ), mZoomedOutResampler->type() );
280  }
281  parentElem.appendChild( rasterRendererElem );
282 }
283 
284 void 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:269
@ 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 QgsRasterInterface * input() const
Current input.
virtual int xSize() const
Gets raster size.
virtual const QgsRasterInterface * sourceInput() const
Gets source / raw input, the first in pipe, usually provider.
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.
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 xMinimum() const
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:201
double yMinimum() const
Returns the y minimum value (bottom side of rectangle).
Definition: qgsrectangle.h:211
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:236
double xMaximum() const
Returns the x maximum value (right side of rectangle).
Definition: qgsrectangle.h:196
double yMaximum() const
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:206
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:243
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:5741
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:5740
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5172
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38