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