QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
qgsrasterlayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterlayer.cpp - description
3  -------------------
4 begin : Sat Jun 22 2002
5 copyright : (C) 2003 by Tim Sutton, Steve Halasz and Gary E.Sherman
6 email : tim at linfiniti.com
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 #include "qgsapplication.h"
19 #include "qgscolorrampshader.h"
21 #include "qgscoordinatetransform.h"
22 #include "qgsdatasourceuri.h"
23 #include "qgshuesaturationfilter.h"
25 #include "qgslogger.h"
26 #include "qgsmaplayerlegend.h"
27 #include "qgsmaplayerutils.h"
28 #include "qgsmaptopixel.h"
29 #include "qgsmessagelog.h"
31 #include "qgspainting.h"
33 #include "qgspathresolver.h"
35 #include "qgsproviderregistry.h"
36 #include "qgsrasterdataprovider.h"
37 #include "qgsrasterdrawer.h"
38 #include "qgsrasteriterator.h"
39 #include "qgsrasterlayer.h"
40 #include "qgsrasterlayerrenderer.h"
41 #include "qgsrasterprojector.h"
42 #include "qgsrasterrange.h"
45 #include "qgsrastershader.h"
46 #include "qgsreadwritecontext.h"
47 #include "qgsxmlutils.h"
48 #include "qgsrectangle.h"
49 #include "qgsrendercontext.h"
53 #include "qgssettings.h"
54 #include "qgssymbollayerutils.h"
55 #include "qgsgdalprovider.h"
59 #include "qgsruntimeprofiler.h"
60 #include "qgsmaplayerfactory.h"
61 #include "qgsrasterpipe.h"
62 
63 #include <cmath>
64 #include <cstdio>
65 #include <limits>
66 #include <typeinfo>
67 
68 #include <QApplication>
69 #include <QCursor>
70 #include <QDir>
71 #include <QDomElement>
72 #include <QDomNode>
73 #include <QFile>
74 #include <QFileInfo>
75 #include <QFont>
76 #include <QFontMetrics>
77 #include <QFrame>
78 #include <QImage>
79 #include <QLabel>
80 #include <QList>
81 #include <QPainter>
82 #include <QPixmap>
83 #include <QRegularExpression>
84 #include <QSlider>
85 #include <QUrl>
86 
87 #define ERR(message) QGS_ERROR_MESSAGE(message,"Raster layer")
88 
89 const double QgsRasterLayer::SAMPLE_SIZE = 250000;
90 
97 
104 
107  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
108  , TRSTRING_NOT_SET( tr( "Not Set" ) )
109  , mTemporalProperties( new QgsRasterLayerTemporalProperties( this ) )
110  , mPipe( std::make_unique< QgsRasterPipe >() )
111 
112 {
113  init();
114  setValid( false );
115 }
116 
117 QgsRasterLayer::QgsRasterLayer( const QString &uri,
118  const QString &baseName,
119  const QString &providerKey,
120  const LayerOptions &options )
121  : QgsMapLayer( QgsMapLayerType::RasterLayer, baseName, uri )
122  // Constant that signals property not used.
123  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
124  , TRSTRING_NOT_SET( tr( "Not Set" ) )
125  , mTemporalProperties( new QgsRasterLayerTemporalProperties( this ) )
126  , mPipe( std::make_unique< QgsRasterPipe >() )
127 {
129 
130  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
131  setProviderType( providerKey );
132 
133  const QgsDataProvider::ProviderOptions providerOptions { options.transformContext };
134  QgsDataProvider::ReadFlags providerFlags = QgsDataProvider::ReadFlags();
135  if ( options.loadDefaultStyle )
136  {
137  providerFlags |= QgsDataProvider::FlagLoadDefaultStyle;
138  }
139  setDataSource( uri, baseName, providerKey, providerOptions, providerFlags );
140 
141  if ( isValid() )
142  {
143  mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( mDataProvider->temporalCapabilities() );
144  }
145 
146 } // QgsRasterLayer ctor
147 
149 {
150  emit willBeDeleted();
151 
152  setValid( false );
153  // Note: provider and other interfaces are owned and deleted by pipe
154 }
155 
157 {
159  if ( mDataProvider )
160  {
161  options.transformContext = mDataProvider->transformContext();
162  }
163  QgsRasterLayer *layer = new QgsRasterLayer( source(), name(), mProviderKey, options );
164  QgsMapLayer::clone( layer );
165 
166  // do not clone data provider which is the first element in pipe
167  for ( int i = 1; i < mPipe->size(); i++ )
168  {
169  if ( mPipe->at( i ) )
170  layer->pipe()->set( mPipe->at( i )->clone() );
171  }
172  layer->pipe()->setDataDefinedProperties( mPipe->dataDefinedProperties() );
173 
174  return layer;
175 }
176 
178 //
179 // Static Methods and members
180 //
182 
183 bool QgsRasterLayer::isValidRasterFileName( const QString &fileNameQString, QString &retErrMsg )
184 {
185  const bool myIsValid = QgsGdalProvider::isValidRasterFileName( fileNameQString, retErrMsg );
186  return myIsValid;
187 }
188 
189 bool QgsRasterLayer::isValidRasterFileName( QString const &fileNameQString )
190 {
191  QString retErrMsg;
192  return isValidRasterFileName( fileNameQString, retErrMsg );
193 }
194 
195 QDateTime QgsRasterLayer::lastModified( QString const &name )
196 {
197  QgsDebugMsgLevel( "name=" + name, 4 );
198  QDateTime t;
199 
200  const QFileInfo fi( name );
201 
202  // Is it file?
203  if ( !fi.exists() )
204  return t;
205 
206  t = fi.lastModified();
207 
208  QgsDebugMsgLevel( "last modified = " + t.toString(), 4 );
209 
210  return t;
211 }
212 
213 void QgsRasterLayer::setDataProvider( const QString &provider )
214 {
216 }
217 
218 // typedef for the QgsDataProvider class factory
219 typedef QgsDataProvider *classFactoryFunction_t( const QString *, const QgsDataProvider::ProviderOptions &options );
220 
222 //
223 // Non Static Public methods
224 //
226 
228 {
229  if ( !mDataProvider ) return 0;
230  return mDataProvider->bandCount();
231 }
232 
233 QString QgsRasterLayer::bandName( int bandNo ) const
234 {
235  if ( !mDataProvider ) return QString();
236  return mDataProvider->generateBandName( bandNo );
237 }
238 
239 void QgsRasterLayer::setRendererForDrawingStyle( QgsRaster::DrawingStyle drawingStyle )
240 {
241  setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( drawingStyle, mDataProvider ) );
242 }
243 
245 {
246  return mDataProvider;
247 }
248 
250 {
251  return mDataProvider;
252 }
253 
255 {
256  if ( mDataProvider )
257  {
258  mDataProvider->reloadData();
259  }
260 }
261 
263 {
264  return new QgsRasterLayerRenderer( this, rendererContext );
265 }
266 
267 
268 void QgsRasterLayer::draw( QPainter *theQPainter,
269  QgsRasterViewPort *rasterViewPort,
270  const QgsMapToPixel *qgsMapToPixel )
271 {
272  QgsDebugMsgLevel( QStringLiteral( " 3 arguments" ), 4 );
273  QElapsedTimer time;
274  time.start();
275  //
276  //
277  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
278  // so that we can maximise performance of the rendering process. So now we check which drawing
279  // procedure to use :
280  //
281 
282  QgsRasterProjector *projector = mPipe->projector();
283  bool restoreOldResamplingStage = false;
284  const Qgis::RasterResamplingStage oldResamplingState = resamplingStage();
285  // TODO add a method to interface to get provider and get provider
286  // params in QgsRasterProjector
287 
288  if ( projector )
289  {
290  // Force provider resampling if reprojection is needed
291  if ( mDataProvider != nullptr &&
293  rasterViewPort->mSrcCRS != rasterViewPort->mDestCRS &&
294  oldResamplingState != Qgis::RasterResamplingStage::Provider )
295  {
296  restoreOldResamplingStage = true;
298  }
299  projector->setCrs( rasterViewPort->mSrcCRS, rasterViewPort->mDestCRS, rasterViewPort->mTransformContext );
300  }
301 
302  // Drawer to pipe?
303  QgsRasterIterator iterator( mPipe->last() );
304  QgsRasterDrawer drawer( &iterator );
305  drawer.draw( theQPainter, rasterViewPort, qgsMapToPixel );
306 
307  if ( restoreOldResamplingStage )
308  {
309  setResamplingStage( oldResamplingState );
310  }
311 
312  QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
313 }
314 
316 {
317  QgsRasterRenderer *renderer = mPipe->renderer();
318  return renderer ? renderer->legendSymbologyItems() : QList< QPair< QString, QColor > >();;
319 }
320 
322 {
323  if ( !mDataProvider )
324  return QString();
325 
326  const QgsLayerMetadataFormatter htmlFormatter( metadata() );
327  QString myMetadata = QStringLiteral( "<html><head></head>\n<body>\n" );
328 
329  myMetadata += generalHtmlMetadata();
330 
331  // Begin Provider section
332  myMetadata += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) + QStringLiteral( "<table class=\"list-view\">\n" );
333 
334  myMetadata += QStringLiteral( "\n" ) %
335  // Extent
336  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Extent" ) % QStringLiteral( "</td><td>" ) % extent().toString() % QStringLiteral( "</td></tr>\n" ) %
337 
338  // Raster Width
339  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Width" ) % QStringLiteral( "</td><td>" );
340  if ( dataProvider()->capabilities() & QgsRasterDataProvider::Size )
341  myMetadata += QString::number( width() );
342  else
343  myMetadata += tr( "n/a" );
344  myMetadata += QStringLiteral( "</td></tr>\n" ) %
345 
346  // Raster height
347  QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Height" ) + QStringLiteral( "</td><td>" );
348  if ( dataProvider()->capabilities() & QgsRasterDataProvider::Size )
349  myMetadata += QString::number( height() );
350  else
351  myMetadata += tr( "n/a" );
352  myMetadata += QStringLiteral( "</td></tr>\n" ) %
353 
354  // Data type
355  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Data type" ) % QStringLiteral( "</td><td>" );
356  // Just use the first band
357  switch ( mDataProvider->sourceDataType( 1 ) )
358  {
360  myMetadata += tr( "Byte - Eight bit unsigned integer" );
361  break;
363  myMetadata += tr( "UInt16 - Sixteen bit unsigned integer " );
364  break;
366  myMetadata += tr( "Int16 - Sixteen bit signed integer " );
367  break;
369  myMetadata += tr( "UInt32 - Thirty two bit unsigned integer " );
370  break;
372  myMetadata += tr( "Int32 - Thirty two bit signed integer " );
373  break;
375  myMetadata += tr( "Float32 - Thirty two bit floating point " );
376  break;
378  myMetadata += tr( "Float64 - Sixty four bit floating point " );
379  break;
381  myMetadata += tr( "CInt16 - Complex Int16 " );
382  break;
384  myMetadata += tr( "CInt32 - Complex Int32 " );
385  break;
387  myMetadata += tr( "CFloat32 - Complex Float32 " );
388  break;
390  myMetadata += tr( "CFloat64 - Complex Float64 " );
391  break;
392  default:
393  myMetadata += tr( "Could not determine raster data type." );
394  }
395  myMetadata += QStringLiteral( "</td></tr>\n" ) %
396 
397  // Insert provider-specific (e.g. WMS-specific) metadata
398  mDataProvider->htmlMetadata() %
399 
400  // End Provider section
401  QStringLiteral( "</table>\n<br><br>" );
402 
403  // CRS
404  myMetadata += crsHtmlMetadata();
405 
406  // Identification section
407  myMetadata += QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
408  htmlFormatter.identificationSectionHtml() %
409  QStringLiteral( "<br><br>\n" ) %
410 
411  // extent section
412  QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
413  htmlFormatter.extentSectionHtml( ) %
414  QStringLiteral( "<br><br>\n" ) %
415 
416  // Start the Access section
417  QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
418  htmlFormatter.accessSectionHtml( ) %
419  QStringLiteral( "<br><br>\n" ) %
420 
421  // Bands section
422  QStringLiteral( "</table>\n<br><br><h1>" ) % tr( "Bands" ) % QStringLiteral( "</h1>\n<hr>\n<table class=\"list-view\">\n" ) %
423 
424  // Band count
425  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Band count" ) % QStringLiteral( "</td><td>" ) % QString::number( bandCount() ) % QStringLiteral( "</td></tr>\n" );
426 
427  // Band table
428  myMetadata += QStringLiteral( "</table>\n<br><table width=\"100%\" class=\"tabular-view\">\n" ) %
429  QStringLiteral( "<tr><th>" ) % tr( "Number" ) % QStringLiteral( "</th><th>" ) % tr( "Band" ) % QStringLiteral( "</th><th>" ) % tr( "No-Data" ) % QStringLiteral( "</th><th>" ) %
430  tr( "Min" ) % QStringLiteral( "</th><th>" ) % tr( "Max" ) % QStringLiteral( "</th></tr>\n" );
431 
432  QgsRasterDataProvider *provider = const_cast< QgsRasterDataProvider * >( mDataProvider );
433  for ( int i = 1; i <= bandCount(); i++ )
434  {
435  QString rowClass;
436  if ( i % 2 )
437  rowClass = QStringLiteral( "class=\"odd-row\"" );
438 
439  myMetadata += QStringLiteral( "<tr " ) % rowClass % QStringLiteral( "><td>" ) % QString::number( i ) % QStringLiteral( "</td><td>" ) % bandName( i ) % QStringLiteral( "</td><td>" );
440 
441  if ( dataProvider()->sourceHasNoDataValue( i ) )
442  myMetadata += QString::number( dataProvider()->sourceNoDataValue( i ) );
443  else
444  myMetadata += tr( "n/a" );
445  myMetadata += QLatin1String( "</td>" );
446 
448  {
449  const QgsRasterBandStats myRasterBandStats = provider->bandStatistics( i, QgsRasterBandStats::Min | QgsRasterBandStats::Max, provider->extent(), SAMPLE_SIZE );
450  myMetadata += QStringLiteral( "<td>" ) % QString::number( myRasterBandStats.minimumValue, 'f', 10 ) % QStringLiteral( "</td>" ) %
451  QStringLiteral( "<td>" ) % QString::number( myRasterBandStats.maximumValue, 'f', 10 ) % QStringLiteral( "</td>" );
452  }
453  else
454  {
455  myMetadata += QStringLiteral( "<td>" ) % tr( "n/a" ) % QStringLiteral( "</td><td>" ) % tr( "n/a" ) % QStringLiteral( "</td>" );
456  }
457 
458  myMetadata += QLatin1String( "</tr>\n" );
459  }
460 
461  //close previous bands table
462  myMetadata += QStringLiteral( "</table>\n<br><br>" ) %
463 
464  // Start the contacts section
465  QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
466  htmlFormatter.contactsSectionHtml( ) %
467  QStringLiteral( "<br><br>\n" ) %
468 
469  // Start the links section
470  QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
471  htmlFormatter.linksSectionHtml( ) %
472  QStringLiteral( "<br><br>\n" ) %
473 
474  // Start the history section
475  QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
476  htmlFormatter.historySectionHtml( ) %
477  QStringLiteral( "<br><br>\n" ) %
478 
479  QStringLiteral( "\n</body>\n</html>\n" );
480  return myMetadata;
481 }
482 
483 QPixmap QgsRasterLayer::paletteAsPixmap( int bandNumber )
484 {
485  //TODO: This function should take dimensions
486  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
487 
488  // Only do this for the GDAL provider?
489  // Maybe WMS can do this differently using QImage::numColors and QImage::color()
490  if ( mDataProvider &&
491  mDataProvider->colorInterpretation( bandNumber ) == QgsRaster::PaletteIndex )
492  {
493  QgsDebugMsgLevel( QStringLiteral( "....found paletted image" ), 4 );
494  QgsColorRampShader myShader;
495  const QList<QgsColorRampShader::ColorRampItem> myColorRampItemList = mDataProvider->colorTable( bandNumber );
496  if ( !myColorRampItemList.isEmpty() )
497  {
498  QgsDebugMsgLevel( QStringLiteral( "....got color ramp item list" ), 4 );
499  myShader.setColorRampItemList( myColorRampItemList );
501  // Draw image
502  const int mySize = 100;
503  QPixmap myPalettePixmap( mySize, mySize );
504  QPainter myQPainter( &myPalettePixmap );
505 
506  QImage myQImage = QImage( mySize, mySize, QImage::Format_RGB32 );
507  myQImage.fill( 0 );
508  myPalettePixmap.fill();
509 
510  const double myStep = ( static_cast< double >( myColorRampItemList.size() ) - 1 ) / static_cast< double >( mySize * mySize );
511  double myValue = 0.0;
512  for ( int myRow = 0; myRow < mySize; myRow++ )
513  {
514  QRgb *myLineBuffer = reinterpret_cast< QRgb * >( myQImage.scanLine( myRow ) );
515  for ( int myCol = 0; myCol < mySize; myCol++ )
516  {
517  myValue = myStep * static_cast< double >( myCol + myRow * mySize );
518  int c1, c2, c3, c4;
519  myShader.shade( myValue, &c1, &c2, &c3, &c4 );
520  myLineBuffer[ myCol ] = qRgba( c1, c2, c3, c4 );
521  }
522  }
523 
524  myQPainter.drawImage( 0, 0, myQImage );
525  return myPalettePixmap;
526  }
527  const QPixmap myNullPixmap;
528  return myNullPixmap;
529  }
530  else
531  {
532  //invalid layer was requested
533  const QPixmap myNullPixmap;
534  return myNullPixmap;
535  }
536 }
537 
539 {
540  return mProviderKey;
541 }
542 
544 {
545 // We return one raster pixel per map unit pixel
546 // One raster pixel can have several raster units...
547 
548 // We can only use one of the mGeoTransform[], so go with the
549 // horisontal one.
550 
551  if ( mDataProvider &&
552  mDataProvider->capabilities() & QgsRasterDataProvider::Size && !qgsDoubleNear( mDataProvider->xSize(), 0.0 ) )
553  {
554  return mDataProvider->extent().width() / mDataProvider->xSize();
555  }
556  return 1;
557 }
558 
560 {
561  if ( mDataProvider &&
562  mDataProvider->capabilities() & QgsRasterDataProvider::Size && !qgsDoubleNear( mDataProvider->ySize(), 0.0 ) )
563  {
564  return mDataProvider->extent().height() / mDataProvider->ySize();
565  }
566  return 1;
567 }
568 
569 void QgsRasterLayer::setOpacity( double opacity )
570 {
571  if ( !mPipe->renderer() || mPipe->renderer()->opacity() == opacity )
572  return;
573 
574  mPipe->renderer()->setOpacity( opacity );
575  emit opacityChanged( opacity );
577 }
578 
580 {
581  return mPipe->renderer() ? mPipe->renderer()->opacity() : 1.0;
582 }
583 
584 void QgsRasterLayer::init()
585 {
586  mRasterType = QgsRasterLayer::GrayOrUndefined;
587 
588  whileBlocking( this )->setLegend( QgsMapLayerLegend::defaultRasterLegend( this ) );
589 
590  setRendererForDrawingStyle( QgsRaster::UndefinedDrawingStyle );
591 
592  //Initialize the last view port structure, should really be a class
593  mLastViewPort.mWidth = 0;
594  mLastViewPort.mHeight = 0;
595 }
596 
597 void QgsRasterLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
598 {
599  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
600  setValid( false ); // assume the layer is invalid until we determine otherwise
601 
602  mPipe->remove( mDataProvider ); // deletes if exists
603  mDataProvider = nullptr;
604 
605  // XXX should I check for and possibly delete any pre-existing providers?
606  // XXX How often will that scenario occur?
607 
608  mProviderKey = provider;
609  // set the layer name (uppercase first character)
610  if ( ! mLayerName.isEmpty() ) // XXX shouldn't this happen in parent?
611  {
612  setName( mLayerName );
613  }
614 
615  //mBandCount = 0;
616 
617  std::unique_ptr< QgsScopedRuntimeProfile > profile;
618  if ( QgsApplication::profiler()->groupIsActive( QStringLiteral( "projectload" ) ) )
619  profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Create %1 provider" ).arg( provider ), QStringLiteral( "projectload" ) );
620 
621  mDataProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mProviderKey, mDataSource, options, flags ) );
622  if ( !mDataProvider )
623  {
624  //QgsMessageLog::logMessage( tr( "Cannot instantiate the data provider" ), tr( "Raster" ) );
625  appendError( ERR( tr( "Cannot instantiate the '%1' data provider" ).arg( mProviderKey ) ) );
626  return;
627  }
628  QgsDebugMsgLevel( QStringLiteral( "Data provider created" ), 4 );
629  mDataProvider->setParent( this );
630 
631  // Set data provider into pipe even if not valid so that it is deleted with pipe (with layer)
632  mPipe->set( mDataProvider );
633  if ( !mDataProvider->isValid() )
634  {
635  setError( mDataProvider->error() );
636  appendError( ERR( tr( "Provider is not valid (provider: %1, URI: %2" ).arg( mProviderKey, mDataSource ) ) );
637  return;
638  }
639 
641  {
642  setMetadata( mDataProvider->layerMetadata() );
643  QgsDebugMsgLevel( QStringLiteral( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 );
644  }
645 
646  if ( provider == QLatin1String( "gdal" ) )
647  {
648  // make sure that the /vsigzip or /vsizip is added to uri, if applicable
649  mDataSource = mDataProvider->dataSourceUri();
650  }
651 
653  {
654  // get the extent
655  const QgsRectangle mbr = mDataProvider->extent();
656 
657  // store the extent
658  setExtent( mbr );
659  }
660 
661  // upper case the first letter of the layer name
662  QgsDebugMsgLevel( "mLayerName: " + name(), 4 );
663 
664  // set up the raster drawing style
665  // Do not set any 'sensible' style here, the style is set later
666 
667  // Setup source CRS
668  setCrs( QgsCoordinateReferenceSystem( mDataProvider->crs() ) );
669 
670  QgsDebugMsgLevel( "using wkt:\n" + crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), 4 );
671 
672  //defaults - Needs to be set after the Contrast list has been build
673  //Try to read the default contrast enhancement from the config file
674 
675  //decide what type of layer this is...
676  //TODO Change this to look at the color interp and palette interp to decide which type of layer it is
677  QgsDebugMsgLevel( "bandCount = " + QString::number( mDataProvider->bandCount() ), 4 );
678  QgsDebugMsgLevel( "dataType = " + qgsEnumValueToKey< Qgis::DataType >( mDataProvider->dataType( 1 ) ), 4 );
679  if ( ( mDataProvider->bandCount() > 1 ) )
680  {
681  // handle singleband gray with alpha
682  if ( mDataProvider->bandCount() == 2
683  && ( ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::GrayIndex
684  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::AlphaBand )
685  || ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::AlphaBand
686  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::GrayIndex ) ) )
687  {
688  mRasterType = GrayOrUndefined;
689  }
690  else
691  {
692  mRasterType = Multiband;
693  }
694  }
695  else if ( mDataProvider->dataType( 1 ) == Qgis::DataType::ARGB32
696  || mDataProvider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied )
697  {
698  mRasterType = ColorLayer;
699  }
700  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
701  {
702  mRasterType = Palette;
703  }
704  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
705  {
706  mRasterType = Palette;
707  }
708  else
709  {
710  mRasterType = GrayOrUndefined;
711  }
712 
713  QgsDebugMsgLevel( "mRasterType = " + QString::number( mRasterType ), 4 );
714  if ( mRasterType == ColorLayer )
715  {
716  QgsDebugMsgLevel( "Setting drawing style to SingleBandColorDataStyle " + QString::number( QgsRaster::SingleBandColorDataStyle ), 4 );
717  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
718  }
719  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
720  {
721  setRendererForDrawingStyle( QgsRaster::PalettedColor ); //sensible default
722  }
723  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
724  {
725  setRendererForDrawingStyle( QgsRaster::SingleBandPseudoColor );
726  // Load color table
727  const QList<QgsColorRampShader::ColorRampItem> colorTable = mDataProvider->colorTable( 1 );
729  if ( r )
730  {
731  // TODO: this should go somewhere else
732  QgsRasterShader *shader = new QgsRasterShader();
733  QgsColorRampShader *colorRampShader = new QgsColorRampShader();
735  colorRampShader->setColorRampItemList( colorTable );
736  shader->setRasterShaderFunction( colorRampShader );
737  r->setShader( shader );
738  }
739  }
740  else if ( mRasterType == Multiband )
741  {
742  setRendererForDrawingStyle( QgsRaster::MultiBandColor ); //sensible default
743  }
744  else //GrayOrUndefined
745  {
746  setRendererForDrawingStyle( QgsRaster::SingleBandGray ); //sensible default
747  }
748 
749  // Auto set alpha band
750  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
751  {
752  if ( mDataProvider->colorInterpretation( bandNo ) == QgsRaster::AlphaBand )
753  {
754  if ( auto *lRenderer = mPipe->renderer() )
755  {
756  lRenderer->setAlphaBand( bandNo );
757  }
758  break;
759  }
760  }
761 
762  // brightness filter
764  mPipe->set( brightnessFilter );
765 
766  // hue/saturation filter
768  mPipe->set( hueSaturationFilter );
769 
770  // resampler (must be after renderer)
772  mPipe->set( resampleFilter );
773 
775  {
776  const QgsSettings settings;
777  QString resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedInResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
778  if ( resampling == QLatin1String( "bilinear" ) )
779  {
782  }
783  else if ( resampling == QLatin1String( "cubic" ) )
784  {
787  }
788  resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedOutResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
789  if ( resampling == QLatin1String( "bilinear" ) )
790  {
793  }
794 
795  const double maxOversampling = settings.value( QStringLiteral( "/Raster/defaultOversampling" ), 2.0 ).toDouble();
796  resampleFilter->setMaxOversampling( maxOversampling );
797  mDataProvider->setMaxOversampling( maxOversampling );
798 
800  settings.value( QStringLiteral( "/Raster/defaultEarlyResampling" ), false ).toBool() )
801  {
803  }
804  else
805  {
807  }
808  }
809 
810  // projector (may be anywhere in pipe)
811  QgsRasterProjector *projector = new QgsRasterProjector;
812  mPipe->set( projector );
813 
814  // Set default identify format - use the richest format available
815  const int capabilities = mDataProvider->capabilities();
817  if ( capabilities & QgsRasterInterface::IdentifyHtml )
818  {
819  // HTML is usually richest
820  identifyFormat = QgsRaster::IdentifyFormatHtml;
821  }
822  else if ( capabilities & QgsRasterInterface::IdentifyFeature )
823  {
824  identifyFormat = QgsRaster::IdentifyFormatFeature;
825  }
826  else if ( capabilities & QgsRasterInterface::IdentifyText )
827  {
828  identifyFormat = QgsRaster::IdentifyFormatText;
829  }
830  else if ( capabilities & QgsRasterInterface::IdentifyValue )
831  {
832  identifyFormat = QgsRaster::IdentifyFormatValue;
833  }
834  setCustomProperty( QStringLiteral( "identify/format" ), QgsRasterDataProvider::identifyFormatName( identifyFormat ) );
835 
836  // Store timestamp
837  // TODO move to provider
838  mLastModified = lastModified( mDataSource );
839 
840  // Do a passthrough for the status bar text
841  connect( mDataProvider, &QgsRasterDataProvider::statusChanged, this, &QgsRasterLayer::statusChanged );
842 
843  //mark the layer as valid
844  setValid( true );
845 
846  if ( mDataProvider->supportsSubsetString() )
847  connect( this, &QgsRasterLayer::subsetStringChanged, this, &QgsMapLayer::configChanged, Qt::UniqueConnection );
848  else
850 
851 
852  QgsDebugMsgLevel( QStringLiteral( "exiting." ), 4 );
853 
854 }
855 
856 void QgsRasterLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider,
857  const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
858 {
859  const bool hadRenderer( renderer() );
860 
861  QDomImplementation domImplementation;
862  QDomDocumentType documentType;
863  QString errorMsg;
864 
865  bool loadDefaultStyleFlag = false;
867  {
868  loadDefaultStyleFlag = true;
869  }
870 
871  // Store the original style
872  if ( hadRenderer && ! loadDefaultStyleFlag )
873  {
874  documentType = domImplementation.createDocumentType(
875  QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
876 
877  QDomDocument doc = QDomDocument( documentType );
878  QDomElement styleElem = doc.createElement( QStringLiteral( "qgis" ) );
879  styleElem.setAttribute( QStringLiteral( "version" ), Qgis::version() );
880  const QgsReadWriteContext writeContext;
881  if ( ! writeSymbology( styleElem, doc, errorMsg, writeContext ) )
882  {
883  QgsDebugMsg( QStringLiteral( "Could not store symbology for layer %1: %2" )
884  .arg( name(),
885  errorMsg ) );
886  }
887  else
888  {
889  doc.appendChild( styleElem );
890 
891  mOriginalStyleDocument = doc;
892  mOriginalStyleElement = styleElem;
893  }
894  }
895 
896  if ( mDataProvider )
897  closeDataProvider();
898 
899  init();
900 
901  for ( int i = mPipe->size() - 1; i >= 0; --i )
902  {
903  mPipe->remove( i );
904  }
905 
906  mDataSource = dataSource;
907  mLayerName = baseName;
908 
909  setDataProvider( provider, options, flags );
910 
911  if ( mDataProvider )
912  mDataProvider->setDataSourceUri( mDataSource );
913 
914  if ( isValid() )
915  {
916  // load default style
917  bool defaultLoadedFlag = false;
918  bool restoredStyle = false;
919  if ( loadDefaultStyleFlag )
920  {
921  loadDefaultStyle( defaultLoadedFlag );
922  }
923  else if ( !mOriginalStyleElement.isNull() ) // Restore the style
924  {
925  QgsReadWriteContext readContext;
926  if ( ! readSymbology( mOriginalStyleElement, errorMsg, readContext ) )
927  {
928  QgsDebugMsg( QStringLiteral( "Could not restore symbology for layer %1: %2" )
929  .arg( name() )
930  .arg( errorMsg ) );
931 
932  }
933  else
934  {
935  restoredStyle = true;
936  emit repaintRequested();
938  emit rendererChanged();
939  }
940  }
941 
942  if ( !defaultLoadedFlag && !restoredStyle )
943  {
945  }
946  }
947 }
948 
949 void QgsRasterLayer::closeDataProvider()
950 {
951  setValid( false );
952  mPipe->remove( mDataProvider );
953  mDataProvider = nullptr;
954 }
955 
956 void QgsRasterLayer::computeMinMax( int band,
957  const QgsRasterMinMaxOrigin &mmo,
959  const QgsRectangle &extent,
960  int sampleSize,
961  double &min, double &max )
962 {
963 
964  min = std::numeric_limits<double>::quiet_NaN();
965  max = std::numeric_limits<double>::quiet_NaN();
966  if ( !mDataProvider )
967  return;
968 
969  if ( limits == QgsRasterMinMaxOrigin::MinMax )
970  {
971  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Min | QgsRasterBandStats::Max, extent, sampleSize );
972  // Check if statistics were actually gathered, None means a failure
973  if ( myRasterBandStats.statsGathered == QgsRasterBandStats::Stats::None )
974  {
975  // Best guess we can do
976  switch ( mDataProvider->dataType( band ) )
977  {
979  {
980  myRasterBandStats.minimumValue = 0;
981  myRasterBandStats.maximumValue = 255;
982  break;
983  }
985  {
986  myRasterBandStats.minimumValue = 0;
987  myRasterBandStats.maximumValue = std::numeric_limits<uint16_t>::max();
988  break;
989  }
991  {
992  myRasterBandStats.minimumValue = 0;
993  myRasterBandStats.maximumValue = std::numeric_limits<uint32_t>::max();
994  break;
995  }
998  {
999  myRasterBandStats.minimumValue = std::numeric_limits<int16_t>::lowest();
1000  myRasterBandStats.maximumValue = std::numeric_limits<int16_t>::max();
1001  break;
1002  }
1003  case Qgis::DataType::Int32:
1005  {
1006  myRasterBandStats.minimumValue = std::numeric_limits<int32_t>::lowest();
1007  myRasterBandStats.maximumValue = std::numeric_limits<int32_t>::max();
1008  break;
1009  }
1012  {
1013  myRasterBandStats.minimumValue = std::numeric_limits<float_t>::lowest();
1014  myRasterBandStats.maximumValue = std::numeric_limits<float_t>::max();
1015  break;
1016  }
1019  {
1020  myRasterBandStats.minimumValue = std::numeric_limits<double_t>::lowest();
1021  myRasterBandStats.maximumValue = std::numeric_limits<double_t>::max();
1022  break;
1023  }
1027  {
1028  // Nothing to guess
1029  break;
1030  }
1031  }
1032  }
1033  min = myRasterBandStats.minimumValue;
1034  max = myRasterBandStats.maximumValue;
1035  }
1036  else if ( limits == QgsRasterMinMaxOrigin::StdDev )
1037  {
1038  const QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, extent, sampleSize );
1039  min = myRasterBandStats.mean - ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
1040  max = myRasterBandStats.mean + ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
1041  }
1042  else if ( limits == QgsRasterMinMaxOrigin::CumulativeCut )
1043  {
1044  const double myLower = mmo.cumulativeCutLower();
1045  const double myUpper = mmo.cumulativeCutUpper();
1046  QgsDebugMsgLevel( QStringLiteral( "myLower = %1 myUpper = %2" ).arg( myLower ).arg( myUpper ), 4 );
1047  mDataProvider->cumulativeCut( band, myLower, myUpper, min, max, extent, sampleSize );
1048  }
1049  QgsDebugMsgLevel( QStringLiteral( "band = %1 min = %2 max = %3" ).arg( band ).arg( min ).arg( max ), 4 );
1050 
1051 }
1052 
1054 {
1055  return mDataProvider ? mDataProvider->ignoreExtents() : false;
1056 }
1057 
1059 {
1060  return mTemporalProperties;
1061 }
1062 
1064 {
1066  limits,
1067  extent,
1068  sampleSize,
1069  generateLookupTableFlag,
1070  mPipe->renderer() );
1071 }
1072 
1075  const QgsRectangle &extent,
1076  int sampleSize,
1077  bool generateLookupTableFlag,
1078  QgsRasterRenderer *rasterRenderer )
1079 {
1080  QgsDebugMsgLevel( QStringLiteral( "theAlgorithm = %1 limits = %2 extent.isEmpty() = %3" ).arg( algorithm ).arg( limits ).arg( extent.isEmpty() ), 4 );
1081  if ( !rasterRenderer || !mDataProvider )
1082  {
1083  return;
1084  }
1085 
1086  QList<int> myBands;
1087  QList<QgsContrastEnhancement *> myEnhancements;
1088  QgsRasterMinMaxOrigin myMinMaxOrigin;
1089  QgsRasterRenderer *myRasterRenderer = nullptr;
1090  QgsSingleBandGrayRenderer *myGrayRenderer = nullptr;
1091  QgsSingleBandPseudoColorRenderer *myPseudoColorRenderer = nullptr;
1092  QgsMultiBandColorRenderer *myMultiBandRenderer = nullptr;
1093  const QString rendererType = rasterRenderer->type();
1094  if ( rendererType == QLatin1String( "singlebandgray" ) )
1095  {
1096  myGrayRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer );
1097  if ( !myGrayRenderer )
1098  {
1099  return;
1100  }
1101  myBands << myGrayRenderer->grayBand();
1102  myRasterRenderer = myGrayRenderer;
1103  myMinMaxOrigin = myGrayRenderer->minMaxOrigin();
1104  }
1105  else if ( rendererType == QLatin1String( "multibandcolor" ) )
1106  {
1107  myMultiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer );
1108  if ( !myMultiBandRenderer )
1109  {
1110  return;
1111  }
1112  myBands << myMultiBandRenderer->redBand() << myMultiBandRenderer->greenBand() << myMultiBandRenderer->blueBand();
1113  myRasterRenderer = myMultiBandRenderer;
1114  myMinMaxOrigin = myMultiBandRenderer->minMaxOrigin();
1115  }
1116  else if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
1117  {
1118  myPseudoColorRenderer = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer );
1119  if ( !myPseudoColorRenderer )
1120  {
1121  return;
1122  }
1123  myBands << myPseudoColorRenderer->band();
1124  myRasterRenderer = myPseudoColorRenderer;
1125  myMinMaxOrigin = myPseudoColorRenderer->minMaxOrigin();
1126  }
1127  else
1128  {
1129  return;
1130  }
1131 
1132  const auto constMyBands = myBands;
1133  for ( const int myBand : constMyBands )
1134  {
1135  if ( myBand != -1 )
1136  {
1137  const Qgis::DataType myType = static_cast< Qgis::DataType >( mDataProvider->dataType( myBand ) );
1138  std::unique_ptr<QgsContrastEnhancement> myEnhancement( new QgsContrastEnhancement( static_cast< Qgis::DataType >( myType ) ) );
1139  myEnhancement->setContrastEnhancementAlgorithm( algorithm, generateLookupTableFlag );
1140 
1141  double min;
1142  double max;
1143  computeMinMax( myBand, myMinMaxOrigin, limits, extent, sampleSize, min, max );
1144 
1145  if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
1146  {
1147  myPseudoColorRenderer->setClassificationMin( min );
1148  myPseudoColorRenderer->setClassificationMax( max );
1149  if ( myPseudoColorRenderer->shader() )
1150  {
1151  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( myPseudoColorRenderer->shader()->rasterShaderFunction() );
1152  if ( colorRampShader )
1153  {
1154  colorRampShader->classifyColorRamp( myPseudoColorRenderer->band(), extent, myPseudoColorRenderer->input() );
1155  }
1156  }
1157  }
1158  else
1159  {
1160  myEnhancement->setMinimumValue( min );
1161  myEnhancement->setMaximumValue( max );
1162  myEnhancements.append( myEnhancement.release() );
1163  }
1164  }
1165  else
1166  {
1167  myEnhancements.append( nullptr );
1168  }
1169  }
1170 
1171  if ( rendererType == QLatin1String( "singlebandgray" ) )
1172  {
1173  if ( myEnhancements.first() ) myGrayRenderer->setContrastEnhancement( myEnhancements.takeFirst() );
1174  }
1175  else if ( rendererType == QLatin1String( "multibandcolor" ) )
1176  {
1177  if ( myEnhancements.first() ) myMultiBandRenderer->setRedContrastEnhancement( myEnhancements.takeFirst() );
1178  if ( myEnhancements.first() ) myMultiBandRenderer->setGreenContrastEnhancement( myEnhancements.takeFirst() );
1179  if ( myEnhancements.first() ) myMultiBandRenderer->setBlueContrastEnhancement( myEnhancements.takeFirst() );
1180  }
1181 
1182  //delete all remaining unused enhancements
1183  qDeleteAll( myEnhancements );
1184 
1185  myMinMaxOrigin.setLimits( limits );
1186  if ( extent != QgsRectangle() &&
1187  myMinMaxOrigin.extent() == QgsRasterMinMaxOrigin::WholeRaster )
1188  {
1190  }
1191  if ( myRasterRenderer )
1192  {
1193  myRasterRenderer->setMinMaxOrigin( myMinMaxOrigin );
1194  }
1195 
1196  if ( rasterRenderer == renderer() )
1197  {
1198  emit repaintRequested();
1199  emitStyleChanged();
1200  emit rendererChanged();
1201  }
1202 }
1203 
1205 {
1206  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1207  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1208  const QgsContrastEnhancement *ce = nullptr;
1209  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) ) )
1210  {
1211  ce = singleBandRenderer->contrastEnhancement();
1212  }
1213  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) ) )
1214  {
1215  ce = multiBandRenderer->redContrastEnhancement();
1216  }
1217 
1218  if ( ce )
1219  {
1222  renderer()->minMaxOrigin().limits() == QgsRasterMinMaxOrigin::None ?
1223  QgsRasterMinMaxOrigin::MinMax : renderer()->minMaxOrigin().limits(),
1224  extent,
1225  SAMPLE_SIZE,
1226  true,
1227  renderer() );
1228  }
1229  else
1230  {
1233  if ( defaultContrastEnhancementSettings( myAlgorithm, myLimits ) )
1234  {
1236  myLimits,
1237  extent,
1238  SAMPLE_SIZE,
1239  true,
1240  renderer() );
1241  }
1242  }
1243 }
1244 
1246  const QgsRectangle &extent )
1247 {
1248  if ( mDataProvider &&
1249  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded != extent &&
1250  rasterRenderer->minMaxOrigin().limits() != QgsRasterMinMaxOrigin::None &&
1252  {
1253  refreshRenderer( rasterRenderer, extent );
1254  }
1255 }
1256 
1257 void QgsRasterLayer::refreshRenderer( QgsRasterRenderer *rasterRenderer, const QgsRectangle &extent )
1258 {
1259  if ( mDataProvider )
1260  {
1261  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1262  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1263  QgsSingleBandPseudoColorRenderer *sbpcr = nullptr;
1264  const QgsContrastEnhancement *ce = nullptr;
1265  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer ) ) )
1266  {
1267  ce = singleBandRenderer->contrastEnhancement();
1268  }
1269  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer ) ) )
1270  {
1271  ce = multiBandRenderer->redContrastEnhancement();
1272  }
1273  else if ( ( sbpcr = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer ) ) )
1274  {
1275  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1276  double min;
1277  double max;
1278  computeMinMax( sbpcr->band(),
1279  rasterRenderer->minMaxOrigin(),
1280  rasterRenderer->minMaxOrigin().limits(), extent,
1281  SAMPLE_SIZE, min, max );
1282  sbpcr->setClassificationMin( min );
1283  sbpcr->setClassificationMax( max );
1284 
1285  if ( sbpcr->shader() )
1286  {
1287  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( sbpcr->shader()->rasterShaderFunction() );
1288  if ( colorRampShader )
1289  {
1290  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1291  }
1292  }
1293 
1295  r->setClassificationMin( min );
1296  r->setClassificationMax( max );
1297 
1298  if ( r->shader() )
1299  {
1300  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( r->shader()->rasterShaderFunction() );
1301  if ( colorRampShader )
1302  {
1303  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1304  }
1305  }
1306 
1307  emit repaintRequested();
1308  emitStyleChanged();
1309  emit rendererChanged();
1310  return;
1311  }
1312 
1313  if ( ce &&
1315  {
1316  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1317 
1319  rasterRenderer->minMaxOrigin().limits(),
1320  extent,
1321  SAMPLE_SIZE,
1322  true,
1323  rasterRenderer );
1324 
1325  // Update main renderer so that the legends get updated
1326  if ( singleBandRenderer )
1327  static_cast<QgsSingleBandGrayRenderer *>( renderer() )->setContrastEnhancement( new QgsContrastEnhancement( * singleBandRenderer->contrastEnhancement() ) );
1328  else if ( multiBandRenderer )
1329  {
1330  if ( multiBandRenderer->redContrastEnhancement() )
1331  {
1332  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setRedContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->redContrastEnhancement() ) );
1333  }
1334  if ( multiBandRenderer->greenContrastEnhancement() )
1335  {
1336  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setGreenContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->greenContrastEnhancement() ) );
1337  }
1338  if ( multiBandRenderer->blueContrastEnhancement() )
1339  {
1340  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setBlueContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->blueContrastEnhancement() ) );
1341  }
1342  }
1343 
1344  emitStyleChanged();
1345  emit rendererChanged();
1346  }
1347  }
1348 }
1349 
1351 {
1352  if ( !isValid() || !mDataProvider )
1353  {
1354  QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider" ), 3 );
1355  return customProperty( QStringLiteral( "storedSubsetString" ) ).toString();
1356  }
1357  if ( !mDataProvider->supportsSubsetString() )
1358  {
1359  return QString();
1360  }
1361  return mDataProvider->subsetString();
1362 }
1363 
1364 bool QgsRasterLayer::setSubsetString( const QString &subset )
1365 {
1366  if ( !isValid() || !mDataProvider )
1367  {
1368  QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider or while editing" ), 3 );
1369  setCustomProperty( QStringLiteral( "storedSubsetString" ), subset );
1370  return false;
1371  }
1372 
1373  if ( !mDataProvider->supportsSubsetString() )
1374  {
1375  return false;
1376  }
1377 
1378  if ( subset == mDataProvider->subsetString() )
1379  return true;
1380 
1381  const bool res = mDataProvider->setSubsetString( subset );
1382 
1383  // get the updated data source string from the provider
1384  mDataSource = mDataProvider->dataSourceUri();
1385 
1386  if ( res )
1387  {
1388  setExtent( mDataProvider->extent() );
1389  refreshRenderer( renderer(), extent() );
1390  emit subsetStringChanged();
1391  }
1392 
1393  return res;
1394 }
1395 
1398  QgsRasterMinMaxOrigin::Limits &myLimits ) const
1399 {
1400  const QgsSettings mySettings;
1401 
1402  QString key;
1403  QString defaultAlg;
1404  QString defaultLimits;
1405 
1406  // TODO: we should not test renderer class here, move it somehow to renderers
1407  if ( dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) )
1408  {
1409  key = QStringLiteral( "singleBand" );
1412  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1414  }
1415  else if ( dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) )
1416  {
1417  if ( QgsRasterBlock::typeSize( dataProvider()->sourceDataType( 1 ) ) == 1 )
1418  {
1419  key = QStringLiteral( "multiBandSingleByte" );
1422  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1424  }
1425  else
1426  {
1427  key = QStringLiteral( "multiBandMultiByte" );
1430  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1432  }
1433  }
1434 
1435  if ( key.isEmpty() )
1436  {
1437  QgsDebugMsgLevel( QStringLiteral( "No default contrast enhancement for this drawing style" ), 2 );
1439  myLimits = QgsRasterMinMaxOrigin::limitsFromString( QString() );
1440  return false;
1441  }
1442  QgsDebugMsgLevel( "key = " + key, 4 );
1443 
1444  const QString myAlgorithmString = mySettings.value( "/Raster/defaultContrastEnhancementAlgorithm/" + key, defaultAlg ).toString();
1445  QgsDebugMsgLevel( "myAlgorithmString = " + myAlgorithmString, 4 );
1446 
1447  myAlgorithm = QgsContrastEnhancement::contrastEnhancementAlgorithmFromString( myAlgorithmString );
1448 
1449  const QString myLimitsString = mySettings.value( "/Raster/defaultContrastEnhancementLimits/" + key, defaultLimits ).toString();
1450  QgsDebugMsgLevel( "myLimitsString = " + myLimitsString, 4 );
1451  myLimits = QgsRasterMinMaxOrigin::limitsFromString( myLimitsString );
1452 
1453  return true;
1454 }
1455 
1457 {
1458  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1459 
1462  defaultContrastEnhancementSettings( myAlgorithm, myLimits );
1463 
1464  setContrastEnhancement( myAlgorithm, myLimits );
1465 }
1466 
1467 void QgsRasterLayer::setLayerOrder( QStringList const &layers )
1468 {
1469  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
1470 
1471  if ( mDataProvider )
1472  {
1473  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setLayerOrder(layers)." ), 4 );
1474  mDataProvider->setLayerOrder( layers );
1475  }
1476 
1477 }
1478 
1479 void QgsRasterLayer::setSubLayerVisibility( const QString &name, bool vis )
1480 {
1481 
1482  if ( mDataProvider )
1483  {
1484  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setSubLayerVisibility(name, vis)." ), 4 );
1485  mDataProvider->setSubLayerVisibility( name, vis );
1486  }
1487 
1488 }
1489 
1490 QDateTime QgsRasterLayer::timestamp() const
1491 {
1492  if ( !mDataProvider )
1493  return QDateTime();
1494  return mDataProvider->timestamp();
1495 }
1496 
1498 {
1499  if ( auto *lRenderer = mPipe->renderer() )
1500  {
1501  if ( !lRenderer->accept( visitor ) )
1502  return false;
1503  }
1504  return true;
1505 }
1506 
1507 
1508 bool QgsRasterLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props ) const
1509 {
1510  Q_UNUSED( errorMessage )
1511 
1512  QVariantMap localProps = QVariantMap( props );
1513  if ( hasScaleBasedVisibility() )
1514  {
1515  // TODO: QgsSymbolLayerUtils::mergeScaleDependencies generate SE only and not SLD1.0
1517  }
1518 
1519  if ( isSpatial() ) // TODO: does it make sense this control?
1520  {
1521  // store constraints
1522  QDomElement constraintElem = doc.createElement( QStringLiteral( "sld:LayerFeatureConstraints" ) );
1523  node.appendChild( constraintElem );
1524 
1525  const QDomElement featureTypeConstraintElem = doc.createElement( QStringLiteral( "sld:FeatureTypeConstraint" ) );
1526  constraintElem.appendChild( featureTypeConstraintElem );
1527 
1528  QDomElement userStyleElem = doc.createElement( QStringLiteral( "sld:UserStyle" ) );
1529  node.appendChild( userStyleElem );
1530 
1531  if ( !name().isEmpty() )
1532  {
1533  QDomElement nameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1534  nameElem.appendChild( doc.createTextNode( name() ) );
1535  userStyleElem.appendChild( nameElem );
1536  }
1537 
1538  if ( !abstract().isEmpty() )
1539  {
1540  QDomElement abstractElem = doc.createElement( QStringLiteral( "sld:Abstract" ) );
1541  abstractElem.appendChild( doc.createTextNode( abstract() ) );
1542  userStyleElem.appendChild( abstractElem );
1543  }
1544 
1545  if ( !title().isEmpty() )
1546  {
1547  QDomElement titleElem = doc.createElement( QStringLiteral( "sld:Title" ) );
1548  titleElem.appendChild( doc.createTextNode( title() ) );
1549  userStyleElem.appendChild( titleElem );
1550  }
1551 
1552  QDomElement featureTypeStyleElem = doc.createElement( QStringLiteral( "sld:FeatureTypeStyle" ) );
1553  userStyleElem.appendChild( featureTypeStyleElem );
1554 
1555 #if 0
1556  // TODO: Is there a way to fill it's value with the named style?
1557  // by default <sld:Name> under <sld:FeatureTypeStyle> can have 0 occurrences
1558  // the same happen for tags:
1559  // sld:Title
1560  // sld:Abstract
1561  // sld:FeatureTypeName
1562  // sld:SemanticTypeIdentifier
1563  QDomElement typeStyleNameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1564  featureTypeStyleElem.appendChild( typeStyleNameElem );
1565 #endif
1566 
1567  QDomElement typeStyleRuleElem = doc.createElement( QStringLiteral( "sld:Rule" ) );
1568  featureTypeStyleElem.appendChild( typeStyleRuleElem );
1569 
1570  // add ScaleDenominator tags
1571  if ( hasScaleBasedVisibility() )
1572  {
1573  // note that denominator is the inverted value of scale
1574  if ( maximumScale() != 0.0 )
1575  {
1576  QDomElement minScaleElem = doc.createElement( QStringLiteral( "sld:MinScaleDenominator" ) );
1577  minScaleElem.appendChild( doc.createTextNode( QString::number( maximumScale() ) ) );
1578  typeStyleRuleElem.appendChild( minScaleElem );
1579  }
1580 
1581  QDomElement maxScaleElem = doc.createElement( QStringLiteral( "sld:MaxScaleDenominator" ) );
1582  maxScaleElem.appendChild( doc.createTextNode( QString::number( minimumScale() ) ) );
1583  typeStyleRuleElem.appendChild( maxScaleElem );
1584  }
1585 
1586  // export renderer dependent tags
1587  mPipe->renderer()->toSld( doc, typeStyleRuleElem, localProps );
1588 
1589  // inject raster layer parameters in RasterSymbolizer tag because
1590  // they belongs to rasterlayer and not to the renderer => avoid to
1591  // pass many parameters value via localProps
1592  const QDomNodeList elements = typeStyleRuleElem.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
1593  if ( elements.size() != 0 )
1594  {
1595  // there SHOULD be only one
1596  QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
1597 
1598  // lamda helper used below to reduce code redundancy
1599  auto vendorOptionWriter = [&]( QString name, QString value )
1600  {
1601  QDomElement vendorOptionElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
1602  vendorOptionElem.setAttribute( QStringLiteral( "name" ), name );
1603  vendorOptionElem.appendChild( doc.createTextNode( value ) );
1604  rasterSymbolizerElem.appendChild( vendorOptionElem );
1605  };
1606 
1607  if ( hueSaturationFilter()->invertColors() )
1608  {
1609  vendorOptionWriter( QStringLiteral( "invertColors" ), QString::number( 1 ) );
1610  }
1611 
1612  // add greyScale rendering mode if set
1613  if ( hueSaturationFilter()->grayscaleMode() != QgsHueSaturationFilter::GrayscaleOff )
1614  {
1615  QString property;
1616  switch ( hueSaturationFilter()->grayscaleMode() )
1617  {
1619  property = QStringLiteral( "lightness" );
1620  break;
1622  property = QStringLiteral( "luminosity" );
1623  break;
1625  property = QStringLiteral( "average" );
1626  break;
1628  // added just to avoid travis fail
1629  break;
1630  }
1631  if ( !property.isEmpty() )
1632  vendorOptionWriter( QStringLiteral( "grayScale" ), property );
1633  }
1634 
1635  // add Hue, Saturation and Lighting values in props is Hue filter is set
1636  if ( hueSaturationFilter() && hueSaturationFilter()->colorizeOn() )
1637  {
1638  vendorOptionWriter( QStringLiteral( "colorizeOn" ), QString::number( hueSaturationFilter()->colorizeOn() ) );
1639  vendorOptionWriter( QStringLiteral( "colorizeRed" ), QString::number( hueSaturationFilter()->colorizeColor().red() ) );
1640  vendorOptionWriter( QStringLiteral( "colorizeGreen" ), QString::number( hueSaturationFilter()->colorizeColor().green() ) );
1641  vendorOptionWriter( QStringLiteral( "colorizeBlue" ), QString::number( hueSaturationFilter()->colorizeColor().blue() ) );
1642  if ( hueSaturationFilter()->colorizeStrength() != 100.0 )
1643  vendorOptionWriter( QStringLiteral( "colorizeStrength" ), QString::number( hueSaturationFilter()->colorizeStrength() / 100.0 ) );
1644  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( hueSaturationFilter()->colorizeColor().saturationF() ) );
1645  }
1646  else
1647  {
1648  // saturation != 0 (default value)
1649  if ( hueSaturationFilter()->saturation() != 0 )
1650  {
1651  // normlize value [-100:100] -> [0:1]
1652  const int s = hueSaturationFilter()->saturation();
1653  const double sF = ( s - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1654  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( sF ) );
1655  }
1656  }
1657 
1658  // brightness != 0 (default value)
1659  if ( brightnessFilter()->brightness() != 0 )
1660  {
1661  // normalize value [-255:255] -> [0:1]
1662  const int b = brightnessFilter()->brightness();
1663  const double bF = ( b - ( -255.0 ) ) / ( 255.0 - ( -255.0 ) );
1664  vendorOptionWriter( QStringLiteral( "brightness" ), QString::number( bF ) );
1665  }
1666 
1667  // contrast != 0 (default value)
1668  if ( brightnessFilter()->contrast() != 0 )
1669  {
1670  // normlize value [-100:100] -> [0:1]
1671  const int c = brightnessFilter()->contrast();
1672  const double cF = ( c - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1673  vendorOptionWriter( QStringLiteral( "contrast" ), QString::number( cF ) );
1674  }
1675 
1676 #if 0
1677  // TODO: check if the below mapping formula make sense to map QGIS contrast with SLD gamma value
1678  //
1679  // add SLD1.0 ContrastEnhancement GammaValue = QGIS Contrast
1680  // SLD1.0 does only define 1 as neutral/center double value but does not define range.
1681  // because https://en.wikipedia.org/wiki/Gamma_correction assumed gamma is >0.
1682  // whilst QGIS has a -100/100 values centered in 0 => QGIS contrast value will be scaled in the
1683  // following way:
1684  // [-100,0] => [0,1] and [0,100] => [1,100]
1685  // an alternative could be scale [-100,100] => (0,2]
1686  //
1687  if ( newProps.contains( QStringLiteral( "contrast" ) ) )
1688  {
1689  double gamma;
1690  double contrast = newProps[ QStringLiteral( "contrast" ) ].toDouble();
1691  double percentage = ( contrast - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1692  if ( percentage <= 0.5 )
1693  {
1694  // stretch % to [0-1]
1695  gamma = percentage / 0.5;
1696  }
1697  else
1698  {
1699  gamma = contrast;
1700  }
1701 
1702  QDomElement globalContrastEnhancementElem = doc.createElement( QStringLiteral( "sld:ContrastEnhancement" ) );
1703  rasterSymolizerElem.appendChild( globalContrastEnhancementElem );
1704 
1705  QDomElement gammaValueElem = doc.createElement( QStringLiteral( "sld:GammaValue" ) );
1706  gammaValueElem.appendChild( doc.createTextNode( QString::number( gamma ) ) );
1707  globalContrastEnhancementElem.appendChild( gammaValueElem );
1708  }
1709 #endif
1710  }
1711  }
1712  return true;
1713 }
1714 
1715 
1717 {
1718  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1719  if ( !renderer )
1720  {
1721  return;
1722  }
1723 
1724  mPipe->set( renderer );
1725  emit rendererChanged();
1726  emitStyleChanged();
1727 }
1728 
1730 {
1731  return mPipe->renderer();
1732 }
1733 
1735 {
1736  return mPipe->resampleFilter();
1737 }
1738 
1740 {
1741  return mPipe->brightnessFilter();
1742 }
1743 
1745 {
1746  return mPipe->hueSaturationFilter();
1747 }
1748 
1749 void QgsRasterLayer::showStatusMessage( QString const &message )
1750 {
1751  // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
1752 
1753  // Pass-through
1754  // TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
1755  emit statusChanged( message );
1756 }
1757 
1759 {
1760  if ( mDataProvider )
1761  mDataProvider->setTransformContext( transformContext );
1763 }
1764 
1765 QStringList QgsRasterLayer::subLayers() const
1766 {
1767  if ( ! mDataProvider )
1768  return QStringList();
1769  return mDataProvider->subLayers();
1770 }
1771 
1772 // this function should be used when rendering with the MTR engine introduced in 2.3, as QPixmap is not thread safe (see bug #9626)
1773 // note: previewAsImage and previewAsPixmap should use a common low-level fct QgsRasterLayer::previewOnPaintDevice( QSize size, QColor bgColor, QPaintDevice &device )
1774 QImage QgsRasterLayer::previewAsImage( QSize size, const QColor &bgColor, QImage::Format format )
1775 {
1776  QImage image( size, format );
1777 
1778  if ( ! isValid( ) )
1779  return QImage();
1780 
1781  if ( image.format() == QImage::Format_Indexed8 )
1782  {
1783  image.setColor( 0, bgColor.rgba() );
1784  image.fill( 0 ); //defaults to white, set to transparent for rendering on a map
1785  }
1786  else
1787  {
1788  image.fill( bgColor );
1789  }
1790 
1791  QgsRasterViewPort *rasterViewPort = new QgsRasterViewPort();
1792 
1793  double mapUnitsPerPixel;
1794  double x = 0.0;
1795  double y = 0.0;
1796  const QgsRectangle extent = mDataProvider->extent();
1797  if ( extent.width() / extent.height() >= static_cast< double >( image.width() ) / image.height() )
1798  {
1799  mapUnitsPerPixel = extent.width() / image.width();
1800  y = ( image.height() - extent.height() / mapUnitsPerPixel ) / 2;
1801  }
1802  else
1803  {
1804  mapUnitsPerPixel = extent.height() / image.height();
1805  x = ( image.width() - extent.width() / mapUnitsPerPixel ) / 2;
1806  }
1807 
1808  const double pixelWidth = extent.width() / mapUnitsPerPixel;
1809  const double pixelHeight = extent.height() / mapUnitsPerPixel;
1810 
1811  rasterViewPort->mTopLeftPoint = QgsPointXY( x, y );
1812  rasterViewPort->mBottomRightPoint = QgsPointXY( pixelWidth, pixelHeight );
1813  rasterViewPort->mWidth = image.width();
1814  rasterViewPort->mHeight = image.height();
1815 
1816  rasterViewPort->mDrawnExtent = extent;
1817  rasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
1818  rasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
1819 
1820  QgsMapToPixel *mapToPixel = new QgsMapToPixel( mapUnitsPerPixel );
1821 
1822  QPainter *painter = new QPainter( &image );
1823  draw( painter, rasterViewPort, mapToPixel );
1824  delete rasterViewPort;
1825  delete mapToPixel;
1826 
1827  painter->end();
1828  delete painter;
1829 
1830  return image;
1831 }
1832 
1834 //
1835 // Protected methods
1836 //
1838 /*
1839  * \param QDomNode node that will contain the symbology definition for this layer.
1840  * \param errorMessage reference to string that will be updated with any error messages
1841  * \return TRUE in case of success.
1842  */
1843 bool QgsRasterLayer::readSymbology( const QDomNode &layer_node, QString &errorMessage,
1844  QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1845 {
1846  Q_UNUSED( errorMessage )
1847  // TODO: implement categories for raster layer
1848 
1849  QDomElement rasterRendererElem;
1850 
1851  const QDomElement layerElement = layer_node.toElement();
1852  readCommonStyle( layerElement, context, categories );
1853 
1854  // pipe element was introduced in the end of 1.9 development when there were
1855  // already many project files in use so we support 1.9 backward compatibility
1856  // even it was never officially released -> use pipe element if present, otherwise
1857  // use layer node
1858  QDomNode pipeNode = layer_node.firstChildElement( QStringLiteral( "pipe" ) );
1859  if ( pipeNode.isNull() ) // old project
1860  {
1861  pipeNode = layer_node;
1862  }
1863 
1864  //rasterlayerproperties element there -> old format (1.8 and early 1.9)
1865  if ( !layer_node.firstChildElement( QStringLiteral( "rasterproperties" ) ).isNull() )
1866  {
1867  //copy node because layer_node is const
1868  QDomNode layerNodeCopy = layer_node.cloneNode();
1869  QDomDocument doc = layerNodeCopy.ownerDocument();
1870  QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterproperties" ) );
1871  QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
1872  this );
1873  rasterRendererElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1874  QgsDebugMsgLevel( doc.toString(), 4 );
1875  }
1876  else
1877  {
1878  rasterRendererElem = pipeNode.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1879  }
1880 
1881  if ( !rasterRendererElem.isNull() )
1882  {
1883  const QString rendererType = rasterRendererElem.attribute( QStringLiteral( "type" ) );
1884  QgsRasterRendererRegistryEntry rendererEntry;
1885  if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererType, rendererEntry ) )
1886  {
1887  QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
1888  mPipe->set( renderer );
1889  }
1890  }
1891 
1892  //brightness
1894  mPipe->set( brightnessFilter );
1895 
1896  //brightness coefficient
1897  const QDomElement brightnessElem = pipeNode.firstChildElement( QStringLiteral( "brightnesscontrast" ) );
1898  if ( !brightnessElem.isNull() )
1899  {
1900  brightnessFilter->readXml( brightnessElem );
1901  }
1902 
1903  //hue/saturation
1905  mPipe->set( hueSaturationFilter );
1906 
1907  //saturation coefficient
1908  const QDomElement hueSaturationElem = pipeNode.firstChildElement( QStringLiteral( "huesaturation" ) );
1909  if ( !hueSaturationElem.isNull() )
1910  {
1911  hueSaturationFilter->readXml( hueSaturationElem );
1912  }
1913 
1914  //resampler
1916  mPipe->set( resampleFilter );
1917 
1918  //max oversampling
1919  const QDomElement resampleElem = pipeNode.firstChildElement( QStringLiteral( "rasterresampler" ) );
1920  if ( !resampleElem.isNull() )
1921  {
1922  resampleFilter->readXml( resampleElem );
1923  }
1924 
1925  //provider
1926  if ( mDataProvider )
1927  {
1928  const QDomElement providerElem = pipeNode.firstChildElement( QStringLiteral( "provider" ) );
1929  if ( !providerElem.isNull() )
1930  {
1931  mDataProvider->readXml( providerElem );
1932  }
1933  }
1934 
1935  // Resampling stage
1936  const QDomNode resamplingStageElement = pipeNode.namedItem( QStringLiteral( "resamplingStage" ) );
1937  if ( !resamplingStageElement.isNull() )
1938  {
1939  const QDomElement e = resamplingStageElement.toElement();
1940  if ( e.text() == QLatin1String( "provider" ) )
1942  else if ( e.text() == QLatin1String( "resamplingFilter" ) )
1944  }
1945 
1946  // get and set the blend mode if it exists
1947  const QDomNode blendModeNode = layer_node.namedItem( QStringLiteral( "blendMode" ) );
1948  if ( !blendModeNode.isNull() )
1949  {
1950  const QDomElement e = blendModeNode.toElement();
1951  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
1952  }
1953 
1954  const QDomElement elemDataDefinedProperties = layer_node.firstChildElement( QStringLiteral( "pipe-data-defined-properties" ) );
1955  if ( !elemDataDefinedProperties.isNull() )
1956  mPipe->dataDefinedProperties().readXml( elemDataDefinedProperties, QgsRasterPipe::propertyDefinitions() );
1957 
1958  readCustomProperties( layer_node );
1959 
1960  return true;
1961 }
1962 
1963 bool QgsRasterLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1964 {
1965  return readSymbology( node, errorMessage, context, categories );
1966 }
1967 
1968 bool QgsRasterLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1969 {
1970  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1971  // Make sure to read the file first so stats etc are initialized properly!
1972 
1973  //process provider key
1974  const QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
1975 
1976  if ( pkeyNode.isNull() )
1977  {
1978  mProviderKey = QStringLiteral( "gdal" );
1979  }
1980  else
1981  {
1982  const QDomElement pkeyElt = pkeyNode.toElement();
1983  mProviderKey = pkeyElt.text();
1984  if ( mProviderKey.isEmpty() )
1985  {
1986  mProviderKey = QStringLiteral( "gdal" );
1987  }
1988  }
1989 
1990  // Open the raster source based on provider and datasource
1991 
1992  // Go down the raster-data-provider paradigm
1993 
1994  // Collect provider-specific information
1995 
1996  const QDomNode rpNode = layer_node.namedItem( QStringLiteral( "rasterproperties" ) );
1997 
1998  if ( mProviderKey == QLatin1String( "wms" ) )
1999  {
2000  // >>> BACKWARD COMPATIBILITY < 1.9
2001  // The old WMS URI format does not contain all the information, we add them here.
2002  if ( !mDataSource.contains( QLatin1String( "crs=" ) ) && !mDataSource.contains( QLatin1String( "format=" ) ) )
2003  {
2004  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> adding params" ), 4 );
2005  QgsDataSourceUri uri;
2006  uri.setEncodedUri( mDataSource );
2007  QDomElement layerElement = rpNode.firstChildElement( QStringLiteral( "wmsSublayer" ) );
2008  while ( !layerElement.isNull() )
2009  {
2010  // TODO: sublayer visibility - post-0.8 release timeframe
2011 
2012  // collect name for the sublayer
2013  uri.setParam( QStringLiteral( "layers" ), layerElement.namedItem( QStringLiteral( "name" ) ).toElement().text() );
2014 
2015  // collect style for the sublayer
2016  uri.setParam( QStringLiteral( "styles" ), layerElement.namedItem( QStringLiteral( "style" ) ).toElement().text() );
2017 
2018  layerElement = layerElement.nextSiblingElement( QStringLiteral( "wmsSublayer" ) );
2019  }
2020 
2021  // Collect format
2022  uri.setParam( QStringLiteral( "format" ), rpNode.namedItem( QStringLiteral( "wmsFormat" ) ).toElement().text() );
2023 
2024  // WMS CRS URL param should not be mixed with that assigned to the layer.
2025  // In the old WMS URI version there was no CRS and layer crs().authid() was used.
2026  uri.setParam( QStringLiteral( "crs" ), crs().authid() );
2027  mDataSource = uri.encodedUri();
2028  }
2029  // <<< BACKWARD COMPATIBILITY < 1.9
2030  }
2031 
2033  {
2034  const QgsDataProvider::ProviderOptions providerOptions { context.transformContext() };
2035  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2037  {
2039  }
2040  // read extent
2042  {
2043  const QDomNode extentNode = layer_node.namedItem( QStringLiteral( "extent" ) );
2044  if ( !extentNode.isNull() )
2045  {
2046  // get the extent
2047  const QgsRectangle mbr = QgsXmlUtils::readRectangle( extentNode.toElement() );
2048 
2049  // store the extent
2050  setExtent( mbr );
2051 
2052  // skip get extent
2054  }
2055  }
2056  setDataProvider( mProviderKey, providerOptions, flags );
2057  }
2058 
2059  mOriginalStyleElement = layer_node.namedItem( QStringLiteral( "originalStyle" ) ).firstChildElement();
2060  if ( mOriginalStyleElement.isNull() )
2061  mOriginalStyleElement = layer_node.toElement();
2062  mOriginalStyleDocument = layer_node.ownerDocument();
2063 
2064  if ( ! mDataProvider )
2065  {
2067  {
2068  QgsDebugMsg( QStringLiteral( "Raster data provider could not be created for %1" ).arg( mDataSource ) );
2069  }
2070  return false;
2071  }
2072 
2073  QString error;
2074  const bool res = readSymbology( layer_node, error, context );
2075 
2076  // old wms settings we need to correct
2077  if ( res && mProviderKey == QLatin1String( "wms" ) && ( !renderer() || renderer()->type() != QLatin1String( "singlebandcolordata" ) ) )
2078  {
2079  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
2080  }
2081 
2082  // Check timestamp
2083  // This was probably introduced to reload completely raster if data changed and
2084  // reset completely symbology to reflect new data type etc. It creates however
2085  // problems, because user defined symbology is complete lost if data file time
2086  // changed (the content may be the same). See also 6900.
2087 #if 0
2088  QDomNode stampNode = layer_node.namedItem( "timestamp" );
2089  if ( !stampNode.isNull() )
2090  {
2091  QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
2092  // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
2093  if ( stamp < mDataProvider->dataTimestamp() )
2094  {
2095  QgsDebugMsgLevel( QStringLiteral( "data changed, reload provider" ), 3 );
2096  closeDataProvider();
2097  init();
2099  if ( !isValid() ) return false;
2100  }
2101  }
2102 #endif
2103 
2104  // Load user no data value
2105  const QDomElement noDataElement = layer_node.firstChildElement( QStringLiteral( "noData" ) );
2106 
2107  const QDomNodeList noDataBandList = noDataElement.elementsByTagName( QStringLiteral( "noDataList" ) );
2108 
2109  for ( int i = 0; i < noDataBandList.size(); ++i )
2110  {
2111  const QDomElement bandElement = noDataBandList.at( i ).toElement();
2112  bool ok;
2113  const int bandNo = bandElement.attribute( QStringLiteral( "bandNo" ) ).toInt( &ok );
2114  QgsDebugMsgLevel( QStringLiteral( "bandNo = %1" ).arg( bandNo ), 4 );
2115  if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
2116  {
2117  mDataProvider->setUseSourceNoDataValue( bandNo, bandElement.attribute( QStringLiteral( "useSrcNoData" ) ).toInt() );
2118  QgsRasterRangeList myNoDataRangeList;
2119 
2120  const QDomNodeList rangeList = bandElement.elementsByTagName( QStringLiteral( "noDataRange" ) );
2121 
2122  myNoDataRangeList.reserve( rangeList.size() );
2123  for ( int j = 0; j < rangeList.size(); ++j )
2124  {
2125  const QDomElement rangeElement = rangeList.at( j ).toElement();
2126  const QgsRasterRange myNoDataRange( rangeElement.attribute( QStringLiteral( "min" ) ).toDouble(),
2127  rangeElement.attribute( QStringLiteral( "max" ) ).toDouble() );
2128  QgsDebugMsgLevel( QStringLiteral( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ), 4 );
2129  myNoDataRangeList << myNoDataRange;
2130  }
2131  mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
2132  }
2133  }
2134 
2135  readStyleManager( layer_node );
2136 
2137  return res;
2138 }
2139 
2140 bool QgsRasterLayer::writeSymbology( QDomNode &layer_node, QDomDocument &document, QString &errorMessage,
2141  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2142 {
2143  Q_UNUSED( errorMessage )
2144  // TODO: implement categories for raster layer
2145 
2146  QDomElement layerElement = layer_node.toElement();
2147  writeCommonStyle( layerElement, document, context, categories );
2148 
2149  // Store pipe members into pipe element, in future, it will be
2150  // possible to add custom filters into the pipe
2151  QDomElement pipeElement = document.createElement( QStringLiteral( "pipe" ) );
2152 
2153  for ( int i = 0; i < mPipe->size(); i++ )
2154  {
2155  QgsRasterInterface *interface = mPipe->at( i );
2156  if ( !interface ) continue;
2157  interface->writeXml( document, pipeElement );
2158  }
2159 
2160  QDomElement elemDataDefinedProperties = document.createElement( QStringLiteral( "pipe-data-defined-properties" ) );
2161  mPipe->dataDefinedProperties().writeXml( elemDataDefinedProperties, QgsRasterPipe::propertyDefinitions() );
2162  layer_node.appendChild( elemDataDefinedProperties );
2163 
2164  QDomElement resamplingStageElement = document.createElement( QStringLiteral( "resamplingStage" ) );
2165  const QDomText resamplingStageText = document.createTextNode( resamplingStage() == Qgis::RasterResamplingStage::Provider ? QStringLiteral( "provider" ) : QStringLiteral( "resamplingFilter" ) );
2166  resamplingStageElement.appendChild( resamplingStageText );
2167  pipeElement.appendChild( resamplingStageElement );
2168 
2169  layer_node.appendChild( pipeElement );
2170 
2171  if ( !isValid() && !mOriginalStyleElement.isNull() )
2172  {
2173  QDomElement originalStyleElement = document.createElement( QStringLiteral( "originalStyle" ) );
2174  originalStyleElement.appendChild( mOriginalStyleElement );
2175  layer_node.appendChild( originalStyleElement );
2176  }
2177 
2178  // add blend mode node
2179  QDomElement blendModeElement = document.createElement( QStringLiteral( "blendMode" ) );
2180  const QDomText blendModeText = document.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
2181  blendModeElement.appendChild( blendModeText );
2182  layer_node.appendChild( blendModeElement );
2183 
2184  return true;
2185 }
2186 
2187 bool QgsRasterLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage,
2188  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2189 {
2190  return writeSymbology( node, doc, errorMessage, context, categories );
2191 } // bool QgsRasterLayer::writeSymbology
2192 
2193 /*
2194  * virtual
2195  * \note Called by QgsMapLayer::writeXml().
2196  */
2197 bool QgsRasterLayer::writeXml( QDomNode &layer_node,
2198  QDomDocument &document,
2199  const QgsReadWriteContext &context ) const
2200 {
2201  if ( !mDataProvider )
2202  return false;
2203 
2204  // first get the layer element so that we can append the type attribute
2205 
2206  QDomElement mapLayerNode = layer_node.toElement();
2207 
2208  if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
2209  {
2210  QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
2211  return false;
2212  }
2213 
2214  mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::RasterLayer ) );
2215 
2216  // add provider node
2217 
2218  QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
2219  const QDomText providerText = document.createTextNode( mProviderKey );
2220  provider.appendChild( providerText );
2221  layer_node.appendChild( provider );
2222 
2223  // User no data
2224  QDomElement noData = document.createElement( QStringLiteral( "noData" ) );
2225 
2226  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
2227  {
2228  QDomElement noDataRangeList = document.createElement( QStringLiteral( "noDataList" ) );
2229  noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), bandNo );
2230  noDataRangeList.setAttribute( QStringLiteral( "useSrcNoData" ), mDataProvider->useSourceNoDataValue( bandNo ) );
2231 
2232  const auto constUserNoDataValues = mDataProvider->userNoDataValues( bandNo );
2233  for ( const QgsRasterRange &range : constUserNoDataValues )
2234  {
2235  QDomElement noDataRange = document.createElement( QStringLiteral( "noDataRange" ) );
2236 
2237  noDataRange.setAttribute( QStringLiteral( "min" ), QgsRasterBlock::printValue( range.min() ) );
2238  noDataRange.setAttribute( QStringLiteral( "max" ), QgsRasterBlock::printValue( range.max() ) );
2239  noDataRangeList.appendChild( noDataRange );
2240  }
2241 
2242  noData.appendChild( noDataRangeList );
2243 
2244  }
2245  if ( noData.hasChildNodes() )
2246  {
2247  layer_node.appendChild( noData );
2248  }
2249 
2250  writeStyleManager( layer_node, document );
2251 
2252  serverProperties()->writeXml( layer_node, document );
2253 
2254  //write out the symbology
2255  QString errorMsg;
2256  return writeSymbology( layer_node, document, errorMsg, context );
2257 }
2258 
2259 // TODO: this should ideally go to gdal provider (together with most of encodedSource() + decodedSource())
2260 static bool _parseGpkgColons( const QString &src, QString &filename, QString &tablename )
2261 {
2262  // GDAL accepts the following input format: GPKG:filename:table
2263  // (GDAL won't accept quoted filename)
2264 
2265  QStringList lst = src.split( ':' );
2266  if ( lst.count() != 3 && lst.count() != 4 )
2267  return false;
2268 
2269  tablename = lst.last();
2270  if ( lst.count() == 3 )
2271  {
2272  filename = lst[1];
2273  return true;
2274  }
2275  else if ( lst.count() == 4 && lst[1].count() == 1 && ( lst[2][0] == '/' || lst[2][0] == '\\' ) )
2276  {
2277  // a bit of handling to make sure that filename C:\hello.gpkg is parsed correctly
2278  filename = lst[1] + ":" + lst[2];
2279  return true;
2280  }
2281  return false;
2282 }
2283 
2284 
2285 QString QgsRasterLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
2286 {
2287  QString src( source );
2288  bool handled = false;
2289 
2290  // Update path for subdataset
2291  if ( providerType() == QLatin1String( "gdal" ) )
2292  {
2293  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2294  {
2295  // NETCDF:filename:variable
2296  // filename can be quoted with " as it can contain colons
2297  const QRegularExpression netcdfEncodedRegExp( QRegularExpression::anchoredPattern( "NETCDF:(.+):([^:]+)" ) );
2298  const QRegularExpressionMatch match = netcdfEncodedRegExp.match( src );
2299  if ( match.hasMatch() )
2300  {
2301  QString filename = match.captured( 1 );
2302  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2303  filename = filename.mid( 1, filename.length() - 2 );
2304  src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + match.captured( 2 );
2305  handled = true;
2306  }
2307  }
2308  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2309  {
2310  // GPKG:filename:table
2311  QString filename, tablename;
2312  if ( _parseGpkgColons( src, filename, tablename ) )
2313  {
2314  filename = context.pathResolver().writePath( filename );
2315  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2316  handled = true;
2317  }
2318  }
2319  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2320  {
2321  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2322  // filename can be quoted with " as it can contain colons
2323  const QRegularExpression hdf4EncodedRegExp( QRegularExpression::anchoredPattern( "HDF4_SDS:([^:]+):(.+):([^:]+)" ) );
2324  const QRegularExpressionMatch match = hdf4EncodedRegExp.match( src );
2325  if ( match.hasMatch() )
2326  {
2327  QString filename = match.captured( 2 );
2328  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2329  filename = filename.mid( 1, filename.length() - 2 );
2330  src = "HDF4_SDS:" + match.captured( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + match.captured( 3 );
2331  handled = true;
2332  }
2333  }
2334  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2335  {
2336  // HDF5:file_name:subdataset
2337  // filename can be quoted with " as it can contain colons
2338  const QRegularExpression hdf5EncodedRegExp( QRegularExpression::anchoredPattern( "HDF5:(.+):([^:]+)" ) );
2339  const QRegularExpressionMatch match = hdf5EncodedRegExp.match( src );
2340  if ( match.hasMatch() )
2341  {
2342  QString filename = match.captured( 1 );
2343  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2344  filename = filename.mid( 1, filename.length() - 2 );
2345  src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + match.captured( 2 );
2346  handled = true;
2347  }
2348  }
2349  else if ( src.contains( QRegularExpression( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2350  {
2351  // NITF_IM:0:filename
2352  // RADARSAT_2_CALIB:?:filename
2353  const QRegularExpression nitfRadarsatEncodedRegExp( QRegularExpression::anchoredPattern( "([^:]+):([^:]+):(.+)" ) );
2354  const QRegularExpressionMatch match = nitfRadarsatEncodedRegExp.match( src );
2355  if ( match.hasMatch() )
2356  {
2357  src = match.captured( 1 ) + ':' + match.captured( 2 ) + ':' + context.pathResolver().writePath( match.captured( 3 ) );
2358  handled = true;
2359  }
2360  }
2361  }
2362  else if ( providerType() == "wms" )
2363  {
2364  // handle relative paths to XYZ tiles
2365  QgsDataSourceUri uri;
2366  uri.setEncodedUri( src );
2367  const QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2368  if ( srcUrl.isLocalFile() )
2369  {
2370  // relative path will become "file:./x.txt"
2371  const QString relSrcUrl = context.pathResolver().writePath( srcUrl.toLocalFile() );
2372  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2373  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
2374  src = uri.encodedUri();
2375  handled = true;
2376  }
2377  }
2378  else if ( providerType() == "virtualraster" )
2379  {
2380 
2382 
2383  for ( auto &it : decodedVirtualParams.rInputLayers )
2384  {
2385  it.uri = context.pathResolver().writePath( it.uri );
2386  }
2387  src = QgsRasterDataProvider::encodeVirtualRasterProviderUri( decodedVirtualParams ) ;
2388  }
2389 
2390  if ( !handled )
2391  src = context.pathResolver().writePath( src );
2392 
2393  return src;
2394 }
2395 
2396 QString QgsRasterLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
2397 {
2398  QString src( source );
2399 
2400  if ( provider == QLatin1String( "wms" ) )
2401  {
2402  // >>> BACKWARD COMPATIBILITY < 1.9
2403  // For project file backward compatibility we must support old format:
2404  // 1. mode: <url>
2405  // example: http://example.org/wms?
2406  // 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
2407  // example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
2408  // example: featureCount=10,http://example.org/wms?
2409  // example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
2410  // This is modified version of old QgsWmsProvider::parseUri
2411  // The new format has always params crs,format,layers,styles and that params
2412  // should not appear in old format url -> use them to identify version
2413  // XYZ tile layers do not need to contain crs,format params, but they have type=xyz
2414  if ( !src.contains( QLatin1String( "type=" ) ) &&
2415  !src.contains( QLatin1String( "crs=" ) ) && !src.contains( QLatin1String( "format=" ) ) )
2416  {
2417  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> converting to new format" ), 2 );
2418  QgsDataSourceUri uri;
2419  if ( !src.startsWith( QLatin1String( "http:" ) ) )
2420  {
2421  const QStringList parts = src.split( ',' );
2422  QStringListIterator iter( parts );
2423  while ( iter.hasNext() )
2424  {
2425  const QString item = iter.next();
2426  if ( item.startsWith( QLatin1String( "username=" ) ) )
2427  {
2428  uri.setUsername( item.mid( 9 ) );
2429  }
2430  else if ( item.startsWith( QLatin1String( "password=" ) ) )
2431  {
2432  uri.setPassword( item.mid( 9 ) );
2433  }
2434  else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
2435  {
2436  // in < 1.9 tiled= may apper in to variants:
2437  // tiled=width;height - non tiled mode, specifies max width and max height
2438  // tiled=width;height;resolutions-1;resolution2;... - tile mode
2439 
2440  QStringList params = item.mid( 6 ).split( ';' );
2441 
2442  if ( params.size() == 2 ) // non tiled mode
2443  {
2444  uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
2445  uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
2446  }
2447  else if ( params.size() > 2 ) // tiled mode
2448  {
2449  // resolutions are no more needed and size limit is not used for tiles
2450  // we have to tell to the provider however that it is tiled
2451  uri.setParam( QStringLiteral( "tileMatrixSet" ), QString() );
2452  }
2453  }
2454  else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
2455  {
2456  uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
2457  }
2458  else if ( item.startsWith( QLatin1String( "url=" ) ) )
2459  {
2460  uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
2461  }
2462  else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
2463  {
2464  uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
2465  }
2466  }
2467  }
2468  else
2469  {
2470  uri.setParam( QStringLiteral( "url" ), src );
2471  }
2472  src = uri.encodedUri();
2473  // At this point, the URI is obviously incomplete, we add additional params
2474  // in QgsRasterLayer::readXml
2475  }
2476  // <<< BACKWARD COMPATIBILITY < 1.9
2477 
2478  // handle relative paths to XYZ tiles
2479  QgsDataSourceUri uri;
2480  uri.setEncodedUri( src );
2481  const QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2482  if ( srcUrl.isLocalFile() ) // file-based URL? convert to relative path
2483  {
2484  const QString absSrcUrl = context.pathResolver().readPath( srcUrl.toLocalFile() );
2485  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2486  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
2487  src = uri.encodedUri();
2488  }
2489 
2490  }
2491  else
2492  {
2493  bool handled = false;
2494 
2495  if ( provider == QLatin1String( "gdal" ) )
2496  {
2497  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2498  {
2499  // NETCDF:filename:variable
2500  // filename can be quoted with " as it can contain colons
2501  const QRegularExpression netcdfDecodedRegExp( QRegularExpression::anchoredPattern( "NETCDF:(.+):([^:]+)" ) );
2502  const QRegularExpressionMatch match = netcdfDecodedRegExp.match( src );
2503  if ( match.hasMatch() )
2504  {
2505  QString filename = match.captured( 1 );
2506  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2507  filename = filename.mid( 1, filename.length() - 2 );
2508  src = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + match.captured( 2 );
2509  handled = true;
2510  }
2511  }
2512  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2513  {
2514  // GPKG:filename:table
2515  QString filename, tablename;
2516  if ( _parseGpkgColons( src, filename, tablename ) )
2517  {
2518  filename = context.pathResolver().readPath( filename );
2519  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2520  handled = true;
2521  }
2522  }
2523  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2524  {
2525  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2526  // filename can be quoted with " as it can contain colons
2527  const QRegularExpression hdf4DecodedRegExp( QRegularExpression::anchoredPattern( "HDF4_SDS:([^:]+):(.+):([^:]+)" ) );
2528  const QRegularExpressionMatch match = hdf4DecodedRegExp.match( src );
2529  if ( match.hasMatch() )
2530  {
2531  QString filename = match.captured( 2 );
2532  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2533  filename = filename.mid( 1, filename.length() - 2 );
2534  src = "HDF4_SDS:" + match.captured( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + match.captured( 3 );
2535  handled = true;
2536  }
2537  }
2538  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2539  {
2540  // HDF5:file_name:subdataset
2541  // filename can be quoted with " as it can contain colons
2542  const QRegularExpression hdf5DecodedRegExp( QRegularExpression::anchoredPattern( "HDF5:(.+):([^:]+)" ) );
2543  const QRegularExpressionMatch match = hdf5DecodedRegExp.match( src );
2544  if ( match.hasMatch() )
2545  {
2546  QString filename = match.captured( 1 );
2547  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2548  filename = filename.mid( 1, filename.length() - 2 );
2549  src = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + match.captured( 2 );
2550  handled = true;
2551  }
2552  }
2553  else if ( src.contains( QRegularExpression( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2554  {
2555  // NITF_IM:0:filename
2556  // RADARSAT_2_CALIB:?:filename
2557  const QRegularExpression niftRadarsatDecodedRegExp( QRegularExpression::anchoredPattern( "([^:]+):([^:]+):(.+)" ) );
2558  const QRegularExpressionMatch match = niftRadarsatDecodedRegExp.match( src );
2559  if ( match.hasMatch() )
2560  {
2561  src = match.captured( 1 ) + ':' + match.captured( 2 ) + ':' + context.pathResolver().readPath( match.captured( 3 ) );
2562  handled = true;
2563  }
2564  }
2565  }
2566 
2567  if ( provider == QLatin1String( "virtualraster" ) )
2568  {
2570 
2571  for ( auto &it : decodedVirtualParams.rInputLayers )
2572  {
2573  it.uri = context.pathResolver().readPath( it.uri );
2574  }
2575  src = QgsRasterDataProvider::encodeVirtualRasterProviderUri( decodedVirtualParams ) ;
2576  handled = true;
2577  }
2578 
2579  if ( !handled )
2580  src = context.pathResolver().readPath( src );
2581  }
2582 
2583  return src;
2584 }
2585 
2587 {
2588  if ( !mDataProvider ) return 0;
2589  return mDataProvider->xSize();
2590 }
2591 
2593 {
2594  if ( !mDataProvider ) return 0;
2595  return mDataProvider->ySize();
2596 }
2597 
2599 {
2600  mPipe->setResamplingStage( stage );
2601 }
2602 
2604 {
2605  return mPipe->resamplingStage();
2606 }
2607 
2609 //
2610 // Private methods
2611 //
2613 bool QgsRasterLayer::update()
2614 {
2615  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
2616  // Check if data changed
2617  if ( mDataProvider && mDataProvider->dataTimestamp() > mDataProvider->timestamp() )
2618  {
2619  QgsDebugMsgLevel( QStringLiteral( "reload data" ), 4 );
2620  closeDataProvider();
2621  init();
2622  const QgsDataProvider::ProviderOptions providerOptions;
2623  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2625  {
2627  }
2628  setDataProvider( mProviderKey, providerOptions, flags );
2629  emit dataChanged();
2630  }
2631  return isValid();
2632 }
static QString version()
Version string.
Definition: qgis.cpp:285
RasterResamplingStage
Stage at which raster resampling occurs.
Definition: qgis.h:488
@ Provider
Resampling occurs in Provider.
@ ResampleFilter
Resampling occurs in ResamplingFilter.
DataType
Raster data types.
Definition: qgis.h:120
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
static QgsRasterRendererRegistry * rasterRendererRegistry()
Returns the application's raster renderer registry, used for managing raster layer renderers.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
Bilinear Raster Resampler.
Brightness/contrast and gamma correction filter pipe for rasters.
int contrast() const
Returns current contrast level.
int brightness() const
Returns current brightness level.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlphaValue) const override
Generates and new RGB value based on one input value.
@ Interpolated
Interpolates the color between two class breaks linearly.
@ Discrete
Assigns the color of the higher class for every pixel between two class breaks.
void classifyColorRamp(int classes=0, int band=-1, const QgsRectangle &extent=QgsRectangle(), QgsRasterInterface *input=nullptr)
Classify color ramp shader.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom colormap.
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
Manipulates raster or point cloud pixel values so that they enhanceContrast or clip into a specified ...
ContrastEnhancementAlgorithm
This enumerator describes the types of contrast enhancement algorithms that can be used.
@ StretchToMinimumMaximum
Linear histogram.
@ NoEnhancement
Default color scaling algorithm, no scaling is applied.
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
Returns a string to serialize ContrastEnhancementAlgorithm.
static ContrastEnhancementAlgorithm contrastEnhancementAlgorithmFromString(const QString &contrastEnhancementString)
Deserialize ContrastEnhancementAlgorithm.
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
This class represents a coordinate reference system (CRS).
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Contains information about the context in which a coordinate transform is executed.
Cubic Raster Resampler.
Abstract base class for spatial data provider implementations.
virtual void setLayerOrder(const QStringList &layers)
Reorder the list of layer names to be rendered by this provider (in order from bottom to top)
@ FlagLoadDefaultStyle
Reset the layer's style to the default for the datasource.
@ FlagTrustDataSource
Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load ti...
@ SkipGetExtent
Skip the extent from provider.
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the coordinate system for the data source.
virtual QString subsetString() const
Returns the subset definition string (typically sql) currently in use by the layer and used by the pr...
virtual QgsLayerMetadata layerMetadata() const
Returns layer metadata collected from the provider's source.
virtual bool isValid() const =0
Returns true if this is a valid layer.
virtual void setDataSourceUri(const QString &uri)
Set the data source specification.
virtual QgsError error() const
Gets current status error.
virtual bool supportsSubsetString() const
Returns true if the provider supports setting of subset strings.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
virtual bool setSubsetString(const QString &subset, bool updateFeatureCount=true)
Set the subset string used to create a subset of features in the layer.
virtual void reloadData()
Reloads the data from the source for providers with data caches to synchronize, changes in the data s...
virtual void setSubLayerVisibility(const QString &name, bool vis)
Set the visibility of the given sublayer name.
virtual void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets data coordinate transform context to transformContext.
Class for storing the component parts of a RDBMS data source URI (e.g.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
int removeParam(const QString &key)
Removes a generic parameter by key.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
void setUsername(const QString &username)
Sets the username for the URI.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
void setPassword(const QString &password)
Sets the password for the URI.
Color and saturation filter pipe for rasters.
bool invertColors() const
Returns true if the filter inverts colors.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
Class for metadata formatter.
QString linksSectionHtml() const
Formats the "Links" section according to a metadata object.
QString extentSectionHtml(const bool showSpatialExtent=true) const
Formats the "Extents" section according to a metadata object (extent and temporal).
QString contactsSectionHtml() const
Formats the "Contacts" section according to a metadata object.
QString identificationSectionHtml() const
Formats the "Identification" section according to a metadata object.
QString historySectionHtml() const
Formats the "History" section according to a metadata object.
QString accessSectionHtml() const
Formats the "Access" section according to a metadata object.
static QString typeToString(QgsMapLayerType type)
Converts a map layer type to a string value.
static QgsMapLayerLegend * defaultRasterLegend(QgsRasterLayer *rl)
Create new legend implementation for raster layer.
Base class for utility classes that encapsulate information necessary for rendering of map layers.
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves server properties to xml under the layer node.
Base class for storage of map layer temporal properties.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString name
Definition: qgsmaplayer.h:76
void readStyleManager(const QDomNode &layerNode)
Read style manager's configuration (if any). To be called by subclasses.
void setError(const QgsError &error)
Sets error message.
Definition: qgsmaplayer.h:1911
void writeStyleManager(QDomNode &layerNode, QDomDocument &doc) const
Write style manager's configuration (if exists). To be called by subclasses.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QString source() const
Returns the source for the layer.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
void configChanged()
Emitted whenever the configuration is changed.
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
virtual QString loadDefaultStyle(bool &resultFlag)
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QString mLayerName
Name of the layer - used for display.
Definition: qgsmaplayer.h:1928
QString crsHtmlMetadata() const
Returns a HTML fragment containing the layer's CRS metadata, for use in the htmlMetadata() method.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:78
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
Definition: qgsmaplayer.h:423
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
void statusChanged(const QString &status)
Emit a signal with status (e.g. to be caught by QgisApp and display a msg on status bar)
void readCustomProperties(const QDomNode &layerNode, const QString &keyStartsWith=QString())
Read custom properties from project file.
virtual void setMetadata(const QgsLayerMetadata &metadata)
Sets the layer's metadata store.
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
QString mProviderKey
Data provider key (name of the data provider)
Definition: qgsmaplayer.h:1966
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
void rendererChanged()
Signal emitted when renderer is changed.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:307
virtual QgsError error() const
Gets current status error.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
void opacityChanged(double opacity)
Emitted when the layer's opacity is changed, where opacity is a value between 0 (transparent) and 1 (...
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
void dataChanged()
Data of layer changed.
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
void setName(const QString &name)
Set the display name of the layer.
virtual void setExtent(const QgsRectangle &rect)
Sets the extent.
bool isValid
Definition: qgsmaplayer.h:81
void appendError(const QgsErrorMessage &error)
Add error message.
Definition: qgsmaplayer.h:1909
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1925
@ FlagReadExtentFromXml
Read extent from xml and skip get extent from provider.
Definition: qgsmaplayer.h:639
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:638
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:637
void setValid(bool valid)
Sets whether layer is valid or not.
void readCommonStyle(const QDomElement &layerElement, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read style data common to all layer types.
QgsMapLayer::ReadFlags mReadFlags
Read flags. It's up to the subclass to respect these when restoring state from XML.
Definition: qgsmaplayer.h:1971
void setDataSource(const QString &dataSource, const QString &baseName, const QString &provider, bool loadDefaultStyleFlag=false)
Updates the data source of the layer.
double minimumScale() const
Returns the minimum map scale (i.e.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setProviderType(const QString &providerType)
Sets the providerType (provider key)
QString generalHtmlMetadata() const
Returns an HTML fragment containing general metadata information, for use in the htmlMetadata() metho...
void writeCommonStyle(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write style data common to all layer types.
double maximumScale() const
Returns the maximum map scale (i.e.
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
Definition: qgsmaplayer.h:1978
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Renderer for multiband images with the color components.
const QgsContrastEnhancement * redContrastEnhancement() const
Returns the contrast enhancement to use for the red channel.
const QgsContrastEnhancement * blueContrastEnhancement() const
Returns the contrast enhancement to use for the blue channel.
void setGreenContrastEnhancement(QgsContrastEnhancement *ce)
Sets the contrast enhancement to use for the green channel.
void setBlueContrastEnhancement(QgsContrastEnhancement *ce)
Sets the contrast enhancement to use for the blue channel.
void setRedContrastEnhancement(QgsContrastEnhancement *ce)
Sets the contrast enhancement to use for the red channel.
const QgsContrastEnhancement * greenContrastEnhancement() const
Returns the contrast enhancement to use for the green channel.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer.
Definition: qgspainting.h:37
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
A class to represent a 2D point.
Definition: qgspointxy.h:59
static void convertRasterProperties(QDomDocument &doc, QDomNode &parentNode, QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer)
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
The RasterBandStats struct is a container for statistics about a single raster band.
int statsGathered
Collected statistics.
double mean
The mean cell value for the band. NO_DATA values are excluded.
double stdDev
The standard deviation of the cell values.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
static int typeSize(Qgis::DataType dataType) SIP_HOLDGIL
Returns the size in bytes for the specified dataType.
static QString printValue(double value)
Print double value with all necessary significant digits.
Base class for raster data providers.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
static QString encodeVirtualRasterProviderUri(const VirtualRasterParameters &parts)
Encodes the URI starting from the struct .
virtual bool useSourceNoDataValue(int bandNo) const
Returns the source nodata value usage.
static QgsRasterDataProvider::VirtualRasterParameters decodeVirtualRasterProviderUri(const QString &uri, bool *ok=nullptr)
Decodes the URI returning a struct with all the parameters for QgsVirtualRasterProvider class.
Qgis::DataType sourceDataType(int bandNo) const override=0
Returns source data type for the band specified by number, source data type may be shorter than dataT...
virtual void setUseSourceNoDataValue(int bandNo, bool use)
Sets the source nodata value usage.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
virtual QgsRasterDataProvider::ProviderCapabilities providerCapabilities() const
Returns flags containing the supported capabilities of the data provider.
QDateTime timestamp() const override
Time stamp of data source in the moment when data/metadata were loaded by provider.
QgsRectangle extent() const override=0
Returns the extent of the layer.
virtual bool ignoreExtents() const
Returns true if the extents reported by the data provider are not reliable and it's possible that the...
virtual bool setMaxOversampling(double factor)
Sets maximum oversampling factor for zoomed-out operations.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
QStringList subLayers() const override
Returns the sublayers of this layer - useful for providers that manage their own layers,...
static QString identifyFormatName(QgsRaster::IdentifyFormat format)
@ ReadLayerMetadata
Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata(...
@ ProviderHintBenefitsFromResampling
Provider benefits from resampling and should apply user default resampling settings (since QGIS 3....
@ ProviderHintCanPerformProviderResampling
Provider can perform resampling (to be opposed to post rendering resampling) (since QGIS 3....
QDateTime dataTimestamp() const override
Current time stamp of data source.
virtual QString htmlMetadata()=0
Returns metadata in a format suitable for feeding directly into a subset of the GUI raster properties...
virtual int colorInterpretation(int bandNo) const
Returns data type for the band specified by number.
void statusChanged(const QString &) const
Emit a message to be displayed on status bar, usually used by network providers (WMS,...
virtual QgsRasterRangeList userNoDataValues(int bandNo) const
Returns a list of user no data value ranges.
virtual bool setZoomedInResamplingMethod(ResamplingMethod method)
Set resampling method to apply for zoomed-in operations.
QgsRasterDataProviderTemporalCapabilities * temporalCapabilities() override
Returns the provider's temporal capabilities.
@ Bilinear
Bilinear (2x2 kernel) resampling.
@ Cubic
Cubic Convolution Approximation (4x4 kernel) resampling.
virtual void setUserNoDataValue(int bandNo, const QgsRasterRangeList &noData)
virtual bool setZoomedOutResamplingMethod(ResamplingMethod method)
Set resampling method to apply for zoomed-out operations.
The drawing pipe for raster layers.
void draw(QPainter *p, QgsRasterViewPort *viewPort, const QgsMapToPixel *qgsMapToPixel, QgsRasterBlockFeedback *feedback=nullptr)
Draws raster data.
Base class for processing filters like renderers, reprojector, resampler etc.
virtual void cumulativeCut(int bandNo, double lowerCount, double upperCount, double &lowerValue, double &upperValue, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0)
Find values for cumulative pixel count cut.
@ IdentifyValue
Numerical values.
@ IdentifyFeature
WMS GML -> feature.
@ Size
Original data source size (and thus resolution) is known, it is not always available,...
virtual QgsRasterInterface * input() const
Current input.
virtual int xSize() const
Gets raster size.
virtual QString generateBandName(int bandNumber) const
helper function to create zero padded band names
virtual int bandCount() const =0
Gets number of bands.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
virtual bool hasStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0)
Returns true if histogram is available (cached, already calculated).
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
virtual int ySize() const
Iterator for sequentially processing raster cells.
Implementation of threaded rendering for raster layers.
Implementation of map layer temporal properties for raster layers.
void setDefaultsFromDataProviderTemporalCapabilities(const QgsDataProviderTemporalCapabilities *capabilities) override
Sets the layers temporal settings to appropriate defaults based on a provider's temporal capabilities...
Represents a raster layer.
QString htmlMetadata() const override
Obtain a formatted HTML string containing assorted metadata for this layer.
bool defaultContrastEnhancementSettings(QgsContrastEnhancement::ContrastEnhancementAlgorithm &myAlgorithm, QgsRasterMinMaxOrigin::Limits &myLimits) const
Returns default contrast enhancement settings for that type of raster.
void subsetStringChanged()
Emitted when the layer's subset string has changed.
QString encodedSource(const QString &source, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by derived classes to encode provider's specific data source to proje...
Q_DECL_DEPRECATED QgsLegendColorList legendSymbologyItems() const
Returns a list with classification items (Text and color).
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context.
void showStatusMessage(const QString &message)
virtual bool setSubsetString(const QString &subset)
Sets the string (typically sql) used to define a subset of the layer.
QgsRasterResampleFilter * resampleFilter() const
Returns the raster's resample filter.
bool writeSymbology(QDomNode &, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const override
QgsRasterLayer * clone() const override
Returns a new instance equivalent to this one.
void setResamplingStage(Qgis::RasterResamplingStage stage)
Select which stage of the pipe should apply resampling.
Qgis::RasterResamplingStage resamplingStage() const
Returns which stage of the pipe should apply resampling.
void setContrastEnhancement(QgsContrastEnhancement::ContrastEnhancementAlgorithm algorithm, QgsRasterMinMaxOrigin::Limits limits=QgsRasterMinMaxOrigin::MinMax, const QgsRectangle &extent=QgsRectangle(), int sampleSize=QgsRasterLayer::SAMPLE_SIZE, bool generateLookupTableFlag=true)
Set contrast enhancement algorithm.
void refreshContrastEnhancement(const QgsRectangle &extent)
Refresh contrast enhancement with new extent.
int height() const
Returns the height of the (unclipped) raster.
void setOpacity(double opacity) FINAL
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
Q_DECL_DEPRECATED void setDataProvider(const QString &provider)
Set the data provider.
QgsRasterPipe * pipe()
Returns the raster pipe.
static const QgsContrastEnhancement::ContrastEnhancementAlgorithm MULTIPLE_BAND_SINGLE_BYTE_ENHANCEMENT_ALGORITHM
Default enhancement algorithm for multiple band raster of type Byte.
int bandCount() const
Returns the number of bands in this layer.
void refreshRendererIfNeeded(QgsRasterRenderer *rasterRenderer, const QgsRectangle &extent)
Refresh renderer with new extent, if needed.
QgsMapLayerTemporalProperties * temporalProperties() override
Returns the layer's temporal properties.
bool ignoreExtents() const
If the ignoreExtent flag is set, the layer will also render outside the bounding box reported by the ...
QString decodedSource(const QString &source, const QString &provider, const QgsReadWriteContext &context) const override
Called by readLayerXML(), used by derived classes to decode provider's specific data source from proj...
void setSubLayerVisibility(const QString &name, bool vis) override
Set the visibility of the given sublayer name.
virtual QString subsetString() const
Returns the string (typically sql) used to define a subset of the layer.
static const QgsRasterMinMaxOrigin::Limits SINGLE_BAND_MIN_MAX_LIMITS
Default enhancement limits for single band raster.
QgsBrightnessContrastFilter * brightnessFilter() const
Returns the raster's brightness/contrast filter.
static const QgsRasterMinMaxOrigin::Limits MULTIPLE_BAND_MULTI_BYTE_MIN_MAX_LIMITS
Default enhancement limits for multiple band raster of type different from Byte.
bool writeXml(QDomNode &layer_node, QDomDocument &doc, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by children to write state specific to them to project files.
bool writeStyle(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const override
double opacity() const FINAL
Returns the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
void reload() override
Synchronises with changes in the datasource.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the layer.
QImage previewAsImage(QSize size, const QColor &bgColor=Qt::white, QImage::Format format=QImage::Format_ARGB32_Premultiplied)
Draws a preview of the rasterlayer into a QImage.
QStringList subLayers() const override
Returns the sublayers of this layer.
void setDefaultContrastEnhancement()
Sets the default contrast enhancement.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
static const double SAMPLE_SIZE
Default sample size (number of pixels) for estimated statistics/histogram calculation.
static const QgsRasterMinMaxOrigin::Limits MULTIPLE_BAND_SINGLE_BYTE_MIN_MAX_LIMITS
Default enhancement limits for multiple band raster of type Byte.
static const QgsContrastEnhancement::ContrastEnhancementAlgorithm MULTIPLE_BAND_MULTI_BYTE_ENHANCEMENT_ALGORITHM
Default enhancement algorithm for multiple band raster of type different from Byte.
bool readXml(const QDomNode &layer_node, QgsReadWriteContext &context) override
Called by readLayerXML(), used by children to read state specific to them from project files.
QString bandName(int bandNoInt) const
Returns the name of a band given its number.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
static bool isValidRasterFileName(const QString &fileNameQString, QString &retError)
This helper checks to see whether the file name appears to be a valid raster file name.
bool isSpatial() const override
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
QString providerType() const
[ data provider interface ] Which provider is being used for this Raster Layer?
int width() const
Returns the width of the (unclipped) raster.
bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) override
void draw(QPainter *theQPainter, QgsRasterViewPort *myRasterViewPort, const QgsMapToPixel *qgsMapToPixel=nullptr)
This is an overloaded version of the draw() function that is called by both draw() and thumbnailAsPix...
bool writeSld(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props=QVariantMap()) const
Writes the symbology of the layer into the document provided in SLD 1.0.0 format.
static QDateTime lastModified(const QString &name)
Returns time stamp for given file name.
static const QgsContrastEnhancement::ContrastEnhancementAlgorithm SINGLE_BAND_ENHANCEMENT_ALGORITHM
Default enhancement algorithm for single band raster.
~QgsRasterLayer() override
QDateTime timestamp() const override
Time stamp of data source in the moment when data/metadata were loaded by provider.
virtual void setTransformContext(const QgsCoordinateTransformContext &transformContext) override
Sets the coordinate transform context to transformContext.
void setRenderer(QgsRasterRenderer *renderer)
Sets the raster's renderer.
QgsRasterLayer()
Constructor. Provider is not set.
void setLayerOrder(const QStringList &layers) override
Reorders the previously selected sublayers of this layer from bottom to top.
QgsHueSaturationFilter * hueSaturationFilter() const
Returns the raster's hue/saturation filter.
bool readStyle(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) override
QPixmap paletteAsPixmap(int bandNumber=1)
Returns a 100x100 pixmap of the color palette.
This class describes the origin of min/max values.
void setExtent(QgsRasterMinMaxOrigin::Extent extent)
Sets the extent.
static QString limitsString(Limits limits)
Returns a string to serialize Limits.
double cumulativeCutLower() const
Returns the lower bound of cumulative cut method (between 0 and 1).
QgsRasterMinMaxOrigin::Limits limits() const
Returns the raster limits.
double stdDevFactor() const
Returns the factor f so that the min/max range is [ mean - f * stddev , mean + f * stddev ].
void setLimits(QgsRasterMinMaxOrigin::Limits limits)
Sets the limits.
@ UpdatedCanvas
Constantly updated extent of the canvas is used to compute statistics.
@ CurrentCanvas
Current extent of the canvas (at the time of computation) is used to compute statistics.
@ WholeRaster
Whole raster is used to compute statistics.
double cumulativeCutUpper() const
Returns the upper bound of cumulative cut method (between 0 and 1).
Limits
This enumerator describes the limits used to compute min/max values.
@ StdDev
Range is [ mean - stdDevFactor() * stddev, mean + stdDevFactor() * stddev ].
@ MinMax
Real min-max values.
@ CumulativeCut
Range is [ min + cumulativeCutLower() * (max - min), min + cumulativeCutUpper() * (max - min) ].
static Limits limitsFromString(const QString &limits)
Deserialize Limits.
QgsRasterMinMaxOrigin::Extent extent() const
Returns the raster extent.
Contains a pipeline of raster interfaces for sequential raster processing.
Definition: qgsrasterpipe.h:50
bool set(QgsRasterInterface *interface)
Inserts a new known interface in default place or replace interface of the same role if it already ex...
static QgsPropertiesDefinition propertyDefinitions()
Returns the definitions for data defined properties available for use in raster pipes.
void setDataDefinedProperties(const QgsPropertyCollection &collection)
Sets the pipe's property collection, used for data defined overrides.
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
Q_DECL_DEPRECATED void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.
Raster values range container.
double min() const
Returns the minimum value for the range.
Raster renderer pipe that applies colors to a raster.
virtual QString type() const
void setMinMaxOrigin(const QgsRasterMinMaxOrigin &origin)
Sets origin of min/max values.
const QgsRasterMinMaxOrigin & minMaxOrigin() const
Returns const reference to origin of min/max values.
virtual QList< QPair< QString, QColor > > legendSymbologyItems() const
Returns symbology items if provided by renderer.
Resample filter pipe for rasters.
void setZoomedOutResampler(QgsRasterResampler *r)
Sets resampler for zoomed out scales. Takes ownership of the object.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
void setZoomedInResampler(QgsRasterResampler *r)
Sets resampler for zoomed in scales. Takes ownership of the object.
Interface for all raster shaders.
QgsRasterShaderFunction * rasterShaderFunction()
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
IdentifyFormat
Definition: qgsraster.h:58
@ IdentifyFormatFeature
Definition: qgsraster.h:63
@ IdentifyFormatValue
Definition: qgsraster.h:60
@ IdentifyFormatText
Definition: qgsraster.h:61
@ IdentifyFormatUndefined
Definition: qgsraster.h:59
@ IdentifyFormatHtml
Definition: qgsraster.h:62
@ PaletteIndex
Paletted (see associated color table)
Definition: qgsraster.h:39
@ AlphaBand
Alpha (0=transparent, 255=opaque)
Definition: qgsraster.h:43
@ GrayIndex
Greyscale.
Definition: qgsraster.h:38
@ ContinuousPalette
Continuous palette, QGIS addition, GRASS.
Definition: qgsraster.h:54
DrawingStyle
This enumerator describes the different kinds of drawing we can do.
Definition: qgsraster.h:90
@ SingleBandColorDataStyle
Definition: qgsraster.h:101
@ MultiBandColor
Definition: qgsraster.h:100
@ PalettedColor
Definition: qgsraster.h:94
@ SingleBandPseudoColor
Definition: qgsraster.h:93
@ SingleBandGray
Definition: qgsraster.h:92
@ UndefinedDrawingStyle
Definition: qgsraster.h:91
The class is used as a container of context for various read/write operations on other objects.
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
Contains information about the context of a rendering operation.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:62
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Raster renderer pipe for single band gray.
void setContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
const QgsContrastEnhancement * contrastEnhancement() const
Raster renderer pipe for single band pseudocolor.
int band() const
Returns the band used by the renderer.
QgsRasterShader * shader()
Returns the raster shader.
void setShader(QgsRasterShader *shader)
Takes ownership of the shader.
An interface for classes which can visit style entity (e.g.
static void mergeScaleDependencies(double mScaleMinDenom, double mScaleMaxDenom, QVariantMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
static QgsRectangle readRectangle(const QDomElement &element)
Definition: qgsxmlutils.cpp:39
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1246
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1185
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
#define ERR(message)
QgsDataProvider * classFactoryFunction_t(const QString *, const QgsDataProvider::ProviderOptions &options)
QList< QPair< QString, QColor > > QgsLegendColorList
QList< QgsRasterRange > QgsRasterRangeList
Setting options for creating vector data providers.
Struct that stores the information about the parameters that should be given to the QgsVirtualRasterP...
QList< QgsRasterDataProvider::VirtualRasterInputLayers > rInputLayers
Setting options for loading raster layers.
bool skipCrsValidation
Controls whether the layer is allowed to have an invalid/unknown CRS.
bool loadDefaultStyle
Sets to true if the default layer style should be loaded.
QgsCoordinateTransformContext transformContext
Coordinate transform context.
Registry for raster renderer entries.
QgsRasterRendererCreateFunc rendererCreateFunction
This class provides details of the viewable area that a raster will be rendered into.
qgssize mHeight
Height, number of rows to be rendered.
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
QgsPointXY mBottomRightPoint
Coordinate (in output device coordinate system) of bottom right corner of the part of the raster that...
QgsPointXY mTopLeftPoint
Coordinate (in output device coordinate system) of top left corner of the part of the raster that is ...
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
QgsRectangle mDrawnExtent
Intersection of current map extent and layer extent.
QgsCoordinateTransformContext mTransformContext
Coordinate transform context.
qgssize mWidth
Width, number of columns to be rendered.