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 #include "qgspoint.h"
27 
28 #include <QTime>
29 #include <QMap>
30 #include <QByteArray>
31 #include <QVariant>
32 
33 #include <QUrl>
34 #include <QUrlQuery>
35 #include <QSet>
36 
37 #define ERR(message) QgsError(message, "Raster provider")
38 
setUseSourceNoDataValue(int bandNo,bool use)39 void QgsRasterDataProvider::setUseSourceNoDataValue( int bandNo, bool use )
40 {
41   if ( mUseSrcNoDataValue.size() < bandNo )
42   {
43     for ( int i = mUseSrcNoDataValue.size(); i < bandNo; i++ )
44     {
45       mUseSrcNoDataValue.append( false );
46     }
47   }
48   mUseSrcNoDataValue[bandNo - 1] = use;
49 }
50 
block(int bandNo,QgsRectangle const & boundingBox,int width,int height,QgsRasterBlockFeedback * feedback)51 QgsRasterBlock *QgsRasterDataProvider::block( int bandNo, QgsRectangle  const &boundingBox, int width, int height, QgsRasterBlockFeedback *feedback )
52 {
53   QgsDebugMsgLevel( QStringLiteral( "bandNo = %1 width = %2 height = %3" ).arg( bandNo ).arg( width ).arg( height ), 4 );
54   QgsDebugMsgLevel( QStringLiteral( "boundingBox = %1" ).arg( boundingBox.toString() ), 4 );
55 
56   std::unique_ptr< QgsRasterBlock > block = std::make_unique< QgsRasterBlock >( dataType( bandNo ), width, height );
57   if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) )
58   {
59     block->setNoDataValue( sourceNoDataValue( bandNo ) );
60   }
61 
62   if ( block->isEmpty() )
63   {
64     QgsDebugMsg( QStringLiteral( "Couldn't create raster block" ) );
65     block->setError( { tr( "Couldn't create raster block." ), QStringLiteral( "Raster" ) } );
66     block->setValid( false );
67     return block.release();
68   }
69 
70   // Read necessary extent only
71   QgsRectangle tmpExtent = boundingBox;
72 
73   if ( tmpExtent.isEmpty() )
74   {
75     QgsDebugMsg( QStringLiteral( "Extent outside provider extent" ) );
76     block->setError( { tr( "Extent outside provider extent." ), QStringLiteral( "Raster" ) } );
77     block->setValid( false );
78     block->setIsNoData();
79     return block.release();
80   }
81 
82   const double xRes = boundingBox.width() / width;
83   const double yRes = boundingBox.height() / height;
84   double tmpXRes, tmpYRes;
85   double providerXRes = 0;
86   double providerYRes = 0;
87   if ( capabilities() & Size )
88   {
89     providerXRes = extent().width() / xSize();
90     providerYRes = extent().height() / ySize();
91     tmpXRes = std::max( providerXRes, xRes );
92     tmpYRes = std::max( providerYRes, yRes );
93     if ( qgsDoubleNear( tmpXRes, xRes ) ) tmpXRes = xRes;
94     if ( qgsDoubleNear( tmpYRes, yRes ) ) tmpYRes = yRes;
95   }
96   else
97   {
98     tmpXRes = xRes;
99     tmpYRes = yRes;
100   }
101 
102   if ( tmpExtent != boundingBox ||
103        tmpXRes > xRes || tmpYRes > yRes )
104   {
105     // Read smaller extent or lower resolution
106 
107     if ( !extent().contains( boundingBox ) )
108     {
109       const QRect subRect = QgsRasterBlock::subRect( boundingBox, width, height, extent() );
110       block->setIsNoDataExcept( subRect );
111     }
112 
113     // Calculate row/col limits (before tmpExtent is aligned)
114     const int fromRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMaximum() ) / yRes );
115     const int toRow = std::round( ( boundingBox.yMaximum() - tmpExtent.yMinimum() ) / yRes ) - 1;
116     const int fromCol = std::round( ( tmpExtent.xMinimum() - boundingBox.xMinimum() ) / xRes );
117     const int toCol = std::round( ( tmpExtent.xMaximum() - boundingBox.xMinimum() ) / xRes ) - 1;
118 
119     QgsDebugMsgLevel( QStringLiteral( "fromRow = %1 toRow = %2 fromCol = %3 toCol = %4" ).arg( fromRow ).arg( toRow ).arg( fromCol ).arg( toCol ), 4 );
120 
121     if ( fromRow < 0 || fromRow >= height || toRow < 0 || toRow >= height ||
122          fromCol < 0 || fromCol >= width || toCol < 0 || toCol >= width )
123     {
124       // Should not happen
125       QgsDebugMsg( QStringLiteral( "Row or column limits out of range" ) );
126       block->setError( { tr( "Row or column limits out of range" ), QStringLiteral( "Raster" ) } );
127       block->setValid( false );
128       return block.release();
129     }
130 
131     // If lower source resolution is used, the extent must be aligned to original
132     // resolution to avoid possible shift due to resampling
133     if ( tmpXRes > xRes )
134     {
135       int col = std::floor( ( tmpExtent.xMinimum() - extent().xMinimum() ) / providerXRes );
136       tmpExtent.setXMinimum( extent().xMinimum() + col * providerXRes );
137       col = std::ceil( ( tmpExtent.xMaximum() - extent().xMinimum() ) / providerXRes );
138       tmpExtent.setXMaximum( extent().xMinimum() + col * providerXRes );
139     }
140     if ( tmpYRes > yRes )
141     {
142       int row = std::floor( ( extent().yMaximum() - tmpExtent.yMaximum() ) / providerYRes );
143       tmpExtent.setYMaximum( extent().yMaximum() - row * providerYRes );
144       row = std::ceil( ( extent().yMaximum() - tmpExtent.yMinimum() ) / providerYRes );
145       tmpExtent.setYMinimum( extent().yMaximum() - row * providerYRes );
146     }
147     const int tmpWidth = std::round( tmpExtent.width() / tmpXRes );
148     const int tmpHeight = std::round( tmpExtent.height() / tmpYRes );
149     tmpXRes = tmpExtent.width() / tmpWidth;
150     tmpYRes = tmpExtent.height() / tmpHeight;
151 
152     QgsDebugMsgLevel( QStringLiteral( "Reading smaller block tmpWidth = %1 height = %2" ).arg( tmpWidth ).arg( tmpHeight ), 4 );
153     QgsDebugMsgLevel( QStringLiteral( "tmpExtent = %1" ).arg( tmpExtent.toString() ), 4 );
154 
155     std::unique_ptr< QgsRasterBlock > tmpBlock = std::make_unique< QgsRasterBlock >( dataType( bandNo ), tmpWidth, tmpHeight );
156     if ( sourceHasNoDataValue( bandNo ) && useSourceNoDataValue( bandNo ) )
157     {
158       tmpBlock->setNoDataValue( sourceNoDataValue( bandNo ) );
159     }
160 
161     if ( !readBlock( bandNo, tmpExtent, tmpWidth, tmpHeight, tmpBlock->bits(), feedback ) )
162     {
163       QgsDebugMsg( QStringLiteral( "Error occurred while reading block" ) );
164       block->setError( { tr( "Error occurred while reading block." ), QStringLiteral( "Raster" ) } );
165       block->setValid( false );
166       block->setIsNoData();
167       return block.release();
168     }
169 
170     const int pixelSize = dataTypeSize( bandNo );
171 
172     const double xMin = boundingBox.xMinimum();
173     const double yMax = boundingBox.yMaximum();
174     const double tmpXMin = tmpExtent.xMinimum();
175     const double tmpYMax = tmpExtent.yMaximum();
176 
177     for ( int row = fromRow; row <= toRow; row++ )
178     {
179       const double y = yMax - ( row + 0.5 ) * yRes;
180       const int tmpRow = std::floor( ( tmpYMax - y ) / tmpYRes );
181 
182       for ( int col = fromCol; col <= toCol; col++ )
183       {
184         const double x = xMin + ( col + 0.5 ) * xRes;
185         const int tmpCol = std::floor( ( x - tmpXMin ) / tmpXRes );
186 
187         if ( tmpRow < 0 || tmpRow >= tmpHeight || tmpCol < 0 || tmpCol >= tmpWidth )
188         {
189           QgsDebugMsg( QStringLiteral( "Source row or column limits out of range" ) );
190           block->setIsNoData(); // so that the problem becomes obvious and fixed
191           block->setError( { tr( "Source row or column limits out of range." ), QStringLiteral( "Raster" ) } );
192           block->setValid( false );
193           return block.release();
194         }
195 
196         const qgssize tmpIndex = static_cast< qgssize >( tmpRow ) * static_cast< qgssize >( tmpWidth ) + tmpCol;
197         const qgssize index = row * static_cast< qgssize >( width ) + col;
198 
199         char *tmpBits = tmpBlock->bits( tmpIndex );
200         char *bits = block->bits( index );
201         if ( !tmpBits )
202         {
203           QgsDebugMsg( QStringLiteral( "Cannot get input block data tmpRow = %1 tmpCol = %2 tmpIndex = %3." ).arg( tmpRow ).arg( tmpCol ).arg( tmpIndex ) );
204           continue;
205         }
206         if ( !bits )
207         {
208           QgsDebugMsg( QStringLiteral( "Cannot set output block data." ) );
209           continue;
210         }
211         memcpy( bits, tmpBits, pixelSize );
212       }
213     }
214   }
215   else
216   {
217     if ( !readBlock( bandNo, boundingBox, width, height, block->bits(), feedback ) )
218     {
219       QgsDebugMsg( QStringLiteral( "Error occurred while reading block" ) );
220       block->setIsNoData();
221       block->setError( { tr( "Error occurred while reading block." ), QStringLiteral( "Raster" ) } );
222       block->setValid( false );
223       return block.release();
224     }
225   }
226 
227   // apply scale and offset
228   block->applyScaleOffset( bandScale( bandNo ), bandOffset( bandNo ) );
229   // apply user no data values
230   block->applyNoDataValues( userNoDataValues( bandNo ) );
231   return block.release();
232 }
233 
QgsRasterDataProvider()234 QgsRasterDataProvider::QgsRasterDataProvider()
235   : QgsDataProvider( QString(), QgsDataProvider::ProviderOptions(), QgsDataProvider::ReadFlags() )
236   , QgsRasterInterface( nullptr )
237   , mTemporalCapabilities( std::make_unique< QgsRasterDataProviderTemporalCapabilities >() )
238 {
239 
240 }
241 
QgsRasterDataProvider(const QString & uri,const ProviderOptions & options,QgsDataProvider::ReadFlags flags)242 QgsRasterDataProvider::QgsRasterDataProvider( const QString &uri, const ProviderOptions &options,
243     QgsDataProvider::ReadFlags flags )
244   : QgsDataProvider( uri, options, flags )
245   , QgsRasterInterface( nullptr )
246   , mTemporalCapabilities( std::make_unique< QgsRasterDataProviderTemporalCapabilities >() )
247 {
248 }
249 
providerCapabilities() const250 QgsRasterDataProvider::ProviderCapabilities QgsRasterDataProvider::providerCapabilities() const
251 {
252   return QgsRasterDataProvider::NoProviderCapabilities;
253 }
254 
colorInterpretation(int bandNo) const255 int QgsRasterDataProvider::colorInterpretation( int bandNo ) const
256 {
257   Q_UNUSED( bandNo )
258   return QgsRaster::UndefinedColorInterpretation;
259 }
260 
261 //
262 //Random Static convenience function
263 //
264 /////////////////////////////////////////////////////////
265 
266 // TODO
267 // (WMS) IdentifyFormatFeature is not consistent with QgsRaster::IdentifyFormatValue.
268 // IdentifyFormatHtml: better error reporting
identify(const QgsPointXY & point,QgsRaster::IdentifyFormat format,const QgsRectangle & boundingBox,int width,int height,int)269 QgsRasterIdentifyResult QgsRasterDataProvider::identify( const QgsPointXY &point, QgsRaster::IdentifyFormat format, const QgsRectangle &boundingBox, int width, int height, int /*dpi*/ )
270 {
271   QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
272   QMap<int, QVariant> results;
273 
274   if ( format != QgsRaster::IdentifyFormatValue || !( capabilities() & IdentifyValue ) )
275   {
276     QgsDebugMsg( QStringLiteral( "Format not supported" ) );
277     return QgsRasterIdentifyResult( ERR( tr( "Format not supported" ) ) );
278   }
279 
280   if ( !extent().contains( point ) )
281   {
282     // Outside the raster
283     for ( int bandNo = 1; bandNo <= bandCount(); bandNo++ )
284     {
285       results.insert( bandNo, QVariant() );
286     }
287     return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results );
288   }
289 
290   QgsRectangle finalExtent = boundingBox;
291   if ( finalExtent.isEmpty() )
292     finalExtent = extent();
293 
294   if ( width == 0 )
295   {
296     width = capabilities() & Size ? xSize() : 1000;
297   }
298   if ( height == 0 )
299   {
300     height = capabilities() & Size ? ySize() : 1000;
301   }
302 
303   // Calculate the row / column where the point falls
304   const double xres = ( finalExtent.width() ) / width;
305   const double yres = ( finalExtent.height() ) / height;
306 
307   const int col = static_cast< int >( std::floor( ( point.x() - finalExtent.xMinimum() ) / xres ) );
308   const int row = static_cast< int >( std::floor( ( finalExtent.yMaximum() - point.y() ) / yres ) );
309 
310   const double xMin = finalExtent.xMinimum() + col * xres;
311   const double xMax = xMin + xres;
312   const double yMax = finalExtent.yMaximum() - row * yres;
313   const double yMin = yMax - yres;
314   const QgsRectangle pixelExtent( xMin, yMin, xMax, yMax );
315 
316   for ( int i = 1; i <= bandCount(); i++ )
317   {
318     std::unique_ptr< QgsRasterBlock > bandBlock( block( i, pixelExtent, 1, 1 ) );
319 
320     if ( bandBlock )
321     {
322       const double value = bandBlock->value( 0 );
323 
324       results.insert( i, value );
325     }
326     else
327     {
328       results.insert( i, QVariant() );
329     }
330   }
331   return QgsRasterIdentifyResult( QgsRaster::IdentifyFormatValue, results );
332 }
333 
sample(const QgsPointXY & point,int band,bool * ok,const QgsRectangle & boundingBox,int width,int height,int dpi)334 double QgsRasterDataProvider::sample( const QgsPointXY &point, int band,
335                                       bool *ok, const QgsRectangle &boundingBox, int width, int height, int dpi )
336 {
337   if ( ok )
338     *ok = false;
339 
340   const auto res = identify( point, QgsRaster::IdentifyFormatValue, boundingBox, width, height, dpi );
341   const QVariant value = res.results().value( band );
342 
343   if ( !value.isValid() )
344     return std::numeric_limits<double>::quiet_NaN();
345 
346   if ( ok )
347     *ok = true;
348 
349   return value.toDouble( ok );
350 }
351 
lastErrorFormat()352 QString QgsRasterDataProvider::lastErrorFormat()
353 {
354   return QStringLiteral( "text/plain" );
355 }
356 
writeBlock(QgsRasterBlock * block,int band,int xOffset,int yOffset)357 bool QgsRasterDataProvider::writeBlock( QgsRasterBlock *block, int band, int xOffset, int yOffset )
358 {
359   if ( !block )
360     return false;
361   if ( !isEditable() )
362   {
363     QgsDebugMsg( QStringLiteral( "writeBlock() called on read-only provider." ) );
364     return false;
365   }
366   return write( block->bits(), band, block->width(), block->height(), xOffset, yOffset );
367 }
368 
369 // typedef QList<QPair<QString, QString> > *pyramidResamplingMethods_t();
pyramidResamplingMethods(const QString & providerKey)370 QList<QPair<QString, QString> > QgsRasterDataProvider::pyramidResamplingMethods( const QString &providerKey )
371 {
372   QList<QPair<QString, QString> > methods = QgsProviderRegistry::instance()->pyramidResamplingMethods( providerKey );
373   if ( methods.isEmpty() )
374   {
375     QgsDebugMsg( QStringLiteral( "provider pyramidResamplingMethods returned no methods" ) );
376   }
377   return methods;
378 }
379 
hasPyramids()380 bool QgsRasterDataProvider::hasPyramids()
381 {
382   const QList<QgsRasterPyramid> pyramidList = buildPyramidList();
383   return std::any_of( pyramidList.constBegin(), pyramidList.constEnd(), []( QgsRasterPyramid pyramid ) { return pyramid.getExists(); } );
384 }
385 
setUserNoDataValue(int bandNo,const QgsRasterRangeList & noData)386 void QgsRasterDataProvider::setUserNoDataValue( int bandNo, const QgsRasterRangeList &noData )
387 {
388   if ( bandNo >= mUserNoDataValue.size() )
389   {
390     for ( int i = mUserNoDataValue.size(); i < bandNo; i++ )
391     {
392       mUserNoDataValue.append( QgsRasterRangeList() );
393     }
394   }
395   QgsDebugMsgLevel( QStringLiteral( "set %1 band %1 no data ranges" ).arg( noData.size() ), 4 );
396 
397   if ( mUserNoDataValue[bandNo - 1] != noData )
398   {
399     // Clear statistics
400     mStatistics.erase( std::remove_if( mStatistics.begin(), mStatistics.end(), [bandNo]( const QgsRasterBandStats & stats )
401     {
402       return stats.bandNumber == bandNo;
403     } ), mStatistics.end() );
404     mHistograms.erase( std::remove_if( mHistograms.begin(), mHistograms.end(), [bandNo]( const QgsRasterHistogram & histogram )
405     {
406       return histogram.bandNumber == bandNo;
407     } ), mHistograms.end() );
408     mUserNoDataValue[bandNo - 1] = noData;
409   }
410 }
411 
temporalCapabilities()412 QgsRasterDataProviderTemporalCapabilities *QgsRasterDataProvider::temporalCapabilities()
413 {
414   return mTemporalCapabilities.get();
415 }
416 
temporalCapabilities() const417 const QgsRasterDataProviderTemporalCapabilities *QgsRasterDataProvider::temporalCapabilities() const
418 {
419   return mTemporalCapabilities.get();
420 }
421 
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)422 QgsRasterDataProvider *QgsRasterDataProvider::create( const QString &providerKey,
423     const QString &uri,
424     const QString &format, int nBands,
425     Qgis::DataType type,
426     int width, int height, double *geoTransform,
427     const QgsCoordinateReferenceSystem &crs,
428     const QStringList &createOptions )
429 {
430   QgsRasterDataProvider *ret = QgsProviderRegistry::instance()->createRasterDataProvider(
431                                  providerKey,
432                                  uri, format,
433                                  nBands, type, width,
434                                  height, geoTransform, crs, createOptions );
435   if ( !ret )
436   {
437     QgsDebugMsg( "Cannot resolve 'createRasterDataProviderFunction' function in " + providerKey + " provider" );
438   }
439 
440   // TODO: it would be good to return invalid QgsRasterDataProvider
441   // with QgsError set, but QgsRasterDataProvider has pure virtual methods
442 
443   return ret;
444 }
445 
identifyFormatName(QgsRaster::IdentifyFormat format)446 QString QgsRasterDataProvider::identifyFormatName( QgsRaster::IdentifyFormat format )
447 {
448   switch ( format )
449   {
450     case QgsRaster::IdentifyFormatValue:
451       return QStringLiteral( "Value" );
452     case QgsRaster::IdentifyFormatText:
453       return QStringLiteral( "Text" );
454     case QgsRaster::IdentifyFormatHtml:
455       return QStringLiteral( "Html" );
456     case QgsRaster::IdentifyFormatFeature:
457       return QStringLiteral( "Feature" );
458     default:
459       return QStringLiteral( "Undefined" );
460   }
461 }
462 
identifyFormatLabel(QgsRaster::IdentifyFormat format)463 QString QgsRasterDataProvider::identifyFormatLabel( QgsRaster::IdentifyFormat format )
464 {
465   switch ( format )
466   {
467     case QgsRaster::IdentifyFormatValue:
468       return tr( "Value" );
469     case QgsRaster::IdentifyFormatText:
470       return tr( "Text" );
471     case QgsRaster::IdentifyFormatHtml:
472       return tr( "Html" );
473     case QgsRaster::IdentifyFormatFeature:
474       return tr( "Feature" );
475     default:
476       return QStringLiteral( "Undefined" );
477   }
478 }
479 
identifyFormatFromName(const QString & formatName)480 QgsRaster::IdentifyFormat QgsRasterDataProvider::identifyFormatFromName( const QString &formatName )
481 {
482   if ( formatName == QLatin1String( "Value" ) ) return QgsRaster::IdentifyFormatValue;
483   if ( formatName == QLatin1String( "Text" ) ) return QgsRaster::IdentifyFormatText;
484   if ( formatName == QLatin1String( "Html" ) ) return QgsRaster::IdentifyFormatHtml;
485   if ( formatName == QLatin1String( "Feature" ) ) return QgsRaster::IdentifyFormatFeature;
486   return QgsRaster::IdentifyFormatUndefined;
487 }
488 
identifyFormatToCapability(QgsRaster::IdentifyFormat format)489 QgsRasterInterface::Capability QgsRasterDataProvider::identifyFormatToCapability( QgsRaster::IdentifyFormat format )
490 {
491   switch ( format )
492   {
493     case QgsRaster::IdentifyFormatValue:
494       return IdentifyValue;
495     case QgsRaster::IdentifyFormatText:
496       return IdentifyText;
497     case QgsRaster::IdentifyFormatHtml:
498       return IdentifyHtml;
499     case QgsRaster::IdentifyFormatFeature:
500       return IdentifyFeature;
501     default:
502       return NoCapabilities;
503   }
504 }
505 
nativeResolutions() const506 QList<double> QgsRasterDataProvider::nativeResolutions() const
507 {
508   return QList< double >();
509 }
510 
ignoreExtents() const511 bool QgsRasterDataProvider::ignoreExtents() const
512 {
513   return false;
514 }
515 
transformCoordinates(const QgsPoint & point,QgsRasterDataProvider::TransformType type)516 QgsPoint QgsRasterDataProvider::transformCoordinates( const QgsPoint &point, QgsRasterDataProvider::TransformType type )
517 {
518   Q_UNUSED( point )
519   Q_UNUSED( type )
520   return QgsPoint();
521 }
522 
userNoDataValuesContains(int bandNo,double value) const523 bool QgsRasterDataProvider::userNoDataValuesContains( int bandNo, double value ) const
524 {
525   const QgsRasterRangeList rangeList = mUserNoDataValue.value( bandNo - 1 );
526   return QgsRasterRange::contains( value, rangeList );
527 }
528 
copyBaseSettings(const QgsRasterDataProvider & other)529 void QgsRasterDataProvider::copyBaseSettings( const QgsRasterDataProvider &other )
530 {
531   mDpi = other.mDpi;
532   mSrcNoDataValue = other.mSrcNoDataValue;
533   mSrcHasNoDataValue = other.mSrcHasNoDataValue;
534   mUseSrcNoDataValue = other.mUseSrcNoDataValue;
535   mUserNoDataValue = other.mUserNoDataValue;
536   mExtent = other.mExtent;
537   mProviderResamplingEnabled = other.mProviderResamplingEnabled;
538   mZoomedInResamplingMethod = other.mZoomedInResamplingMethod;
539   mZoomedOutResamplingMethod = other.mZoomedOutResamplingMethod;
540   mMaxOversampling = other.mMaxOversampling;
541 
542   // copy temporal properties
543   if ( mTemporalCapabilities && other.mTemporalCapabilities )
544   {
545     *mTemporalCapabilities = *other.mTemporalCapabilities;
546   }
547 }
548 
resamplingMethodFromString(const QString & str)549 static QgsRasterDataProvider::ResamplingMethod resamplingMethodFromString( const QString &str )
550 {
551   if ( str == QLatin1String( "bilinear" ) )
552   {
553     return QgsRasterDataProvider::ResamplingMethod::Bilinear;
554   }
555   else if ( str == QLatin1String( "cubic" ) )
556   {
557     return QgsRasterDataProvider::ResamplingMethod::Cubic;
558   }
559   else if ( str == QLatin1String( "cubicSpline" ) )
560   {
561     return QgsRasterDataProvider::ResamplingMethod::CubicSpline;
562   }
563   else if ( str == QLatin1String( "lanczos" ) )
564   {
565     return QgsRasterDataProvider::ResamplingMethod::Lanczos;
566   }
567   else if ( str == QLatin1String( "average" ) )
568   {
569     return QgsRasterDataProvider::ResamplingMethod::Average;
570   }
571   else if ( str == QLatin1String( "mode" ) )
572   {
573     return QgsRasterDataProvider::ResamplingMethod::Mode;
574   }
575   else if ( str == QLatin1String( "gauss" ) )
576   {
577     return QgsRasterDataProvider::ResamplingMethod::Gauss;
578   }
579   return  QgsRasterDataProvider::ResamplingMethod::Nearest;
580 }
581 
readXml(const QDomElement & filterElem)582 void QgsRasterDataProvider::readXml( const QDomElement &filterElem )
583 {
584   if ( filterElem.isNull() )
585   {
586     return;
587   }
588 
589   const QDomElement resamplingElement = filterElem.firstChildElement( QStringLiteral( "resampling" ) );
590   if ( !resamplingElement.isNull() )
591   {
592     setMaxOversampling( resamplingElement.attribute( QStringLiteral( "maxOversampling" ), QStringLiteral( "2.0" ) ).toDouble() );
593     setZoomedInResamplingMethod( resamplingMethodFromString( resamplingElement.attribute( QStringLiteral( "zoomedInResamplingMethod" ) ) ) );
594     setZoomedOutResamplingMethod( resamplingMethodFromString( resamplingElement.attribute( QStringLiteral( "zoomedOutResamplingMethod" ) ) ) );
595     enableProviderResampling( resamplingElement.attribute( QStringLiteral( "enabled" ) ) == QLatin1String( "true" ) );
596   }
597 }
598 
resamplingMethodToString(QgsRasterDataProvider::ResamplingMethod method)599 static QString resamplingMethodToString( QgsRasterDataProvider::ResamplingMethod method )
600 {
601   switch ( method )
602   {
603     case QgsRasterDataProvider::ResamplingMethod::Nearest:
604       return QStringLiteral( "nearestNeighbour" );
605     case QgsRasterDataProvider::ResamplingMethod::Bilinear:
606       return QStringLiteral( "bilinear" );
607     case QgsRasterDataProvider::ResamplingMethod::Cubic:
608       return QStringLiteral( "cubic" );
609     case QgsRasterDataProvider::ResamplingMethod::CubicSpline:
610       return QStringLiteral( "cubicSpline" );
611     case QgsRasterDataProvider::ResamplingMethod::Lanczos:
612       return QStringLiteral( "lanczos" );
613     case QgsRasterDataProvider::ResamplingMethod::Average:
614       return QStringLiteral( "average" );
615     case QgsRasterDataProvider::ResamplingMethod::Mode:
616       return QStringLiteral( "mode" );
617     case QgsRasterDataProvider::ResamplingMethod::Gauss:
618       return QStringLiteral( "gauss" );
619   }
620   // should not happen
621   return QStringLiteral( "nearestNeighbour" );
622 }
623 
writeXml(QDomDocument & doc,QDomElement & parentElem) const624 void QgsRasterDataProvider::writeXml( QDomDocument &doc, QDomElement &parentElem ) const
625 {
626   QDomElement providerElement = doc.createElement( QStringLiteral( "provider" ) );
627   parentElem.appendChild( providerElement );
628 
629   QDomElement resamplingElement = doc.createElement( QStringLiteral( "resampling" ) );
630   providerElement.appendChild( resamplingElement );
631 
632   resamplingElement.setAttribute( QStringLiteral( "enabled" ),
633                                   mProviderResamplingEnabled ? QStringLiteral( "true" ) :  QStringLiteral( "false" ) );
634 
635   resamplingElement.setAttribute( QStringLiteral( "zoomedInResamplingMethod" ),
636                                   resamplingMethodToString( mZoomedInResamplingMethod ) );
637 
638   resamplingElement.setAttribute( QStringLiteral( "zoomedOutResamplingMethod" ),
639                                   resamplingMethodToString( mZoomedOutResamplingMethod ) );
640 
641   resamplingElement.setAttribute( QStringLiteral( "maxOversampling" ),
642                                   QString::number( mMaxOversampling ) );
643 }
644 
colorInterpretationName(int bandNo) const645 QString QgsRasterDataProvider::colorInterpretationName( int bandNo ) const
646 {
647   return colorName( colorInterpretation( bandNo ) );
648 }
649 
decodeVirtualRasterProviderUri(const QString & uri,bool * ok)650 QgsRasterDataProvider::VirtualRasterParameters QgsRasterDataProvider::decodeVirtualRasterProviderUri( const QString &uri, bool *ok )
651 {
652   QUrl url = QUrl::fromPercentEncoding( uri.toUtf8() );
653   const QUrlQuery query( url.query() );
654   VirtualRasterParameters components;
655 
656   if ( ! query.hasQueryItem( QStringLiteral( "crs" ) ) )
657   {
658     QgsDebugMsg( "crs is missing" );
659     if ( ok ) *ok = false;
660     return components;
661   }
662   if ( ! components.crs.createFromString( query.queryItemValue( QStringLiteral( "crs" ) ) ) )
663   {
664     QgsDebugMsg( "failed to create crs" );
665     if ( ok ) *ok = false;
666     return components;
667   }
668 
669 
670   if ( ! query.hasQueryItem( QStringLiteral( "extent" ) ) )
671   {
672     QgsDebugMsg( "extent is missing" );
673     if ( ok ) *ok = false;
674     return components;
675   }
676   QStringList pointValuesList = query.queryItemValue( QStringLiteral( "extent" ) ).split( ',' );
677   if ( pointValuesList.size() != 4 )
678   {
679     QgsDebugMsg( "the extent is not correct" );
680     if ( ok ) *ok = false;
681     return components;
682   }
683   components.extent = QgsRectangle( pointValuesList.at( 0 ).toDouble(), pointValuesList.at( 1 ).toDouble(),
684                                     pointValuesList.at( 2 ).toDouble(), pointValuesList.at( 3 ).toDouble() );
685 
686   if ( ! query.hasQueryItem( QStringLiteral( "width" ) ) )
687   {
688     QgsDebugMsg( "width is missing" );
689     if ( ok ) *ok = false;
690     return components;
691   }
692   bool flagW;
693   components.width = query.queryItemValue( QStringLiteral( "width" ) ).toInt( & flagW );
694   if ( !flagW ||  components.width < 0 )
695   {
696     QgsDebugMsg( "invalid or negative width input" );
697     if ( ok ) *ok = false;
698     return components;
699   }
700 
701   if ( ! query.hasQueryItem( QStringLiteral( "height" ) ) )
702   {
703     QgsDebugMsg( "height is missing" );
704     if ( ok ) *ok = false;
705     return components;
706   }
707   bool flagH;
708   components.height = query.queryItemValue( QStringLiteral( "height" ) ).toInt( & flagH );
709   if ( !flagH ||  components.height < 0 )
710   {
711     QgsDebugMsg( "invalid or negative width input" );
712     if ( ok ) *ok = false;
713     return components;
714   }
715 
716   if ( ! query.hasQueryItem( QStringLiteral( "formula" ) ) )
717   {
718     QgsDebugMsg( "formula is missing" );
719     if ( ok ) *ok = false;
720     return components;
721   }
722   components.formula = query.queryItemValue( QStringLiteral( "formula" ) );
723 
724   for ( const auto &item : query.queryItems() )
725   {
726     if ( !( item.first.mid( item.first.indexOf( ':' ), -1 ) == QLatin1String( ":uri" ) ) )
727     {
728       continue;
729     }
730 
731     VirtualRasterInputLayers rLayer;
732     rLayer.name = item.first.mid( 0, item.first.indexOf( ':' ) );
733     rLayer.uri = query.queryItemValue( item.first );
734     rLayer.provider = query.queryItemValue( item.first.mid( 0, item.first.indexOf( ':' ) ) + QStringLiteral( ":provider" ) );
735 
736     if ( rLayer.uri.isNull() || rLayer.provider.isNull() )
737     {
738       QgsDebugMsg( "One or more raster information are missing" );
739       if ( ok ) *ok = false;
740       return components;
741     }
742 
743     components.rInputLayers.append( rLayer ) ;
744 
745   }
746 
747   if ( ok ) *ok = true;
748   return components;
749 }
750 
encodeVirtualRasterProviderUri(const VirtualRasterParameters & parts)751 QString QgsRasterDataProvider::encodeVirtualRasterProviderUri( const VirtualRasterParameters &parts )
752 {
753   QUrl uri;
754   QUrlQuery query;
755 
756   if ( parts.crs.isValid() )
757   {
758     query.addQueryItem( QStringLiteral( "crs" ), parts.crs.authid() );
759   }
760 
761   if ( ! parts.extent.isNull() )
762   {
763     QString rect = QString( "%1,%2,%3,%4" ).arg( qgsDoubleToString( parts.extent.xMinimum() ), qgsDoubleToString( parts.extent.yMinimum() ),
764                    qgsDoubleToString( parts.extent.xMaximum() ), qgsDoubleToString( parts.extent.yMaximum() ) );
765 
766     query.addQueryItem( QStringLiteral( "extent" ), rect );
767   }
768 
769   query.addQueryItem( QStringLiteral( "width" ), QString::number( parts.width ) );
770 
771   query.addQueryItem( QStringLiteral( "height" ), QString::number( parts.height ) );
772 
773   query.addQueryItem( QStringLiteral( "formula" ), parts.formula );
774 
775   if ( ! parts.rInputLayers.isEmpty() )
776   {
777     for ( const auto &it : parts.rInputLayers )
778     {
779       query.addQueryItem( it.name + QStringLiteral( ":uri" ), it.uri );
780       query.addQueryItem( it.name + QStringLiteral( ":provider" ), it.provider );
781     }
782   }
783   uri.setQuery( query );
784   return QString( QUrl::toPercentEncoding( uri.toEncoded() ) );
785 }
786