1 /***************************************************************************
2     qgsrasterdataprovider.cpp - DataProvider Interface for raster layers
3      --------------------------------------
4     Date                 : Mar 11, 2005
5     Copyright            : (C) 2005 by Brendan Morley
6     email                : morb at ozemail dot com dot au
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 "qgsproviderregistry.h"
19 #include "qgsrasterdataprovider.h"
20 #include "qgsrasteridentifyresult.h"
21 #include "qgsprovidermetadata.h"
22 #include "qgsrasterprojector.h"
23 #include "qgslogger.h"
24 #include "qgsmessagelog.h"
25 #include "qgsapplication.h"
26 
27 #include <QTime>
28 #include <QMap>
29 #include <QByteArray>
30 #include <QVariant>
31 
32 #define ERR(message) QgsError(message, "Raster provider")
33 
setUseSourceNoDataValue(int bandNo,bool use)34 void QgsRasterDataProvider::setUseSourceNoDataValue( int bandNo, bool use )
35 {
36   if ( mUseSrcNoDataValue.size() < bandNo )
37   {
38     for ( int i = mUseSrcNoDataValue.size(); i < bandNo; i++ )
39     {
40       mUseSrcNoDataValue.append( false );
41     }
42   }
43   mUseSrcNoDataValue[bandNo - 1] = use;
44 }
45 
block(int bandNo,QgsRectangle const & boundingBox,int width,int height,QgsRasterBlockFeedback * feedback)46 QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle  const &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback )
47 {
48   QgsDebugMsgLevel( QStringLiteral( "bandNo = %1 width = %2 height = %3" ).arg( bandNo ).arg( width ).arg( height ), 4 );
49   QgsDebugMsgLevel( QStringLiteral( "boundingBox = %1" ).arg( boundingBox.toString() ), 4 );
50 
51   std::unique_ptr< QgsRasterBlock > block = qgis::make_unique< QgsRasterBlock >( dataType( bandNo ), width, height );
52   if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) )
53   {
54     block->setNoDataValue( sourceNoDataValue( bandNo ) );
55   }
56 
57   if ( block->isEmpty() )
58   {
59     QgsDebugMsg( QStringLiteral( "Couldn't create raster block" ) );
60     block->setError( { tr( "Couldn't create raster block." ), QStringLiteral( "Raster" ) } );
61     block->setValid( false );
62     return block.release();
63   }
64 
65   // Read necessary extent only
66   QgsRectangle tmpExtent = boundingBox;
67 
68   if ( tmpExtent.isEmpty() )
69   {
70     QgsDebugMsg( QStringLiteral( "Extent outside provider extent" ) );
71     block->setError( { tr( "Extent outside provider extent." ), QStringLiteral( "Raster" ) } );
72     block->setValid( false );
73     block->setIsNoData();
74     return block.release();
75   }
76 
77   double xRes = boundingBox.width() / width;
78   double yRes = boundingBox.height() / height;
79   double tmpXRes, tmpYRes;
80   double providerXRes = 0;
81   double providerYRes = 0;
82   if ( capabilities() & Size )
83   {
84     providerXRes = extent().width() / xSize();
85     providerYRes = extent().height() / ySize();
86     tmpXRes = std::max( providerXRes, xRes );
87     tmpYRes = std::max( providerYRes, yRes );
88     if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes;
89     if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes;
90   }
91   else
92   {
93     tmpXRes = xRes;
94     tmpYRes = yRes;
95   }
96 
97   if ( tmpExtent != boundingBox ||
98        tmpXRes > xRes || tmpYRes > yRes )
99   {
100     // Read smaller extent or lower resolution
101 
102     if ( !extent().contains( boundingBox ) )
103     {
104       QRect subRect = QgsRasterBlock::subRect( boundingBox, width, height, extent() );
105       block->setIsNoDataExcept( subRect );
106     }
107 
108     // Calculate row/col limits (before tmpExtent is aligned)
109     int fromRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes );
110     int toRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1;
111     int fromCol = std::round( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes );
112     int toCol = std::round( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1;
113 
114     QgsDebugMsgLevel( QStringLiteral( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 );
115 
116     if ( fromRow < 0 || fromRow >= height || toRow < 0 || toRow >= height ||
117          fromCol < 0 || fromCol >= width || toCol < 0 || toCol >= width )
118     {
119       // Should not happen
120       QgsDebugMsg( QStringLiteral( "Row or column limits out of range" ) );
121       block->setError( { tr( "Row or column limits out of range" ), QStringLiteral( "Raster" ) } );
122       block->setValid( false );
123       return block.release();
124     }
125 
126     // If lower source resolution is used, the extent must be aligned to original
127     // resolution to avoid possible shift due to resampling
128     if ( tmpXRes > xRes )
129     {
130       int col = std::floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes );
131       tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes );
132       col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes );
133       tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes );
134     }
135     if ( tmpYRes > yRes )
136     {
137       int row = std::floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes );
138       tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes );
139       row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes );
140       tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes );
141     }
142     int tmpWidth = std::round( tmpExtent.width() / tmpXRes );
143     int tmpHeight = std::round( tmpExtent.height() / tmpYRes );
144     tmpXRes = tmpExtent.width() / tmpWidth;
145     tmpYRes = tmpExtent.height() / tmpHeight;
146 
147     QgsDebugMsgLevel( QStringLiteral( "Reading smaller block tmpWidth = %1 height = %2" ).arg( tmpWidth ).arg( tmpHeight ), 4 );
148     QgsDebugMsgLevel( QStringLiteral( "tmpExtent = %1" ).arg( tmpExtent.toString() ), 4 );
149 
150     std::unique_ptr< QgsRasterBlock > tmpBlock = qgis::make_unique< QgsRasterBlock >( dataType( bandNo ), tmpWidth, tmpHeight );
151     if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) )
152     {
153       tmpBlock->setNoDataValue( sourceNoDataValue( bandNo ) );
154     }
155 
156     if ( !readBlock( bandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback ) )
157     {
158       QgsDebugMsg( QStringLiteral( "Error occurred while reading block" ) );
159       block->setError( { tr( "Error occurred while reading block." ), QStringLiteral( "Raster" ) } );
160       block->setValid( false );
161       block->setIsNoData();
162       return block.release();
163     }
164 
165     int pixelSize = dataTypeSize( bandNo );
166 
167     double xMin = boundingBox.xMinimum();
168     double yMax = boundingBox.yMaximum();
169     double tmpXMin = tmpExtent.xMinimum();
170     double tmpYMax = tmpExtent.yMaximum();
171 
172     for ( int row = fromRow; row <= toRow; row++ )
173     {
174       double y = yMax - ( row + 0.5 ) * yRes;
175       int tmpRow = std::floor( ( tmpYMax - y ) / tmpYRes );
176 
177       for ( int col = fromCol; col <= toCol; col++ )
178       {
179         double x = xMin + ( col + 0.5 ) * xRes;
180         int tmpCol = std::floor( ( x - tmpXMin ) / tmpXRes );
181 
182         if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth )
183         {
184           QgsDebugMsg( QStringLiteral( "Source row or column limits out of range" ) );
185           block->setIsNoData(); // so that the problem becomes obvious and fixed
186           block->setError( { tr( "Source row or column limits out of range." ), QStringLiteral( "Raster" ) } );
187           block->setValid( false );
188           return block.release();
189         }
190 
191         qgssize tmpIndex = static_cast< qgssize >( tmpRow ) * static_cast< qgssize >( tmpWidth ) + tmpCol;
192         qgssize index = row * static_cast< qgssize >( width ) + col;
193 
194         char *tmpBits = tmpBlock->bits( tmpIndex );
195         char *bits = block->bits( index );
196         if ( !tmpBits )
197         {
198           QgsDebugMsg( QStringLiteral( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) );
199           continue;
200         }
201         if ( !bits )
202         {
203           QgsDebugMsg( QStringLiteral( "Cannot set output block data." ) );
204           continue;
205         }
206         memcpy( bits, tmpBits, pixelSize );
207       }
208     }
209   }
210   else
211   {
212     if ( !readBlock( bandNo, boundingBox, width, height, block->bits(), feedback ) )
213     {
214       QgsDebugMsg( QStringLiteral( "Error occurred while reading block" ) );
215       block->setIsNoData();
216       block->setError( { tr( "Error occurred while reading block." ), QStringLiteral( "Raster" ) } );
217       block->setValid( false );
218       return block.release();
219     }
220   }
221 
222   // apply scale and offset
223   block->applyScaleOffset( bandScale( bandNo ), bandOffset( bandNo ) );
224   // apply user no data values
225   block->applyNoDataValues( userNoDataValues( bandNo ) );
226   return block.release();
227 }
228 
QgsRasterDataProvider()229 QgsRasterDataProvider::QgsRasterDataProvider()
230   : QgsDataProvider( QString(), QgsDataProvider::ProviderOptions(), QgsDataProvider::ReadFlags() )
231   , QgsRasterInterface( nullptr )
232   , mTemporalCapabilities( qgis::make_unique< QgsRasterDataProviderTemporalCapabilities >() )
233 {
234 
235 }
236 
QgsRasterDataProvider(const QString & uri,const ProviderOptions & options,QgsDataProvider::ReadFlags flags)237 QgsRasterDataProvider::QgsRasterDataProvider( const QString &uri, const ProviderOptions &options,
238     QgsDataProvider::ReadFlags flags )
239   : QgsDataProvider( uri, options, flags )
240   , QgsRasterInterface( nullptr )
241   , mTemporalCapabilities( qgis::make_unique< QgsRasterDataProviderTemporalCapabilities >() )
242 {
243 }
244 
providerCapabilities() const245 QgsRasterDataProvider::ProviderCapabilities QgsRasterDataProvider::providerCapabilities() const
246 {
247   return QgsRasterDataProvider::NoProviderCapabilities;
248 }
249 
250 //
251 //Random Static convenience function
252 //
253 /////////////////////////////////////////////////////////
254 
255 // TODO
256 // (WMS) IdentifyFormatFeature is not consistent with QgsRaster::IdentifyFormatValue.
257 // IdentifyFormatHtml: better error reporting
identify(const QgsPointXY & point,QgsRaster::IdentifyFormat format,const QgsRectangle & boundingBox,int width,int height,int)258 QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPointXY &point, QgsRaster::IdentifyFormat format, const QgsRectangle &boundingBox, int width, int height, int /*dpi*/ )
259 {
260   QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
261   QMap<int, QVariant> results;
262 
263   if ( format != QgsRaster::IdentifyFormatValue || !( capabilities() & IdentifyValue ) )
264   {
265     QgsDebugMsg( QStringLiteral( "Format not supported" ) );
266     return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) );
267   }
268 
269   if ( !extent().contains( point ) )
270   {
271     // Outside the raster
272     for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
273     {
274       results.insert( bandNo, QVariant() );
275     }
276     return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results );
277   }
278 
279   QgsRectangle finalExtent = boundingBox;
280   if ( finalExtent.isEmpty() )
281     finalExtent = extent();
282 
283   if ( width == 0 )
284   {
285     width = capabilities() & Size ? xSize() : 1000;
286   }
287   if ( height == 0 )
288   {
289     height = capabilities() & Size ? ySize() : 1000;
290   }
291 
292   // Calculate the row / column where the point falls
293   double xres = ( finalExtent.width() ) / width;
294   double yres = ( finalExtent.height() ) / height;
295 
296   int col = static_cast< int >( std::floor( ( point.x() - finalExtent.xMinimum() ) / xres ) );
297   int row = static_cast< int >( std::floor( ( finalExtent.yMaximum() - point.y() ) / yres ) );
298 
299   double xMin = finalExtent.xMinimum() + col * xres;
300   double xMax = xMin + xres;
301   double yMax = finalExtent.yMaximum() - row * yres;
302   double yMin = yMax - yres;
303   QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );
304 
305   for ( int i = 1; i <= bandCount(); i++ )
306   {
307     std::unique_ptr< QgsRasterBlock > bandBlock( block( i, pixelExtent, 1, 1 ) );
308 
309     if ( bandBlock )
310     {
311       double value = bandBlock->value( 0 );
312 
313       results.insert( i, value );
314     }
315     else
316     {
317       results.insert( i, QVariant() );
318     }
319   }
320   return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results );
321 }
322 
sample(const QgsPointXY & point,int band,bool * ok,const QgsRectangle & boundingBox,int width,int height,int dpi)323 double QgsRasterDataProvider::sample( const QgsPointXY &point, int band,
324                                       bool *ok, const QgsRectangle &boundingBox, int width, int height, int dpi )
325 {
326   if ( ok )
327     *ok = false;
328 
329   const auto res = identify( point, QgsRaster::IdentifyFormatValue, boundingBox, width, height, dpi );
330   const QVariant value = res.results().value( band );
331 
332   if ( !value.isValid() )
333     return std::numeric_limits<double>::quiet_NaN();
334 
335   if ( ok )
336     *ok = true;
337 
338   return value.toDouble( ok );
339 }
340 
lastErrorFormat()341 QString QgsRasterDataProvider::lastErrorFormat()
342 {
343   return QStringLiteral( "text/plain" );
344 }
345 
writeBlock(QgsRasterBlock * block,int band,int xOffset,int yOffset)346 bool QgsRasterDataProvider::writeBlock( QgsRasterBlock *block, int band, int xOffset, int yOffset )
347 {
348   if ( !block )
349     return false;
350   if ( !isEditable() )
351   {
352     QgsDebugMsg( QStringLiteral( "writeBlock() called on read-only provider." ) );
353     return false;
354   }
355   return write( block->bits(), band, block->width(), block->height(), xOffset, yOffset );
356 }
357 
358 // typedef QList<QPair<QString, QString> > *pyramidResamplingMethods_t();
pyramidResamplingMethods(const QString & providerKey)359 QList<QPair<QString, QString> > QgsRasterDataProvider::pyramidResamplingMethods( const QString &providerKey )
360 {
361   QList<QPair<QString, QString> > methods = QgsProviderRegistry::instance()->pyramidResamplingMethods( providerKey );
362   if ( methods.isEmpty() )
363   {
364     QgsDebugMsg( QStringLiteral( "provider pyramidResamplingMethods returned no methods" ) );
365   }
366   return methods;
367 }
368 
hasPyramids()369 bool QgsRasterDataProvider::hasPyramids()
370 {
371   QList<QgsRasterPyramid> myPyramidList = buildPyramidList();
372 
373   if ( myPyramidList.isEmpty() )
374     return false;
375 
376   QList<QgsRasterPyramid>::iterator myRasterPyramidIterator;
377   for ( myRasterPyramidIterator = myPyramidList.begin();
378         myRasterPyramidIterator != myPyramidList.end();
379         ++myRasterPyramidIterator )
380   {
381     if ( myRasterPyramidIterator->exists )
382     {
383       return true;
384     }
385   }
386   return false;
387 }
388 
setUserNoDataValue(int bandNo,const QgsRasterRangeList & noData)389 void QgsRasterDataProvider::setUserNoDataValue( int bandNo, const QgsRasterRangeList &noData )
390 {
391   if ( bandNo >= mUserNoDataValue.size() )
392   {
393     for ( int i = mUserNoDataValue.size(); i < bandNo; i++ )
394     {
395       mUserNoDataValue.append( QgsRasterRangeList() );
396     }
397   }
398   QgsDebugMsgLevel( QStringLiteral( "set %1 band %1 no data ranges" ).arg( noData.size() ), 4 );
399 
400   if ( mUserNoDataValue[bandNo - 1] != noData )
401   {
402     // Clear statistics
403     mStatistics.erase( std::remove_if( mStatistics.begin(), mStatistics.end(), [bandNo]( const QgsRasterBandStats & stats )
404     {
405       return stats.bandNumber == bandNo;
406     } ), mStatistics.end() );
407     mHistograms.erase( std::remove_if( mHistograms.begin(), mHistograms.end(), [bandNo]( const QgsRasterHistogram & histogram )
408     {
409       return histogram.bandNumber == bandNo;
410     } ), mHistograms.end() );
411     mUserNoDataValue[bandNo - 1] = noData;
412   }
413 }
414 
temporalCapabilities()415 QgsRasterDataProviderTemporalCapabilities *QgsRasterDataProvider::temporalCapabilities()
416 {
417   return mTemporalCapabilities.get();
418 }
419 
temporalCapabilities() const420 const QgsRasterDataProviderTemporalCapabilities *QgsRasterDataProvider::temporalCapabilities() const
421 {
422   return mTemporalCapabilities.get();
423 }
424 
create(const QString & providerKey,const QString & uri,const QString & format,int nBands,Qgis::DataType type,int width,int height,double * geoTransform,const QgsCoordinateReferenceSystem & crs,const QStringList & createOptions)425 QgsRasterDataProvider *QgsRasterDataProvider::create( const QString &providerKey,
426     const QString &uri,
427     const QString &format, int nBands,
428     Qgis::DataType type,
429     int width, int height, double *geoTransform,
430     const QgsCoordinateReferenceSystem &crs,
431     const QStringList &createOptions )
432 {
433   QgsRasterDataProvider *ret = QgsProviderRegistry::instance()->createRasterDataProvider(
434                                  providerKey,
435                                  uri, format,
436                                  nBands, type, width,
437                                  height, geoTransform, crs, createOptions );
438   if ( !ret )
439   {
440     QgsDebugMsg( "Cannot resolve 'createRasterDataProviderFunction' function in " + providerKey + " provider" );
441   }
442 
443   // TODO: it would be good to return invalid QgsRasterDataProvider
444   // with QgsError set, but QgsRasterDataProvider has pure virtual methods
445 
446   return ret;
447 }
448 
identifyFormatName(QgsRaster::IdentifyFormat format)449 QString QgsRasterDataProvider::identifyFormatName( QgsRaster::IdentifyFormat format )
450 {
451   switch ( format )
452   {
453     case QgsRaster::IdentifyFormatValue:
454       return QStringLiteral( "Value" );
455     case QgsRaster::IdentifyFormatText:
456       return QStringLiteral( "Text" );
457     case QgsRaster::IdentifyFormatHtml:
458       return QStringLiteral( "Html" );
459     case QgsRaster::IdentifyFormatFeature:
460       return QStringLiteral( "Feature" );
461     default:
462       return QStringLiteral( "Undefined" );
463   }
464 }
465 
identifyFormatLabel(QgsRaster::IdentifyFormat format)466 QString QgsRasterDataProvider::identifyFormatLabel( QgsRaster::IdentifyFormat format )
467 {
468   switch ( format )
469   {
470     case QgsRaster::IdentifyFormatValue:
471       return tr( "Value" );
472     case QgsRaster::IdentifyFormatText:
473       return tr( "Text" );
474     case QgsRaster::IdentifyFormatHtml:
475       return tr( "Html" );
476     case QgsRaster::IdentifyFormatFeature:
477       return tr( "Feature" );
478     default:
479       return QStringLiteral( "Undefined" );
480   }
481 }
482 
identifyFormatFromName(const QString & formatName)483 QgsRaster::IdentifyFormat QgsRasterDataProvider::identifyFormatFromName( const QString &formatName )
484 {
485   if ( formatName == QLatin1String( "Value" ) ) return QgsRaster::IdentifyFormatValue;
486   if ( formatName == QLatin1String( "Text" ) ) return QgsRaster::IdentifyFormatText;
487   if ( formatName == QLatin1String( "Html" ) ) return QgsRaster::IdentifyFormatHtml;
488   if ( formatName == QLatin1String( "Feature" ) ) return QgsRaster::IdentifyFormatFeature;
489   return QgsRaster::IdentifyFormatUndefined;
490 }
491 
identifyFormatToCapability(QgsRaster::IdentifyFormat format)492 QgsRasterInterface::Capability QgsRasterDataProvider::identifyFormatToCapability( QgsRaster::IdentifyFormat format )
493 {
494   switch ( format )
495   {
496     case QgsRaster::IdentifyFormatValue:
497       return IdentifyValue;
498     case QgsRaster::IdentifyFormatText:
499       return IdentifyText;
500     case QgsRaster::IdentifyFormatHtml:
501       return IdentifyHtml;
502     case QgsRaster::IdentifyFormatFeature:
503       return IdentifyFeature;
504     default:
505       return NoCapabilities;
506   }
507 }
508 
nativeResolutions() const509 QList<double> QgsRasterDataProvider::nativeResolutions() const
510 {
511   return QList< double >();
512 }
513 
ignoreExtents() const514 bool QgsRasterDataProvider::ignoreExtents() const
515 {
516   return false;
517 }
518 
transformCoordinates(const QgsPoint & point,QgsRasterDataProvider::TransformType type)519 QgsPoint QgsRasterDataProvider::transformCoordinates( const QgsPoint &point, QgsRasterDataProvider::TransformType type )
520 {
521   Q_UNUSED( point )
522   Q_UNUSED( type )
523   return QgsPoint();
524 }
525 
userNoDataValuesContains(int bandNo,double value) const526 bool QgsRasterDataProvider::userNoDataValuesContains( int bandNo, double value ) const
527 {
528   QgsRasterRangeList rangeList = mUserNoDataValue.value( bandNo - 1 );
529   return QgsRasterRange::contains( value, rangeList );
530 }
531 
copyBaseSettings(const QgsRasterDataProvider & other)532 void QgsRasterDataProvider::copyBaseSettings( const QgsRasterDataProvider &other )
533 {
534   mDpi = other.mDpi;
535   mSrcNoDataValue = other.mSrcNoDataValue;
536   mSrcHasNoDataValue = other.mSrcHasNoDataValue;
537   mUseSrcNoDataValue = other.mUseSrcNoDataValue;
538   mUserNoDataValue = other.mUserNoDataValue;
539   mExtent = other.mExtent;
540   mProviderResamplingEnabled = other.mProviderResamplingEnabled;
541   mZoomedInResamplingMethod = other.mZoomedInResamplingMethod;
542   mZoomedOutResamplingMethod = other.mZoomedOutResamplingMethod;
543   mMaxOversampling = other.mMaxOversampling;
544 
545   // copy temporal properties
546   if ( mTemporalCapabilities && other.mTemporalCapabilities )
547   {
548     *mTemporalCapabilities = *other.mTemporalCapabilities;
549   }
550 }
551 
resamplingMethodFromString(const QString & str)552 static QgsRasterDataProvider::ResamplingMethod resamplingMethodFromString( const QString &str )
553 {
554   if ( str == QLatin1String( "bilinear" ) )
555   {
556     return QgsRasterDataProvider::ResamplingMethod::Bilinear;
557   }
558   else if ( str == QLatin1String( "cubic" ) )
559   {
560     return QgsRasterDataProvider::ResamplingMethod::Cubic;
561   }
562   return  QgsRasterDataProvider::ResamplingMethod::Nearest;
563 }
564 
readXml(const QDomElement & filterElem)565 void QgsRasterDataProvider::readXml( const QDomElement &filterElem )
566 {
567   if ( filterElem.isNull() )
568   {
569     return;
570   }
571 
572   QDomElement resamplingElement = filterElem.firstChildElement( QStringLiteral( "resampling" ) );
573   if ( !resamplingElement.isNull() )
574   {
575     setMaxOversampling( resamplingElement.attribute( QStringLiteral( "maxOversampling" ), QStringLiteral( "2.0" ) ).toDouble() );
576     setZoomedInResamplingMethod( resamplingMethodFromString( resamplingElement.attribute( QStringLiteral( "zoomedInResamplingMethod" ) ) ) );
577     setZoomedOutResamplingMethod( resamplingMethodFromString( resamplingElement.attribute( QStringLiteral( "zoomedOutResamplingMethod" ) ) ) );
578     enableProviderResampling( resamplingElement.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "true" ) );
579   }
580 }
581 
resamplingMethodToString(QgsRasterDataProvider::ResamplingMethod method)582 static QString resamplingMethodToString( QgsRasterDataProvider::ResamplingMethod method )
583 {
584   switch ( method )
585   {
586     case QgsRasterDataProvider::ResamplingMethod::Nearest : return QStringLiteral( "nearestNeighbour" );
587     case QgsRasterDataProvider::ResamplingMethod::Bilinear : return QStringLiteral( "bilinear" );
588     case QgsRasterDataProvider::ResamplingMethod::Cubic : return QStringLiteral( "cubic" );
589   }
590   // should not happen
591   return QStringLiteral( "nearestNeighbour" );
592 }
593 
writeXml(QDomDocument & doc,QDomElement & parentElem) const594 void QgsRasterDataProvider::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
595 {
596   QDomElement providerElement = doc.createElement( QStringLiteral( "provider" ) );
597   parentElem.appendChild( providerElement );
598 
599   QDomElement resamplingElement = doc.createElement( QStringLiteral( "resampling" ) );
600   providerElement.appendChild( resamplingElement );
601 
602   resamplingElement.setAttribute( QStringLiteral( "enabled" ),
603                                   mProviderResamplingEnabled ? QStringLiteral( "true" ) :  QStringLiteral( "false" ) );
604 
605   resamplingElement.setAttribute( QStringLiteral( "zoomedInResamplingMethod" ),
606                                   resamplingMethodToString( mZoomedInResamplingMethod ) );
607 
608   resamplingElement.setAttribute( QStringLiteral( "zoomedOutResamplingMethod" ),
609                                   resamplingMethodToString( mZoomedOutResamplingMethod ) );
610 
611   resamplingElement.setAttribute( QStringLiteral( "maxOversampling" ),
612                                   QString::number( mMaxOversampling ) );
613 }
614 
615 
616 // ENDS
617