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