QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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  // deletes pipe elements (including data provider)
603  mPipe = std::make_unique< QgsRasterPipe >();
604  mDataProvider = nullptr;
605 
606  // XXX should I check for and possibly delete any pre-existing providers?
607  // XXX How often will that scenario occur?
608 
609  mProviderKey = provider;
610  // set the layer name (uppercase first character)
611  if ( ! mLayerName.isEmpty() ) // XXX shouldn't this happen in parent?
612  {
613  setName( mLayerName );
614  }
615 
616  //mBandCount = 0;
617 
618  std::unique_ptr< QgsScopedRuntimeProfile > profile;
619  if ( QgsApplication::profiler()->groupIsActive( QStringLiteral( "projectload" ) ) )
620  profile = std::make_unique< QgsScopedRuntimeProfile >( tr( "Create %1 provider" ).arg( provider ), QStringLiteral( "projectload" ) );
621 
622  mDataProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mProviderKey, mDataSource, options, flags ) );
623  if ( !mDataProvider )
624  {
625  //QgsMessageLog::logMessage( tr( "Cannot instantiate the data provider" ), tr( "Raster" ) );
626  appendError( ERR( tr( "Cannot instantiate the '%1' data provider" ).arg( mProviderKey ) ) );
627  return;
628  }
629  QgsDebugMsgLevel( QStringLiteral( "Data provider created" ), 4 );
630  mDataProvider->setParent( this );
631 
632  // Set data provider into pipe even if not valid so that it is deleted with pipe (with layer)
633  mPipe->set( mDataProvider );
634  if ( !mDataProvider->isValid() )
635  {
636  setError( mDataProvider->error() );
637  appendError( ERR( tr( "Provider is not valid (provider: %1, URI: %2" ).arg( mProviderKey, mDataSource ) ) );
638  return;
639  }
640 
642  {
643  setMetadata( mDataProvider->layerMetadata() );
644  QgsDebugMsgLevel( QStringLiteral( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 );
645  }
646 
647  if ( provider == QLatin1String( "gdal" ) )
648  {
649  // make sure that the /vsigzip or /vsizip is added to uri, if applicable
650  mDataSource = mDataProvider->dataSourceUri();
651  }
652 
654  {
655  // get the extent
656  const QgsRectangle mbr = mDataProvider->extent();
657 
658  // store the extent
659  setExtent( mbr );
660  }
661 
662  // upper case the first letter of the layer name
663  QgsDebugMsgLevel( "mLayerName: " + name(), 4 );
664 
665  // set up the raster drawing style
666  // Do not set any 'sensible' style here, the style is set later
667 
668  // Setup source CRS
669  setCrs( QgsCoordinateReferenceSystem( mDataProvider->crs() ) );
670 
671  QgsDebugMsgLevel( "using wkt:\n" + crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), 4 );
672 
673  //defaults - Needs to be set after the Contrast list has been build
674  //Try to read the default contrast enhancement from the config file
675 
676  //decide what type of layer this is...
677  //TODO Change this to look at the color interp and palette interp to decide which type of layer it is
678  QgsDebugMsgLevel( "bandCount = " + QString::number( mDataProvider->bandCount() ), 4 );
679  QgsDebugMsgLevel( "dataType = " + qgsEnumValueToKey< Qgis::DataType >( mDataProvider->dataType( 1 ) ), 4 );
680  if ( ( mDataProvider->bandCount() > 1 ) )
681  {
682  // handle singleband gray with alpha
683  if ( mDataProvider->bandCount() == 2
684  && ( ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::GrayIndex
685  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::AlphaBand )
686  || ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::AlphaBand
687  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::GrayIndex ) ) )
688  {
689  mRasterType = GrayOrUndefined;
690  }
691  else
692  {
693  mRasterType = Multiband;
694  }
695  }
696  else if ( mDataProvider->dataType( 1 ) == Qgis::DataType::ARGB32
697  || mDataProvider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied )
698  {
699  mRasterType = ColorLayer;
700  }
701  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
702  {
703  mRasterType = Palette;
704  }
705  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
706  {
707  mRasterType = Palette;
708  }
709  else
710  {
711  mRasterType = GrayOrUndefined;
712  }
713 
714  QgsDebugMsgLevel( "mRasterType = " + QString::number( mRasterType ), 4 );
715  if ( mRasterType == ColorLayer )
716  {
717  QgsDebugMsgLevel( "Setting drawing style to SingleBandColorDataStyle " + QString::number( QgsRaster::SingleBandColorDataStyle ), 4 );
718  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
719  }
720  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
721  {
722  setRendererForDrawingStyle( QgsRaster::PalettedColor ); //sensible default
723  }
724  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
725  {
726  setRendererForDrawingStyle( QgsRaster::SingleBandPseudoColor );
727  // Load color table
728  const QList<QgsColorRampShader::ColorRampItem> colorTable = mDataProvider->colorTable( 1 );
730  if ( r )
731  {
732  // TODO: this should go somewhere else
733  QgsRasterShader *shader = new QgsRasterShader();
734  QgsColorRampShader *colorRampShader = new QgsColorRampShader();
736  colorRampShader->setColorRampItemList( colorTable );
737  shader->setRasterShaderFunction( colorRampShader );
738  r->setShader( shader );
739  }
740  }
741  else if ( mRasterType == Multiband )
742  {
743  setRendererForDrawingStyle( QgsRaster::MultiBandColor ); //sensible default
744  }
745  else //GrayOrUndefined
746  {
747  setRendererForDrawingStyle( QgsRaster::SingleBandGray ); //sensible default
748  }
749 
750  // Auto set alpha band
751  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
752  {
753  if ( mDataProvider->colorInterpretation( bandNo ) == QgsRaster::AlphaBand )
754  {
755  if ( auto *lRenderer = mPipe->renderer() )
756  {
757  lRenderer->setAlphaBand( bandNo );
758  }
759  break;
760  }
761  }
762 
763  // brightness filter
765  mPipe->set( brightnessFilter );
766 
767  // hue/saturation filter
769  mPipe->set( hueSaturationFilter );
770 
771  // resampler (must be after renderer)
773  mPipe->set( resampleFilter );
774 
776  {
777  const QgsSettings settings;
778  QString resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedInResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
779  if ( resampling == QLatin1String( "bilinear" ) )
780  {
783  }
784  else if ( resampling == QLatin1String( "cubic" ) )
785  {
788  }
789  resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedOutResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
790  if ( resampling == QLatin1String( "bilinear" ) )
791  {
794  }
795 
796  const double maxOversampling = settings.value( QStringLiteral( "/Raster/defaultOversampling" ), 2.0 ).toDouble();
797  resampleFilter->setMaxOversampling( maxOversampling );
798  mDataProvider->setMaxOversampling( maxOversampling );
799 
801  settings.value( QStringLiteral( "/Raster/defaultEarlyResampling" ), false ).toBool() )
802  {
804  }
805  else
806  {
808  }
809  }
810 
811  // projector (may be anywhere in pipe)
812  QgsRasterProjector *projector = new QgsRasterProjector;
813  mPipe->set( projector );
814 
815  // Set default identify format - use the richest format available
816  const int capabilities = mDataProvider->capabilities();
818  if ( capabilities & QgsRasterInterface::IdentifyHtml )
819  {
820  // HTML is usually richest
821  identifyFormat = QgsRaster::IdentifyFormatHtml;
822  }
823  else if ( capabilities & QgsRasterInterface::IdentifyFeature )
824  {
825  identifyFormat = QgsRaster::IdentifyFormatFeature;
826  }
827  else if ( capabilities & QgsRasterInterface::IdentifyText )
828  {
829  identifyFormat = QgsRaster::IdentifyFormatText;
830  }
831  else if ( capabilities & QgsRasterInterface::IdentifyValue )
832  {
833  identifyFormat = QgsRaster::IdentifyFormatValue;
834  }
835  setCustomProperty( QStringLiteral( "identify/format" ), QgsRasterDataProvider::identifyFormatName( identifyFormat ) );
836 
837  // Store timestamp
838  // TODO move to provider
839  mLastModified = lastModified( mDataSource );
840 
841  // Do a passthrough for the status bar text
842  connect( mDataProvider, &QgsRasterDataProvider::statusChanged, this, &QgsRasterLayer::statusChanged );
843 
844  //mark the layer as valid
845  setValid( true );
846 
847  if ( mDataProvider->supportsSubsetString() )
848  connect( this, &QgsRasterLayer::subsetStringChanged, this, &QgsMapLayer::configChanged, Qt::UniqueConnection );
849  else
851 
852 
853  QgsDebugMsgLevel( QStringLiteral( "exiting." ), 4 );
854 
855 }
856 
857 void QgsRasterLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider,
858  const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
859 {
860  const bool hadRenderer( renderer() );
861 
862  QDomImplementation domImplementation;
863  QDomDocumentType documentType;
864  QString errorMsg;
865 
866  bool loadDefaultStyleFlag = false;
868  {
869  loadDefaultStyleFlag = true;
870  }
871 
872  // Store the original style
873  if ( hadRenderer && ! loadDefaultStyleFlag )
874  {
875  documentType = domImplementation.createDocumentType(
876  QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
877 
878  QDomDocument doc = QDomDocument( documentType );
879  QDomElement styleElem = doc.createElement( QStringLiteral( "qgis" ) );
880  styleElem.setAttribute( QStringLiteral( "version" ), Qgis::version() );
881  const QgsReadWriteContext writeContext;
882  if ( ! writeSymbology( styleElem, doc, errorMsg, writeContext ) )
883  {
884  QgsDebugMsg( QStringLiteral( "Could not store symbology for layer %1: %2" )
885  .arg( name(),
886  errorMsg ) );
887  }
888  else
889  {
890  doc.appendChild( styleElem );
891 
892  mOriginalStyleDocument = doc;
893  mOriginalStyleElement = styleElem;
894  }
895  }
896 
897  if ( mDataProvider )
898  closeDataProvider();
899 
900  init();
901 
902  for ( int i = mPipe->size() - 1; i >= 0; --i )
903  {
904  mPipe->remove( i );
905  }
906 
907  mDataSource = dataSource;
908  mLayerName = baseName;
909 
910  setDataProvider( provider, options, flags );
911 
912  if ( mDataProvider )
913  mDataProvider->setDataSourceUri( mDataSource );
914 
915  if ( isValid() )
916  {
917  // load default style
918  bool defaultLoadedFlag = false;
919  bool restoredStyle = false;
920  if ( loadDefaultStyleFlag )
921  {
922  loadDefaultStyle( defaultLoadedFlag );
923  }
924  else if ( !mOriginalStyleElement.isNull() ) // Restore the style
925  {
926  QgsReadWriteContext readContext;
927  if ( ! readSymbology( mOriginalStyleElement, errorMsg, readContext ) )
928  {
929  QgsDebugMsg( QStringLiteral( "Could not restore symbology for layer %1: %2" )
930  .arg( name() )
931  .arg( errorMsg ) );
932 
933  }
934  else
935  {
936  restoredStyle = true;
937  emit repaintRequested();
939  emit rendererChanged();
940  }
941  }
942 
943  if ( !defaultLoadedFlag && !restoredStyle )
944  {
946  }
947  }
948 }
949 
950 void QgsRasterLayer::closeDataProvider()
951 {
952  setValid( false );
953  mPipe->remove( mDataProvider );
954  mDataProvider = nullptr;
955 }
956 
957 void QgsRasterLayer::computeMinMax( int band,
958  const QgsRasterMinMaxOrigin &mmo,
960  const QgsRectangle &extent,
961  int sampleSize,
962  double &min, double &max )
963 {
964 
965  min = std::numeric_limits<double>::quiet_NaN();
966  max = std::numeric_limits<double>::quiet_NaN();
967  if ( !mDataProvider )
968  return;
969 
970  if ( limits == QgsRasterMinMaxOrigin::MinMax )
971  {
972  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Min | QgsRasterBandStats::Max, extent, sampleSize );
973  // Check if statistics were actually gathered, None means a failure
974  if ( myRasterBandStats.statsGathered == QgsRasterBandStats::Stats::None )
975  {
976  // Best guess we can do
977  switch ( mDataProvider->dataType( band ) )
978  {
980  {
981  myRasterBandStats.minimumValue = 0;
982  myRasterBandStats.maximumValue = 255;
983  break;
984  }
986  {
987  myRasterBandStats.minimumValue = 0;
988  myRasterBandStats.maximumValue = std::numeric_limits<uint16_t>::max();
989  break;
990  }
992  {
993  myRasterBandStats.minimumValue = 0;
994  myRasterBandStats.maximumValue = std::numeric_limits<uint32_t>::max();
995  break;
996  }
999  {
1000  myRasterBandStats.minimumValue = std::numeric_limits<int16_t>::lowest();
1001  myRasterBandStats.maximumValue = std::numeric_limits<int16_t>::max();
1002  break;
1003  }
1004  case Qgis::DataType::Int32:
1006  {
1007  myRasterBandStats.minimumValue = std::numeric_limits<int32_t>::lowest();
1008  myRasterBandStats.maximumValue = std::numeric_limits<int32_t>::max();
1009  break;
1010  }
1013  {
1014  myRasterBandStats.minimumValue = std::numeric_limits<float_t>::lowest();
1015  myRasterBandStats.maximumValue = std::numeric_limits<float_t>::max();
1016  break;
1017  }
1020  {
1021  myRasterBandStats.minimumValue = std::numeric_limits<double_t>::lowest();
1022  myRasterBandStats.maximumValue = std::numeric_limits<double_t>::max();
1023  break;
1024  }
1028  {
1029  // Nothing to guess
1030  break;
1031  }
1032  }
1033  }
1034  min = myRasterBandStats.minimumValue;
1035  max = myRasterBandStats.maximumValue;
1036  }
1037  else if ( limits == QgsRasterMinMaxOrigin::StdDev )
1038  {
1039  const QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, extent, sampleSize );
1040  min = myRasterBandStats.mean - ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
1041  max = myRasterBandStats.mean + ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
1042  }
1043  else if ( limits == QgsRasterMinMaxOrigin::CumulativeCut )
1044  {
1045  const double myLower = mmo.cumulativeCutLower();
1046  const double myUpper = mmo.cumulativeCutUpper();
1047  QgsDebugMsgLevel( QStringLiteral( "myLower = %1 myUpper = %2" ).arg( myLower ).arg( myUpper ), 4 );
1048  mDataProvider->cumulativeCut( band, myLower, myUpper, min, max, extent, sampleSize );
1049  }
1050  QgsDebugMsgLevel( QStringLiteral( "band = %1 min = %2 max = %3" ).arg( band ).arg( min ).arg( max ), 4 );
1051 
1052 }
1053 
1055 {
1056  return mDataProvider ? mDataProvider->ignoreExtents() : false;
1057 }
1058 
1060 {
1061  return mTemporalProperties;
1062 }
1063 
1065 {
1067  limits,
1068  extent,
1069  sampleSize,
1070  generateLookupTableFlag,
1071  mPipe->renderer() );
1072 }
1073 
1076  const QgsRectangle &extent,
1077  int sampleSize,
1078  bool generateLookupTableFlag,
1079  QgsRasterRenderer *rasterRenderer )
1080 {
1081  QgsDebugMsgLevel( QStringLiteral( "theAlgorithm = %1 limits = %2 extent.isEmpty() = %3" ).arg( algorithm ).arg( limits ).arg( extent.isEmpty() ), 4 );
1082  if ( !rasterRenderer || !mDataProvider )
1083  {
1084  return;
1085  }
1086 
1087  QList<int> myBands;
1088  QList<QgsContrastEnhancement *> myEnhancements;
1089  QgsRasterMinMaxOrigin myMinMaxOrigin;
1090  QgsRasterRenderer *myRasterRenderer = nullptr;
1091  QgsSingleBandGrayRenderer *myGrayRenderer = nullptr;
1092  QgsSingleBandPseudoColorRenderer *myPseudoColorRenderer = nullptr;
1093  QgsMultiBandColorRenderer *myMultiBandRenderer = nullptr;
1094  const QString rendererType = rasterRenderer->type();
1095  if ( rendererType == QLatin1String( "singlebandgray" ) )
1096  {
1097  myGrayRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer );
1098  if ( !myGrayRenderer )
1099  {
1100  return;
1101  }
1102  myBands << myGrayRenderer->grayBand();
1103  myRasterRenderer = myGrayRenderer;
1104  myMinMaxOrigin = myGrayRenderer->minMaxOrigin();
1105  }
1106  else if ( rendererType == QLatin1String( "multibandcolor" ) )
1107  {
1108  myMultiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer );
1109  if ( !myMultiBandRenderer )
1110  {
1111  return;
1112  }
1113  myBands << myMultiBandRenderer->redBand() << myMultiBandRenderer->greenBand() << myMultiBandRenderer->blueBand();
1114  myRasterRenderer = myMultiBandRenderer;
1115  myMinMaxOrigin = myMultiBandRenderer->minMaxOrigin();
1116  }
1117  else if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
1118  {
1119  myPseudoColorRenderer = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer );
1120  if ( !myPseudoColorRenderer )
1121  {
1122  return;
1123  }
1124  myBands << myPseudoColorRenderer->band();
1125  myRasterRenderer = myPseudoColorRenderer;
1126  myMinMaxOrigin = myPseudoColorRenderer->minMaxOrigin();
1127  }
1128  else
1129  {
1130  return;
1131  }
1132 
1133  const auto constMyBands = myBands;
1134  for ( const int myBand : constMyBands )
1135  {
1136  if ( myBand != -1 )
1137  {
1138  const Qgis::DataType myType = static_cast< Qgis::DataType >( mDataProvider->dataType( myBand ) );
1139  std::unique_ptr<QgsContrastEnhancement> myEnhancement( new QgsContrastEnhancement( static_cast< Qgis::DataType >( myType ) ) );
1140  myEnhancement->setContrastEnhancementAlgorithm( algorithm, generateLookupTableFlag );
1141 
1142  double min;
1143  double max;
1144  computeMinMax( myBand, myMinMaxOrigin, limits, extent, sampleSize, min, max );
1145 
1146  if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
1147  {
1148  myPseudoColorRenderer->setClassificationMin( min );
1149  myPseudoColorRenderer->setClassificationMax( max );
1150  if ( myPseudoColorRenderer->shader() )
1151  {
1152  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( myPseudoColorRenderer->shader()->rasterShaderFunction() );
1153  if ( colorRampShader )
1154  {
1155  colorRampShader->classifyColorRamp( myPseudoColorRenderer->band(), extent, myPseudoColorRenderer->input() );
1156  }
1157  }
1158  }
1159  else
1160  {
1161  myEnhancement->setMinimumValue( min );
1162  myEnhancement->setMaximumValue( max );
1163  myEnhancements.append( myEnhancement.release() );
1164  }
1165  }
1166  else
1167  {
1168  myEnhancements.append( nullptr );
1169  }
1170  }
1171 
1172  if ( rendererType == QLatin1String( "singlebandgray" ) )
1173  {
1174  if ( myEnhancements.first() ) myGrayRenderer->setContrastEnhancement( myEnhancements.takeFirst() );
1175  }
1176  else if ( rendererType == QLatin1String( "multibandcolor" ) )
1177  {
1178  if ( myEnhancements.first() ) myMultiBandRenderer->setRedContrastEnhancement( myEnhancements.takeFirst() );
1179  if ( myEnhancements.first() ) myMultiBandRenderer->setGreenContrastEnhancement( myEnhancements.takeFirst() );
1180  if ( myEnhancements.first() ) myMultiBandRenderer->setBlueContrastEnhancement( myEnhancements.takeFirst() );
1181  }
1182 
1183  //delete all remaining unused enhancements
1184  qDeleteAll( myEnhancements );
1185 
1186  myMinMaxOrigin.setLimits( limits );
1187  if ( extent != QgsRectangle() &&
1188  myMinMaxOrigin.extent() == QgsRasterMinMaxOrigin::WholeRaster )
1189  {
1191  }
1192  if ( myRasterRenderer )
1193  {
1194  myRasterRenderer->setMinMaxOrigin( myMinMaxOrigin );
1195  }
1196 
1197  if ( rasterRenderer == renderer() )
1198  {
1199  emit repaintRequested();
1200  emitStyleChanged();
1201  emit rendererChanged();
1202  }
1203 }
1204 
1206 {
1207  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1208  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1209  const QgsContrastEnhancement *ce = nullptr;
1210  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) ) )
1211  {
1212  ce = singleBandRenderer->contrastEnhancement();
1213  }
1214  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) ) )
1215  {
1216  ce = multiBandRenderer->redContrastEnhancement();
1217  }
1218 
1219  if ( ce )
1220  {
1223  renderer()->minMaxOrigin().limits() == QgsRasterMinMaxOrigin::None ?
1224  QgsRasterMinMaxOrigin::MinMax : renderer()->minMaxOrigin().limits(),
1225  extent,
1226  SAMPLE_SIZE,
1227  true,
1228  renderer() );
1229  }
1230  else
1231  {
1234  if ( defaultContrastEnhancementSettings( myAlgorithm, myLimits ) )
1235  {
1237  myLimits,
1238  extent,
1239  SAMPLE_SIZE,
1240  true,
1241  renderer() );
1242  }
1243  }
1244 }
1245 
1247  const QgsRectangle &extent )
1248 {
1249  if ( mDataProvider &&
1250  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded != extent &&
1251  rasterRenderer->minMaxOrigin().limits() != QgsRasterMinMaxOrigin::None &&
1253  {
1254  refreshRenderer( rasterRenderer, extent );
1255  }
1256 }
1257 
1258 void QgsRasterLayer::refreshRenderer( QgsRasterRenderer *rasterRenderer, const QgsRectangle &extent )
1259 {
1260  if ( mDataProvider )
1261  {
1262  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1263  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1264  QgsSingleBandPseudoColorRenderer *sbpcr = nullptr;
1265  const QgsContrastEnhancement *ce = nullptr;
1266  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer ) ) )
1267  {
1268  ce = singleBandRenderer->contrastEnhancement();
1269  }
1270  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer ) ) )
1271  {
1272  ce = multiBandRenderer->redContrastEnhancement();
1273  }
1274  else if ( ( sbpcr = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer ) ) )
1275  {
1276  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1277  double min;
1278  double max;
1279  computeMinMax( sbpcr->band(),
1280  rasterRenderer->minMaxOrigin(),
1281  rasterRenderer->minMaxOrigin().limits(), extent,
1282  SAMPLE_SIZE, min, max );
1283  sbpcr->setClassificationMin( min );
1284  sbpcr->setClassificationMax( max );
1285 
1286  if ( sbpcr->shader() )
1287  {
1288  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( sbpcr->shader()->rasterShaderFunction() );
1289  if ( colorRampShader )
1290  {
1291  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1292  }
1293  }
1294 
1296  r->setClassificationMin( min );
1297  r->setClassificationMax( max );
1298 
1299  if ( r->shader() )
1300  {
1301  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( r->shader()->rasterShaderFunction() );
1302  if ( colorRampShader )
1303  {
1304  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1305  }
1306  }
1307 
1308  emit repaintRequested();
1309  emitStyleChanged();
1310  emit rendererChanged();
1311  return;
1312  }
1313 
1314  if ( ce &&
1316  {
1317  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1318 
1320  rasterRenderer->minMaxOrigin().limits(),
1321  extent,
1322  SAMPLE_SIZE,
1323  true,
1324  rasterRenderer );
1325 
1326  // Update main renderer so that the legends get updated
1327  if ( singleBandRenderer )
1328  static_cast<QgsSingleBandGrayRenderer *>( renderer() )->setContrastEnhancement( new QgsContrastEnhancement( * singleBandRenderer->contrastEnhancement() ) );
1329  else if ( multiBandRenderer )
1330  {
1331  if ( multiBandRenderer->redContrastEnhancement() )
1332  {
1333  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setRedContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->redContrastEnhancement() ) );
1334  }
1335  if ( multiBandRenderer->greenContrastEnhancement() )
1336  {
1337  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setGreenContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->greenContrastEnhancement() ) );
1338  }
1339  if ( multiBandRenderer->blueContrastEnhancement() )
1340  {
1341  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setBlueContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->blueContrastEnhancement() ) );
1342  }
1343  }
1344 
1345  emitStyleChanged();
1346  emit rendererChanged();
1347  }
1348  }
1349 }
1350 
1352 {
1353  if ( !isValid() || !mDataProvider )
1354  {
1355  QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider" ), 3 );
1356  return customProperty( QStringLiteral( "storedSubsetString" ) ).toString();
1357  }
1358  if ( !mDataProvider->supportsSubsetString() )
1359  {
1360  return QString();
1361  }
1362  return mDataProvider->subsetString();
1363 }
1364 
1365 bool QgsRasterLayer::setSubsetString( const QString &subset )
1366 {
1367  if ( !isValid() || !mDataProvider )
1368  {
1369  QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider or while editing" ), 3 );
1370  setCustomProperty( QStringLiteral( "storedSubsetString" ), subset );
1371  return false;
1372  }
1373 
1374  if ( !mDataProvider->supportsSubsetString() )
1375  {
1376  return false;
1377  }
1378 
1379  if ( subset == mDataProvider->subsetString() )
1380  return true;
1381 
1382  const bool res = mDataProvider->setSubsetString( subset );
1383 
1384  // get the updated data source string from the provider
1385  mDataSource = mDataProvider->dataSourceUri();
1386 
1387  if ( res )
1388  {
1389  setExtent( mDataProvider->extent() );
1390  refreshRenderer( renderer(), extent() );
1391  emit subsetStringChanged();
1392  }
1393 
1394  return res;
1395 }
1396 
1399  QgsRasterMinMaxOrigin::Limits &myLimits ) const
1400 {
1401  const QgsSettings mySettings;
1402 
1403  QString key;
1404  QString defaultAlg;
1405  QString defaultLimits;
1406 
1407  // TODO: we should not test renderer class here, move it somehow to renderers
1408  if ( dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) )
1409  {
1410  key = QStringLiteral( "singleBand" );
1413  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1415  }
1416  else if ( dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) )
1417  {
1418  if ( QgsRasterBlock::typeSize( dataProvider()->sourceDataType( 1 ) ) == 1 )
1419  {
1420  key = QStringLiteral( "multiBandSingleByte" );
1423  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1425  }
1426  else
1427  {
1428  key = QStringLiteral( "multiBandMultiByte" );
1431  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1433  }
1434  }
1435 
1436  if ( key.isEmpty() )
1437  {
1438  QgsDebugMsgLevel( QStringLiteral( "No default contrast enhancement for this drawing style" ), 2 );
1440  myLimits = QgsRasterMinMaxOrigin::limitsFromString( QString() );
1441  return false;
1442  }
1443  QgsDebugMsgLevel( "key = " + key, 4 );
1444 
1445  const QString myAlgorithmString = mySettings.value( "/Raster/defaultContrastEnhancementAlgorithm/" + key, defaultAlg ).toString();
1446  QgsDebugMsgLevel( "myAlgorithmString = " + myAlgorithmString, 4 );
1447 
1448  myAlgorithm = QgsContrastEnhancement::contrastEnhancementAlgorithmFromString( myAlgorithmString );
1449 
1450  const QString myLimitsString = mySettings.value( "/Raster/defaultContrastEnhancementLimits/" + key, defaultLimits ).toString();
1451  QgsDebugMsgLevel( "myLimitsString = " + myLimitsString, 4 );
1452  myLimits = QgsRasterMinMaxOrigin::limitsFromString( myLimitsString );
1453 
1454  return true;
1455 }
1456 
1458 {
1459  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1460 
1463  defaultContrastEnhancementSettings( myAlgorithm, myLimits );
1464 
1465  setContrastEnhancement( myAlgorithm, myLimits );
1466 }
1467 
1468 void QgsRasterLayer::setLayerOrder( QStringList const &layers )
1469 {
1470  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
1471 
1472  if ( mDataProvider )
1473  {
1474  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setLayerOrder(layers)." ), 4 );
1475  mDataProvider->setLayerOrder( layers );
1476  }
1477 
1478 }
1479 
1480 void QgsRasterLayer::setSubLayerVisibility( const QString &name, bool vis )
1481 {
1482 
1483  if ( mDataProvider )
1484  {
1485  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setSubLayerVisibility(name, vis)." ), 4 );
1486  mDataProvider->setSubLayerVisibility( name, vis );
1487  }
1488 
1489 }
1490 
1491 QDateTime QgsRasterLayer::timestamp() const
1492 {
1493  if ( !mDataProvider )
1494  return QDateTime();
1495  return mDataProvider->timestamp();
1496 }
1497 
1499 {
1500  if ( auto *lRenderer = mPipe->renderer() )
1501  {
1502  if ( !lRenderer->accept( visitor ) )
1503  return false;
1504  }
1505  return true;
1506 }
1507 
1508 
1509 bool QgsRasterLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props ) const
1510 {
1511  Q_UNUSED( errorMessage )
1512 
1513  QVariantMap localProps = QVariantMap( props );
1514  if ( hasScaleBasedVisibility() )
1515  {
1516  // TODO: QgsSymbolLayerUtils::mergeScaleDependencies generate SE only and not SLD1.0
1518  }
1519 
1520  if ( isSpatial() ) // TODO: does it make sense this control?
1521  {
1522  // store constraints
1523  QDomElement constraintElem = doc.createElement( QStringLiteral( "sld:LayerFeatureConstraints" ) );
1524  node.appendChild( constraintElem );
1525 
1526  const QDomElement featureTypeConstraintElem = doc.createElement( QStringLiteral( "sld:FeatureTypeConstraint" ) );
1527  constraintElem.appendChild( featureTypeConstraintElem );
1528 
1529  QDomElement userStyleElem = doc.createElement( QStringLiteral( "sld:UserStyle" ) );
1530  node.appendChild( userStyleElem );
1531 
1532  if ( !name().isEmpty() )
1533  {
1534  QDomElement nameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1535  nameElem.appendChild( doc.createTextNode( name() ) );
1536  userStyleElem.appendChild( nameElem );
1537  }
1538 
1539  if ( !abstract().isEmpty() )
1540  {
1541  QDomElement abstractElem = doc.createElement( QStringLiteral( "sld:Abstract" ) );
1542  abstractElem.appendChild( doc.createTextNode( abstract() ) );
1543  userStyleElem.appendChild( abstractElem );
1544  }
1545 
1546  if ( !title().isEmpty() )
1547  {
1548  QDomElement titleElem = doc.createElement( QStringLiteral( "sld:Title" ) );
1549  titleElem.appendChild( doc.createTextNode( title() ) );
1550  userStyleElem.appendChild( titleElem );
1551  }
1552 
1553  QDomElement featureTypeStyleElem = doc.createElement( QStringLiteral( "sld:FeatureTypeStyle" ) );
1554  userStyleElem.appendChild( featureTypeStyleElem );
1555 
1556 #if 0
1557  // TODO: Is there a way to fill it's value with the named style?
1558  // by default <sld:Name> under <sld:FeatureTypeStyle> can have 0 occurrences
1559  // the same happen for tags:
1560  // sld:Title
1561  // sld:Abstract
1562  // sld:FeatureTypeName
1563  // sld:SemanticTypeIdentifier
1564  QDomElement typeStyleNameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1565  featureTypeStyleElem.appendChild( typeStyleNameElem );
1566 #endif
1567 
1568  QDomElement typeStyleRuleElem = doc.createElement( QStringLiteral( "sld:Rule" ) );
1569  featureTypeStyleElem.appendChild( typeStyleRuleElem );
1570 
1571  // add ScaleDenominator tags
1572  if ( hasScaleBasedVisibility() )
1573  {
1574  // note that denominator is the inverted value of scale
1575  if ( maximumScale() != 0.0 )
1576  {
1577  QDomElement minScaleElem = doc.createElement( QStringLiteral( "sld:MinScaleDenominator" ) );
1578  minScaleElem.appendChild( doc.createTextNode( QString::number( maximumScale() ) ) );
1579  typeStyleRuleElem.appendChild( minScaleElem );
1580  }
1581 
1582  QDomElement maxScaleElem = doc.createElement( QStringLiteral( "sld:MaxScaleDenominator" ) );
1583  maxScaleElem.appendChild( doc.createTextNode( QString::number( minimumScale() ) ) );
1584  typeStyleRuleElem.appendChild( maxScaleElem );
1585  }
1586 
1587  // export renderer dependent tags
1588  mPipe->renderer()->toSld( doc, typeStyleRuleElem, localProps );
1589 
1590  // inject raster layer parameters in RasterSymbolizer tag because
1591  // they belongs to rasterlayer and not to the renderer => avoid to
1592  // pass many parameters value via localProps
1593  const QDomNodeList elements = typeStyleRuleElem.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
1594  if ( elements.size() != 0 )
1595  {
1596  // there SHOULD be only one
1597  QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
1598 
1599  // lamda helper used below to reduce code redundancy
1600  auto vendorOptionWriter = [&]( QString name, QString value )
1601  {
1602  QDomElement vendorOptionElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
1603  vendorOptionElem.setAttribute( QStringLiteral( "name" ), name );
1604  vendorOptionElem.appendChild( doc.createTextNode( value ) );
1605  rasterSymbolizerElem.appendChild( vendorOptionElem );
1606  };
1607 
1608  if ( hueSaturationFilter()->invertColors() )
1609  {
1610  vendorOptionWriter( QStringLiteral( "invertColors" ), QString::number( 1 ) );
1611  }
1612 
1613  // add greyScale rendering mode if set
1614  if ( hueSaturationFilter()->grayscaleMode() != QgsHueSaturationFilter::GrayscaleOff )
1615  {
1616  QString property;
1617  switch ( hueSaturationFilter()->grayscaleMode() )
1618  {
1620  property = QStringLiteral( "lightness" );
1621  break;
1623  property = QStringLiteral( "luminosity" );
1624  break;
1626  property = QStringLiteral( "average" );
1627  break;
1629  // added just to avoid travis fail
1630  break;
1631  }
1632  if ( !property.isEmpty() )
1633  vendorOptionWriter( QStringLiteral( "grayScale" ), property );
1634  }
1635 
1636  // add Hue, Saturation and Lighting values in props is Hue filter is set
1637  if ( hueSaturationFilter() && hueSaturationFilter()->colorizeOn() )
1638  {
1639  vendorOptionWriter( QStringLiteral( "colorizeOn" ), QString::number( hueSaturationFilter()->colorizeOn() ) );
1640  vendorOptionWriter( QStringLiteral( "colorizeRed" ), QString::number( hueSaturationFilter()->colorizeColor().red() ) );
1641  vendorOptionWriter( QStringLiteral( "colorizeGreen" ), QString::number( hueSaturationFilter()->colorizeColor().green() ) );
1642  vendorOptionWriter( QStringLiteral( "colorizeBlue" ), QString::number( hueSaturationFilter()->colorizeColor().blue() ) );
1643  if ( hueSaturationFilter()->colorizeStrength() != 100.0 )
1644  vendorOptionWriter( QStringLiteral( "colorizeStrength" ), QString::number( hueSaturationFilter()->colorizeStrength() / 100.0 ) );
1645  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( hueSaturationFilter()->colorizeColor().saturationF() ) );
1646  }
1647  else
1648  {
1649  // saturation != 0 (default value)
1650  if ( hueSaturationFilter()->saturation() != 0 )
1651  {
1652  // normlize value [-100:100] -> [0:1]
1653  const int s = hueSaturationFilter()->saturation();
1654  const double sF = ( s - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1655  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( sF ) );
1656  }
1657  }
1658 
1659  // brightness != 0 (default value)
1660  if ( brightnessFilter()->brightness() != 0 )
1661  {
1662  // normalize value [-255:255] -> [0:1]
1663  const int b = brightnessFilter()->brightness();
1664  const double bF = ( b - ( -255.0 ) ) / ( 255.0 - ( -255.0 ) );
1665  vendorOptionWriter( QStringLiteral( "brightness" ), QString::number( bF ) );
1666  }
1667 
1668  // contrast != 0 (default value)
1669  if ( brightnessFilter()->contrast() != 0 )
1670  {
1671  // normlize value [-100:100] -> [0:1]
1672  const int c = brightnessFilter()->contrast();
1673  const double cF = ( c - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1674  vendorOptionWriter( QStringLiteral( "contrast" ), QString::number( cF ) );
1675  }
1676 
1677 #if 0
1678  // TODO: check if the below mapping formula make sense to map QGIS contrast with SLD gamma value
1679  //
1680  // add SLD1.0 ContrastEnhancement GammaValue = QGIS Contrast
1681  // SLD1.0 does only define 1 as neutral/center double value but does not define range.
1682  // because https://en.wikipedia.org/wiki/Gamma_correction assumed gamma is >0.
1683  // whilst QGIS has a -100/100 values centered in 0 => QGIS contrast value will be scaled in the
1684  // following way:
1685  // [-100,0] => [0,1] and [0,100] => [1,100]
1686  // an alternative could be scale [-100,100] => (0,2]
1687  //
1688  if ( newProps.contains( QStringLiteral( "contrast" ) ) )
1689  {
1690  double gamma;
1691  double contrast = newProps[ QStringLiteral( "contrast" ) ].toDouble();
1692  double percentage = ( contrast - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1693  if ( percentage <= 0.5 )
1694  {
1695  // stretch % to [0-1]
1696  gamma = percentage / 0.5;
1697  }
1698  else
1699  {
1700  gamma = contrast;
1701  }
1702 
1703  QDomElement globalContrastEnhancementElem = doc.createElement( QStringLiteral( "sld:ContrastEnhancement" ) );
1704  rasterSymolizerElem.appendChild( globalContrastEnhancementElem );
1705 
1706  QDomElement gammaValueElem = doc.createElement( QStringLiteral( "sld:GammaValue" ) );
1707  gammaValueElem.appendChild( doc.createTextNode( QString::number( gamma ) ) );
1708  globalContrastEnhancementElem.appendChild( gammaValueElem );
1709  }
1710 #endif
1711  }
1712  }
1713  return true;
1714 }
1715 
1716 
1718 {
1719  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1720  if ( !renderer )
1721  {
1722  return;
1723  }
1724 
1725  mPipe->set( renderer );
1726  emit rendererChanged();
1727  emitStyleChanged();
1728 }
1729 
1731 {
1732  return mPipe->renderer();
1733 }
1734 
1736 {
1737  return mPipe->resampleFilter();
1738 }
1739 
1741 {
1742  return mPipe->brightnessFilter();
1743 }
1744 
1746 {
1747  return mPipe->hueSaturationFilter();
1748 }
1749 
1750 void QgsRasterLayer::showStatusMessage( QString const &message )
1751 {
1752  // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
1753 
1754  // Pass-through
1755  // TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
1756  emit statusChanged( message );
1757 }
1758 
1760 {
1761  if ( mDataProvider )
1762  mDataProvider->setTransformContext( transformContext );
1764 }
1765 
1766 QStringList QgsRasterLayer::subLayers() const
1767 {
1768  if ( ! mDataProvider )
1769  return QStringList();
1770  return mDataProvider->subLayers();
1771 }
1772 
1773 // this function should be used when rendering with the MTR engine introduced in 2.3, as QPixmap is not thread safe (see bug #9626)
1774 // note: previewAsImage and previewAsPixmap should use a common low-level fct QgsRasterLayer::previewOnPaintDevice( QSize size, QColor bgColor, QPaintDevice &device )
1775 QImage QgsRasterLayer::previewAsImage( QSize size, const QColor &bgColor, QImage::Format format )
1776 {
1777  QImage image( size, format );
1778 
1779  if ( ! isValid( ) )
1780  return QImage();
1781 
1782  if ( image.format() == QImage::Format_Indexed8 )
1783  {
1784  image.setColor( 0, bgColor.rgba() );
1785  image.fill( 0 ); //defaults to white, set to transparent for rendering on a map
1786  }
1787  else
1788  {
1789  image.fill( bgColor );
1790  }
1791 
1792  QgsRasterViewPort *rasterViewPort = new QgsRasterViewPort();
1793 
1794  double mapUnitsPerPixel;
1795  double x = 0.0;
1796  double y = 0.0;
1797  const QgsRectangle extent = mDataProvider->extent();
1798  if ( extent.width() / extent.height() >= static_cast< double >( image.width() ) / image.height() )
1799  {
1800  mapUnitsPerPixel = extent.width() / image.width();
1801  y = ( image.height() - extent.height() / mapUnitsPerPixel ) / 2;
1802  }
1803  else
1804  {
1805  mapUnitsPerPixel = extent.height() / image.height();
1806  x = ( image.width() - extent.width() / mapUnitsPerPixel ) / 2;
1807  }
1808 
1809  const double pixelWidth = extent.width() / mapUnitsPerPixel;
1810  const double pixelHeight = extent.height() / mapUnitsPerPixel;
1811 
1812  rasterViewPort->mTopLeftPoint = QgsPointXY( x, y );
1813  rasterViewPort->mBottomRightPoint = QgsPointXY( pixelWidth, pixelHeight );
1814  rasterViewPort->mWidth = image.width();
1815  rasterViewPort->mHeight = image.height();
1816 
1817  rasterViewPort->mDrawnExtent = extent;
1818  rasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
1819  rasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
1820 
1821  QgsMapToPixel *mapToPixel = new QgsMapToPixel( mapUnitsPerPixel );
1822 
1823  QPainter *painter = new QPainter( &image );
1824  draw( painter, rasterViewPort, mapToPixel );
1825  delete rasterViewPort;
1826  delete mapToPixel;
1827 
1828  painter->end();
1829  delete painter;
1830 
1831  return image;
1832 }
1833 
1835 //
1836 // Protected methods
1837 //
1839 /*
1840  * \param QDomNode node that will contain the symbology definition for this layer.
1841  * \param errorMessage reference to string that will be updated with any error messages
1842  * \return TRUE in case of success.
1843  */
1844 bool QgsRasterLayer::readSymbology( const QDomNode &layer_node, QString &errorMessage,
1845  QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1846 {
1847  Q_UNUSED( errorMessage )
1848  // TODO: implement categories for raster layer
1849 
1850  QDomElement rasterRendererElem;
1851 
1852  const QDomElement layerElement = layer_node.toElement();
1853  readCommonStyle( layerElement, context, categories );
1854 
1855  // pipe element was introduced in the end of 1.9 development when there were
1856  // already many project files in use so we support 1.9 backward compatibility
1857  // even it was never officially released -> use pipe element if present, otherwise
1858  // use layer node
1859  QDomNode pipeNode = layer_node.firstChildElement( QStringLiteral( "pipe" ) );
1860  if ( pipeNode.isNull() ) // old project
1861  {
1862  pipeNode = layer_node;
1863  }
1864 
1865  //rasterlayerproperties element there -> old format (1.8 and early 1.9)
1866  if ( !layer_node.firstChildElement( QStringLiteral( "rasterproperties" ) ).isNull() )
1867  {
1868  //copy node because layer_node is const
1869  QDomNode layerNodeCopy = layer_node.cloneNode();
1870  QDomDocument doc = layerNodeCopy.ownerDocument();
1871  QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterproperties" ) );
1872  QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
1873  this );
1874  rasterRendererElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1875  QgsDebugMsgLevel( doc.toString(), 4 );
1876  }
1877  else
1878  {
1879  rasterRendererElem = pipeNode.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1880  }
1881 
1882  if ( !rasterRendererElem.isNull() )
1883  {
1884  const QString rendererType = rasterRendererElem.attribute( QStringLiteral( "type" ) );
1885  QgsRasterRendererRegistryEntry rendererEntry;
1886  if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererType, rendererEntry ) )
1887  {
1888  QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
1889  mPipe->set( renderer );
1890  }
1891  }
1892 
1893  //brightness
1895  mPipe->set( brightnessFilter );
1896 
1897  //brightness coefficient
1898  const QDomElement brightnessElem = pipeNode.firstChildElement( QStringLiteral( "brightnesscontrast" ) );
1899  if ( !brightnessElem.isNull() )
1900  {
1901  brightnessFilter->readXml( brightnessElem );
1902  }
1903 
1904  //hue/saturation
1906  mPipe->set( hueSaturationFilter );
1907 
1908  //saturation coefficient
1909  const QDomElement hueSaturationElem = pipeNode.firstChildElement( QStringLiteral( "huesaturation" ) );
1910  if ( !hueSaturationElem.isNull() )
1911  {
1912  hueSaturationFilter->readXml( hueSaturationElem );
1913  }
1914 
1915  //resampler
1917  mPipe->set( resampleFilter );
1918 
1919  //max oversampling
1920  const QDomElement resampleElem = pipeNode.firstChildElement( QStringLiteral( "rasterresampler" ) );
1921  if ( !resampleElem.isNull() )
1922  {
1923  resampleFilter->readXml( resampleElem );
1924  }
1925 
1926  //provider
1927  if ( mDataProvider )
1928  {
1929  const QDomElement providerElem = pipeNode.firstChildElement( QStringLiteral( "provider" ) );
1930  if ( !providerElem.isNull() )
1931  {
1932  mDataProvider->readXml( providerElem );
1933  }
1934  }
1935 
1936  // Resampling stage
1937  const QDomNode resamplingStageElement = pipeNode.namedItem( QStringLiteral( "resamplingStage" ) );
1938  if ( !resamplingStageElement.isNull() )
1939  {
1940  const QDomElement e = resamplingStageElement.toElement();
1941  if ( e.text() == QLatin1String( "provider" ) )
1943  else if ( e.text() == QLatin1String( "resamplingFilter" ) )
1945  }
1946 
1947  // get and set the blend mode if it exists
1948  const QDomNode blendModeNode = layer_node.namedItem( QStringLiteral( "blendMode" ) );
1949  if ( !blendModeNode.isNull() )
1950  {
1951  const QDomElement e = blendModeNode.toElement();
1952  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
1953  }
1954 
1955  const QDomElement elemDataDefinedProperties = layer_node.firstChildElement( QStringLiteral( "pipe-data-defined-properties" ) );
1956  if ( !elemDataDefinedProperties.isNull() )
1957  mPipe->dataDefinedProperties().readXml( elemDataDefinedProperties, QgsRasterPipe::propertyDefinitions() );
1958 
1959  readCustomProperties( layer_node );
1960 
1961  return true;
1962 }
1963 
1964 bool QgsRasterLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1965 {
1966  return readSymbology( node, errorMessage, context, categories );
1967 }
1968 
1969 bool QgsRasterLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1970 {
1971  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1972  // Make sure to read the file first so stats etc are initialized properly!
1973 
1974  //process provider key
1975  const QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
1976 
1977  if ( pkeyNode.isNull() )
1978  {
1979  mProviderKey = QStringLiteral( "gdal" );
1980  }
1981  else
1982  {
1983  const QDomElement pkeyElt = pkeyNode.toElement();
1984  mProviderKey = pkeyElt.text();
1985  if ( mProviderKey.isEmpty() )
1986  {
1987  mProviderKey = QStringLiteral( "gdal" );
1988  }
1989  }
1990 
1991  // Open the raster source based on provider and datasource
1992 
1993  // Go down the raster-data-provider paradigm
1994 
1995  // Collect provider-specific information
1996 
1997  const QDomNode rpNode = layer_node.namedItem( QStringLiteral( "rasterproperties" ) );
1998 
1999  if ( mProviderKey == QLatin1String( "wms" ) )
2000  {
2001  // >>> BACKWARD COMPATIBILITY < 1.9
2002  // The old WMS URI format does not contain all the information, we add them here.
2003  if ( !mDataSource.contains( QLatin1String( "crs=" ) ) && !mDataSource.contains( QLatin1String( "format=" ) ) )
2004  {
2005  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> adding params" ), 4 );
2006  QgsDataSourceUri uri;
2007  uri.setEncodedUri( mDataSource );
2008  QDomElement layerElement = rpNode.firstChildElement( QStringLiteral( "wmsSublayer" ) );
2009  while ( !layerElement.isNull() )
2010  {
2011  // TODO: sublayer visibility - post-0.8 release timeframe
2012 
2013  // collect name for the sublayer
2014  uri.setParam( QStringLiteral( "layers" ), layerElement.namedItem( QStringLiteral( "name" ) ).toElement().text() );
2015 
2016  // collect style for the sublayer
2017  uri.setParam( QStringLiteral( "styles" ), layerElement.namedItem( QStringLiteral( "style" ) ).toElement().text() );
2018 
2019  layerElement = layerElement.nextSiblingElement( QStringLiteral( "wmsSublayer" ) );
2020  }
2021 
2022  // Collect format
2023  uri.setParam( QStringLiteral( "format" ), rpNode.namedItem( QStringLiteral( "wmsFormat" ) ).toElement().text() );
2024 
2025  // WMS CRS URL param should not be mixed with that assigned to the layer.
2026  // In the old WMS URI version there was no CRS and layer crs().authid() was used.
2027  uri.setParam( QStringLiteral( "crs" ), crs().authid() );
2028  mDataSource = uri.encodedUri();
2029  }
2030  // <<< BACKWARD COMPATIBILITY < 1.9
2031  }
2032 
2034  {
2035  const QgsDataProvider::ProviderOptions providerOptions { context.transformContext() };
2036  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2038  {
2040  }
2041  // read extent
2043  {
2044  const QDomNode extentNode = layer_node.namedItem( QStringLiteral( "extent" ) );
2045  if ( !extentNode.isNull() )
2046  {
2047  // get the extent
2048  const QgsRectangle mbr = QgsXmlUtils::readRectangle( extentNode.toElement() );
2049 
2050  // store the extent
2051  setExtent( mbr );
2052 
2053  // skip get extent
2055  }
2056  }
2057  setDataProvider( mProviderKey, providerOptions, flags );
2058  }
2059 
2060  mOriginalStyleElement = layer_node.namedItem( QStringLiteral( "originalStyle" ) ).firstChildElement();
2061  if ( mOriginalStyleElement.isNull() )
2062  mOriginalStyleElement = layer_node.toElement();
2063  mOriginalStyleDocument = layer_node.ownerDocument();
2064 
2065  if ( ! mDataProvider )
2066  {
2068  {
2069  QgsDebugMsg( QStringLiteral( "Raster data provider could not be created for %1" ).arg( mDataSource ) );
2070  }
2071  return false;
2072  }
2073 
2074  QString error;
2075  const bool res = readSymbology( layer_node, error, context );
2076 
2077  // old wms settings we need to correct
2078  if ( res && mProviderKey == QLatin1String( "wms" ) && ( !renderer() || renderer()->type() != QLatin1String( "singlebandcolordata" ) ) )
2079  {
2080  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
2081  }
2082 
2083  // Check timestamp
2084  // This was probably introduced to reload completely raster if data changed and
2085  // reset completely symbology to reflect new data type etc. It creates however
2086  // problems, because user defined symbology is complete lost if data file time
2087  // changed (the content may be the same). See also 6900.
2088 #if 0
2089  QDomNode stampNode = layer_node.namedItem( "timestamp" );
2090  if ( !stampNode.isNull() )
2091  {
2092  QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
2093  // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
2094  if ( stamp < mDataProvider->dataTimestamp() )
2095  {
2096  QgsDebugMsgLevel( QStringLiteral( "data changed, reload provider" ), 3 );
2097  closeDataProvider();
2098  init();
2100  if ( !isValid() ) return false;
2101  }
2102  }
2103 #endif
2104 
2105  // Load user no data value
2106  const QDomElement noDataElement = layer_node.firstChildElement( QStringLiteral( "noData" ) );
2107 
2108  const QDomNodeList noDataBandList = noDataElement.elementsByTagName( QStringLiteral( "noDataList" ) );
2109 
2110  for ( int i = 0; i < noDataBandList.size(); ++i )
2111  {
2112  const QDomElement bandElement = noDataBandList.at( i ).toElement();
2113  bool ok;
2114  const int bandNo = bandElement.attribute( QStringLiteral( "bandNo" ) ).toInt( &ok );
2115  QgsDebugMsgLevel( QStringLiteral( "bandNo = %1" ).arg( bandNo ), 4 );
2116  if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
2117  {
2118  mDataProvider->setUseSourceNoDataValue( bandNo, bandElement.attribute( QStringLiteral( "useSrcNoData" ) ).toInt() );
2119  QgsRasterRangeList myNoDataRangeList;
2120 
2121  const QDomNodeList rangeList = bandElement.elementsByTagName( QStringLiteral( "noDataRange" ) );
2122 
2123  myNoDataRangeList.reserve( rangeList.size() );
2124  for ( int j = 0; j < rangeList.size(); ++j )
2125  {
2126  const QDomElement rangeElement = rangeList.at( j ).toElement();
2127  const QgsRasterRange myNoDataRange( rangeElement.attribute( QStringLiteral( "min" ) ).toDouble(),
2128  rangeElement.attribute( QStringLiteral( "max" ) ).toDouble() );
2129  QgsDebugMsgLevel( QStringLiteral( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ), 4 );
2130  myNoDataRangeList << myNoDataRange;
2131  }
2132  mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
2133  }
2134  }
2135 
2136  readStyleManager( layer_node );
2137 
2138  return res;
2139 }
2140 
2141 bool QgsRasterLayer::writeSymbology( QDomNode &layer_node, QDomDocument &document, QString &errorMessage,
2142  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2143 {
2144  Q_UNUSED( errorMessage )
2145  // TODO: implement categories for raster layer
2146 
2147  QDomElement layerElement = layer_node.toElement();
2148  writeCommonStyle( layerElement, document, context, categories );
2149 
2150  // Store pipe members into pipe element, in future, it will be
2151  // possible to add custom filters into the pipe
2152  QDomElement pipeElement = document.createElement( QStringLiteral( "pipe" ) );
2153 
2154  for ( int i = 0; i < mPipe->size(); i++ )
2155  {
2156  QgsRasterInterface *interface = mPipe->at( i );
2157  if ( !interface ) continue;
2158  interface->writeXml( document, pipeElement );
2159  }
2160 
2161  QDomElement elemDataDefinedProperties = document.createElement( QStringLiteral( "pipe-data-defined-properties" ) );
2162  mPipe->dataDefinedProperties().writeXml( elemDataDefinedProperties, QgsRasterPipe::propertyDefinitions() );
2163  layer_node.appendChild( elemDataDefinedProperties );
2164 
2165  QDomElement resamplingStageElement = document.createElement( QStringLiteral( "resamplingStage" ) );
2166  const QDomText resamplingStageText = document.createTextNode( resamplingStage() == Qgis::RasterResamplingStage::Provider ? QStringLiteral( "provider" ) : QStringLiteral( "resamplingFilter" ) );
2167  resamplingStageElement.appendChild( resamplingStageText );
2168  pipeElement.appendChild( resamplingStageElement );
2169 
2170  layer_node.appendChild( pipeElement );
2171 
2172  if ( !isValid() && !mOriginalStyleElement.isNull() )
2173  {
2174  QDomElement originalStyleElement = document.createElement( QStringLiteral( "originalStyle" ) );
2175  originalStyleElement.appendChild( mOriginalStyleElement );
2176  layer_node.appendChild( originalStyleElement );
2177  }
2178 
2179  // add blend mode node
2180  QDomElement blendModeElement = document.createElement( QStringLiteral( "blendMode" ) );
2181  const QDomText blendModeText = document.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
2182  blendModeElement.appendChild( blendModeText );
2183  layer_node.appendChild( blendModeElement );
2184 
2185  return true;
2186 }
2187 
2188 bool QgsRasterLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage,
2189  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2190 {
2191  return writeSymbology( node, doc, errorMessage, context, categories );
2192 } // bool QgsRasterLayer::writeSymbology
2193 
2194 /*
2195  * virtual
2196  * \note Called by QgsMapLayer::writeXml().
2197  */
2198 bool QgsRasterLayer::writeXml( QDomNode &layer_node,
2199  QDomDocument &document,
2200  const QgsReadWriteContext &context ) const
2201 {
2202  if ( !mDataProvider )
2203  return false;
2204 
2205  // first get the layer element so that we can append the type attribute
2206 
2207  QDomElement mapLayerNode = layer_node.toElement();
2208 
2209  if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
2210  {
2211  QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
2212  return false;
2213  }
2214 
2215  mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::RasterLayer ) );
2216 
2217  // add provider node
2218 
2219  QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
2220  const QDomText providerText = document.createTextNode( mProviderKey );
2221  provider.appendChild( providerText );
2222  layer_node.appendChild( provider );
2223 
2224  // User no data
2225  QDomElement noData = document.createElement( QStringLiteral( "noData" ) );
2226 
2227  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
2228  {
2229  QDomElement noDataRangeList = document.createElement( QStringLiteral( "noDataList" ) );
2230  noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), bandNo );
2231  noDataRangeList.setAttribute( QStringLiteral( "useSrcNoData" ), mDataProvider->useSourceNoDataValue( bandNo ) );
2232 
2233  const auto constUserNoDataValues = mDataProvider->userNoDataValues( bandNo );
2234  for ( const QgsRasterRange &range : constUserNoDataValues )
2235  {
2236  QDomElement noDataRange = document.createElement( QStringLiteral( "noDataRange" ) );
2237 
2238  noDataRange.setAttribute( QStringLiteral( "min" ), QgsRasterBlock::printValue( range.min() ) );
2239  noDataRange.setAttribute( QStringLiteral( "max" ), QgsRasterBlock::printValue( range.max() ) );
2240  noDataRangeList.appendChild( noDataRange );
2241  }
2242 
2243  noData.appendChild( noDataRangeList );
2244 
2245  }
2246  if ( noData.hasChildNodes() )
2247  {
2248  layer_node.appendChild( noData );
2249  }
2250 
2251  writeStyleManager( layer_node, document );
2252 
2253  serverProperties()->writeXml( layer_node, document );
2254 
2255  //write out the symbology
2256  QString errorMsg;
2257  return writeSymbology( layer_node, document, errorMsg, context );
2258 }
2259 
2260 // TODO: this should ideally go to gdal provider (together with most of encodedSource() + decodedSource())
2261 static bool _parseGpkgColons( const QString &src, QString &filename, QString &tablename )
2262 {
2263  // GDAL accepts the following input format: GPKG:filename:table
2264  // (GDAL won't accept quoted filename)
2265 
2266  QStringList lst = src.split( ':' );
2267  if ( lst.count() != 3 && lst.count() != 4 )
2268  return false;
2269 
2270  tablename = lst.last();
2271  if ( lst.count() == 3 )
2272  {
2273  filename = lst[1];
2274  return true;
2275  }
2276  else if ( lst.count() == 4 && lst[1].count() == 1 && ( lst[2][0] == '/' || lst[2][0] == '\\' ) )
2277  {
2278  // a bit of handling to make sure that filename C:\hello.gpkg is parsed correctly
2279  filename = lst[1] + ":" + lst[2];
2280  return true;
2281  }
2282  return false;
2283 }
2284 
2285 
2286 QString QgsRasterLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
2287 {
2288  QString src( source );
2289  bool handled = false;
2290 
2291  // Update path for subdataset
2292  if ( providerType() == QLatin1String( "gdal" ) )
2293  {
2294  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2295  {
2296  // NETCDF:filename:variable
2297  // filename can be quoted with " as it can contain colons
2298  const QRegularExpression netcdfEncodedRegExp( QRegularExpression::anchoredPattern( "NETCDF:(.+):([^:]+)" ) );
2299  const QRegularExpressionMatch match = netcdfEncodedRegExp.match( src );
2300  if ( match.hasMatch() )
2301  {
2302  QString filename = match.captured( 1 );
2303  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2304  filename = filename.mid( 1, filename.length() - 2 );
2305  src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + match.captured( 2 );
2306  handled = true;
2307  }
2308  }
2309  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2310  {
2311  // GPKG:filename:table
2312  QString filename, tablename;
2313  if ( _parseGpkgColons( src, filename, tablename ) )
2314  {
2315  filename = context.pathResolver().writePath( filename );
2316  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2317  handled = true;
2318  }
2319  }
2320  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2321  {
2322  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2323  // filename can be quoted with " as it can contain colons
2324  const QRegularExpression hdf4EncodedRegExp( QRegularExpression::anchoredPattern( "HDF4_SDS:([^:]+):(.+):([^:]+)" ) );
2325  const QRegularExpressionMatch match = hdf4EncodedRegExp.match( src );
2326  if ( match.hasMatch() )
2327  {
2328  QString filename = match.captured( 2 );
2329  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2330  filename = filename.mid( 1, filename.length() - 2 );
2331  src = "HDF4_SDS:" + match.captured( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + match.captured( 3 );
2332  handled = true;
2333  }
2334  }
2335  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2336  {
2337  // HDF5:file_name:subdataset
2338  // filename can be quoted with " as it can contain colons
2339  const QRegularExpression hdf5EncodedRegExp( QRegularExpression::anchoredPattern( "HDF5:(.+):([^:]+)" ) );
2340  const QRegularExpressionMatch match = hdf5EncodedRegExp.match( src );
2341  if ( match.hasMatch() )
2342  {
2343  QString filename = match.captured( 1 );
2344  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2345  filename = filename.mid( 1, filename.length() - 2 );
2346  src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + match.captured( 2 );
2347  handled = true;
2348  }
2349  }
2350  else if ( src.contains( QRegularExpression( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2351  {
2352  // NITF_IM:0:filename
2353  // RADARSAT_2_CALIB:?:filename
2354  const QRegularExpression nitfRadarsatEncodedRegExp( QRegularExpression::anchoredPattern( "([^:]+):([^:]+):(.+)" ) );
2355  const QRegularExpressionMatch match = nitfRadarsatEncodedRegExp.match( src );
2356  if ( match.hasMatch() )
2357  {
2358  src = match.captured( 1 ) + ':' + match.captured( 2 ) + ':' + context.pathResolver().writePath( match.captured( 3 ) );
2359  handled = true;
2360  }
2361  }
2362  }
2363  else if ( providerType() == "wms" )
2364  {
2365  // handle relative paths to XYZ tiles
2366  QgsDataSourceUri uri;
2367  uri.setEncodedUri( src );
2368  const QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2369  if ( srcUrl.isLocalFile() )
2370  {
2371  // relative path will become "file:./x.txt"
2372  const QString relSrcUrl = context.pathResolver().writePath( srcUrl.toLocalFile() );
2373  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2374  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
2375  src = uri.encodedUri();
2376  handled = true;
2377  }
2378  }
2379  else if ( providerType() == "virtualraster" )
2380  {
2381 
2383 
2384  for ( auto &it : decodedVirtualParams.rInputLayers )
2385  {
2386  it.uri = context.pathResolver().writePath( it.uri );
2387  }
2388  src = QgsRasterDataProvider::encodeVirtualRasterProviderUri( decodedVirtualParams ) ;
2389  }
2390 
2391  if ( !handled )
2392  src = context.pathResolver().writePath( src );
2393 
2394  return src;
2395 }
2396 
2397 QString QgsRasterLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
2398 {
2399  QString src( source );
2400 
2401  if ( provider == QLatin1String( "wms" ) )
2402  {
2403  // >>> BACKWARD COMPATIBILITY < 1.9
2404  // For project file backward compatibility we must support old format:
2405  // 1. mode: <url>
2406  // example: http://example.org/wms?
2407  // 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
2408  // example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
2409  // example: featureCount=10,http://example.org/wms?
2410  // example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
2411  // This is modified version of old QgsWmsProvider::parseUri
2412  // The new format has always params crs,format,layers,styles and that params
2413  // should not appear in old format url -> use them to identify version
2414  // XYZ tile layers do not need to contain crs,format params, but they have type=xyz
2415  if ( !src.contains( QLatin1String( "type=" ) ) &&
2416  !src.contains( QLatin1String( "crs=" ) ) && !src.contains( QLatin1String( "format=" ) ) )
2417  {
2418  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> converting to new format" ), 2 );
2419  QgsDataSourceUri uri;
2420  if ( !src.startsWith( QLatin1String( "http:" ) ) )
2421  {
2422  const QStringList parts = src.split( ',' );
2423  QStringListIterator iter( parts );
2424  while ( iter.hasNext() )
2425  {
2426  const QString item = iter.next();
2427  if ( item.startsWith( QLatin1String( "username=" ) ) )
2428  {
2429  uri.setUsername( item.mid( 9 ) );
2430  }
2431  else if ( item.startsWith( QLatin1String( "password=" ) ) )
2432  {
2433  uri.setPassword( item.mid( 9 ) );
2434  }
2435  else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
2436  {
2437  // in < 1.9 tiled= may apper in to variants:
2438  // tiled=width;height - non tiled mode, specifies max width and max height
2439  // tiled=width;height;resolutions-1;resolution2;... - tile mode
2440 
2441  QStringList params = item.mid( 6 ).split( ';' );
2442 
2443  if ( params.size() == 2 ) // non tiled mode
2444  {
2445  uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
2446  uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
2447  }
2448  else if ( params.size() > 2 ) // tiled mode
2449  {
2450  // resolutions are no more needed and size limit is not used for tiles
2451  // we have to tell to the provider however that it is tiled
2452  uri.setParam( QStringLiteral( "tileMatrixSet" ), QString() );
2453  }
2454  }
2455  else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
2456  {
2457  uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
2458  }
2459  else if ( item.startsWith( QLatin1String( "url=" ) ) )
2460  {
2461  uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
2462  }
2463  else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
2464  {
2465  uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
2466  }
2467  }
2468  }
2469  else
2470  {
2471  uri.setParam( QStringLiteral( "url" ), src );
2472  }
2473  src = uri.encodedUri();
2474  // At this point, the URI is obviously incomplete, we add additional params
2475  // in QgsRasterLayer::readXml
2476  }
2477  // <<< BACKWARD COMPATIBILITY < 1.9
2478 
2479  // handle relative paths to XYZ tiles
2480  QgsDataSourceUri uri;
2481  uri.setEncodedUri( src );
2482  const QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2483  if ( srcUrl.isLocalFile() ) // file-based URL? convert to relative path
2484  {
2485  const QString absSrcUrl = context.pathResolver().readPath( srcUrl.toLocalFile() );
2486  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2487  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
2488  src = uri.encodedUri();
2489  }
2490 
2491  }
2492  else
2493  {
2494  bool handled = false;
2495 
2496  if ( provider == QLatin1String( "gdal" ) )
2497  {
2498  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2499  {
2500  // NETCDF:filename:variable
2501  // filename can be quoted with " as it can contain colons
2502  const QRegularExpression netcdfDecodedRegExp( QRegularExpression::anchoredPattern( "NETCDF:(.+):([^:]+)" ) );
2503  const QRegularExpressionMatch match = netcdfDecodedRegExp.match( src );
2504  if ( match.hasMatch() )
2505  {
2506  QString filename = match.captured( 1 );
2507  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2508  filename = filename.mid( 1, filename.length() - 2 );
2509  src = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + match.captured( 2 );
2510  handled = true;
2511  }
2512  }
2513  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2514  {
2515  // GPKG:filename:table
2516  QString filename, tablename;
2517  if ( _parseGpkgColons( src, filename, tablename ) )
2518  {
2519  filename = context.pathResolver().readPath( filename );
2520  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2521  handled = true;
2522  }
2523  }
2524  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2525  {
2526  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2527  // filename can be quoted with " as it can contain colons
2528  const QRegularExpression hdf4DecodedRegExp( QRegularExpression::anchoredPattern( "HDF4_SDS:([^:]+):(.+):([^:]+)" ) );
2529  const QRegularExpressionMatch match = hdf4DecodedRegExp.match( src );
2530  if ( match.hasMatch() )
2531  {
2532  QString filename = match.captured( 2 );
2533  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2534  filename = filename.mid( 1, filename.length() - 2 );
2535  src = "HDF4_SDS:" + match.captured( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + match.captured( 3 );
2536  handled = true;
2537  }
2538  }
2539  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2540  {
2541  // HDF5:file_name:subdataset
2542  // filename can be quoted with " as it can contain colons
2543  const QRegularExpression hdf5DecodedRegExp( QRegularExpression::anchoredPattern( "HDF5:(.+):([^:]+)" ) );
2544  const QRegularExpressionMatch match = hdf5DecodedRegExp.match( src );
2545  if ( match.hasMatch() )
2546  {
2547  QString filename = match.captured( 1 );
2548  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2549  filename = filename.mid( 1, filename.length() - 2 );
2550  src = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + match.captured( 2 );
2551  handled = true;
2552  }
2553  }
2554  else if ( src.contains( QRegularExpression( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2555  {
2556  // NITF_IM:0:filename
2557  // RADARSAT_2_CALIB:?:filename
2558  const QRegularExpression niftRadarsatDecodedRegExp( QRegularExpression::anchoredPattern( "([^:]+):([^:]+):(.+)" ) );
2559  const QRegularExpressionMatch match = niftRadarsatDecodedRegExp.match( src );
2560  if ( match.hasMatch() )
2561  {
2562  src = match.captured( 1 ) + ':' + match.captured( 2 ) + ':' + context.pathResolver().readPath( match.captured( 3 ) );
2563  handled = true;
2564  }
2565  }
2566  }
2567 
2568  if ( provider == QLatin1String( "virtualraster" ) )
2569  {
2571 
2572  for ( auto &it : decodedVirtualParams.rInputLayers )
2573  {
2574  it.uri = context.pathResolver().readPath( it.uri );
2575  }
2576  src = QgsRasterDataProvider::encodeVirtualRasterProviderUri( decodedVirtualParams ) ;
2577  handled = true;
2578  }
2579 
2580  if ( !handled )
2581  src = context.pathResolver().readPath( src );
2582  }
2583 
2584  return src;
2585 }
2586 
2588 {
2589  if ( !mDataProvider ) return 0;
2590  return mDataProvider->xSize();
2591 }
2592 
2594 {
2595  if ( !mDataProvider ) return 0;
2596  return mDataProvider->ySize();
2597 }
2598 
2600 {
2601  mPipe->setResamplingStage( stage );
2602 }
2603 
2605 {
2606  return mPipe->resamplingStage();
2607 }
2608 
2610 //
2611 // Private methods
2612 //
2614 bool QgsRasterLayer::update()
2615 {
2616  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
2617  // Check if data changed
2618  if ( mDataProvider && mDataProvider->dataTimestamp() > mDataProvider->timestamp() )
2619  {
2620  QgsDebugMsgLevel( QStringLiteral( "reload data" ), 4 );
2621  closeDataProvider();
2622  init();
2623  const QgsDataProvider::ProviderOptions providerOptions;
2624  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2626  {
2628  }
2629  setDataProvider( mProviderKey, providerOptions, flags );
2630  emit dataChanged();
2631  }
2632  return isValid();
2633 }
static QString version()
Version string.
Definition: qgis.cpp:277
RasterResamplingStage
Stage at which raster resampling occurs.
Definition: qgis.h:502
@ Provider
Resampling occurs in Provider.
@ ResampleFilter
Resampling occurs in ResamplingFilter.
DataType
Raster data types.
Definition: qgis.h:121
@ 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:1914
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:1931
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:426
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:1969
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:310
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:1912
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1928
@ FlagReadExtentFromXml
Read extent from xml and skip get extent from provider.
Definition: qgsmaplayer.h:642
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:641
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:640
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:1974
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:1981
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
@ RasterLayer
Raster layer.
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:1578
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:1517
#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.