QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
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 "qgsmaptopixel.h"
28 #include "qgsmessagelog.h"
30 #include "qgspainting.h"
32 #include "qgspathresolver.h"
34 #include "qgsproviderregistry.h"
35 #include "qgsrasterdataprovider.h"
36 #include "qgsrasterdrawer.h"
37 #include "qgsrasteriterator.h"
38 #include "qgsrasterlayer.h"
39 #include "qgsrasterlayerrenderer.h"
40 #include "qgsrasterprojector.h"
41 #include "qgsrasterrange.h"
44 #include "qgsrastershader.h"
45 #include "qgsreadwritecontext.h"
46 #include "qgsrectangle.h"
47 #include "qgsrendercontext.h"
51 #include "qgssettings.h"
52 #include "qgssymbollayerutils.h"
53 #include "qgsgdalprovider.h"
57 #include "qgsruntimeprofiler.h"
58 #include "qgsmaplayerfactory.h"
59 
60 #include <cmath>
61 #include <cstdio>
62 #include <limits>
63 #include <typeinfo>
64 
65 #include <QApplication>
66 #include <QCursor>
67 #include <QDir>
68 #include <QDomElement>
69 #include <QDomNode>
70 #include <QFile>
71 #include <QFileInfo>
72 #include <QFont>
73 #include <QFontMetrics>
74 #include <QFrame>
75 #include <QImage>
76 #include <QLabel>
77 #include <QList>
78 #include <QMatrix>
79 #include <QMessageBox>
80 #include <QPainter>
81 #include <QPixmap>
82 #include <QRegExp>
83 #include <QSlider>
84 #include <QUrl>
85 
86 #define ERR(message) QGS_ERROR_MESSAGE(message,"Raster layer")
87 
88 const double QgsRasterLayer::SAMPLE_SIZE = 250000;
89 
96 
103 
106  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
107  , TRSTRING_NOT_SET( tr( "Not Set" ) )
108  , mTemporalProperties( new QgsRasterLayerTemporalProperties( this ) )
109 
110 {
111  init();
112  setValid( false );
113 }
114 
115 QgsRasterLayer::QgsRasterLayer( const QString &uri,
116  const QString &baseName,
117  const QString &providerKey,
118  const LayerOptions &options )
119  : QgsMapLayer( QgsMapLayerType::RasterLayer, baseName, uri )
120  // Constant that signals property not used.
121  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
122  , TRSTRING_NOT_SET( tr( "Not Set" ) )
123  , mTemporalProperties( new QgsRasterLayerTemporalProperties( this ) )
124 {
126 
127  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
128  setProviderType( providerKey );
129 
130  QgsDataProvider::ProviderOptions providerOptions { options.transformContext };
131 
132  setDataSource( uri, baseName, providerKey, providerOptions, options.loadDefaultStyle );
133 
134  if ( isValid() )
135  {
136  mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( mDataProvider->temporalCapabilities() );
137  }
138 
139 } // QgsRasterLayer ctor
140 
142 {
143  emit willBeDeleted();
144 
145  setValid( false );
146  // Note: provider and other interfaces are owned and deleted by pipe
147 }
148 
150 {
152  if ( mDataProvider )
153  {
154  options.transformContext = mDataProvider->transformContext();
155  }
156  QgsRasterLayer *layer = new QgsRasterLayer( source(), name(), mProviderKey, options );
157  QgsMapLayer::clone( layer );
158 
159  // do not clone data provider which is the first element in pipe
160  for ( int i = 1; i < mPipe.size(); i++ )
161  {
162  if ( mPipe.at( i ) )
163  layer->pipe()->set( mPipe.at( i )->clone() );
164  }
165 
166  return layer;
167 }
168 
170 //
171 // Static Methods and members
172 //
174 
175 bool QgsRasterLayer::isValidRasterFileName( const QString &fileNameQString, QString &retErrMsg )
176 {
177  bool myIsValid = QgsGdalProvider::isValidRasterFileName( fileNameQString, retErrMsg );
178  return myIsValid;
179 }
180 
181 bool QgsRasterLayer::isValidRasterFileName( QString const &fileNameQString )
182 {
183  QString retErrMsg;
184  return isValidRasterFileName( fileNameQString, retErrMsg );
185 }
186 
187 QDateTime QgsRasterLayer::lastModified( QString const &name )
188 {
189  QgsDebugMsgLevel( "name=" + name, 4 );
190  QDateTime t;
191 
192  QFileInfo fi( name );
193 
194  // Is it file?
195  if ( !fi.exists() )
196  return t;
197 
198  t = fi.lastModified();
199 
200  QgsDebugMsgLevel( "last modified = " + t.toString(), 4 );
201 
202  return t;
203 }
204 
205 void QgsRasterLayer::setDataProvider( const QString &provider )
206 {
208 }
209 
210 // typedef for the QgsDataProvider class factory
211 typedef QgsDataProvider *classFactoryFunction_t( const QString *, const QgsDataProvider::ProviderOptions &options );
212 
214 //
215 // Non Static Public methods
216 //
218 
220 {
221  if ( !mDataProvider ) return 0;
222  return mDataProvider->bandCount();
223 }
224 
225 QString QgsRasterLayer::bandName( int bandNo ) const
226 {
227  if ( !mDataProvider ) return QString();
228  return mDataProvider->generateBandName( bandNo );
229 }
230 
231 void QgsRasterLayer::setRendererForDrawingStyle( QgsRaster::DrawingStyle drawingStyle )
232 {
233  setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( drawingStyle, mDataProvider ) );
234 }
235 
237 {
238  return mDataProvider;
239 }
240 
242 {
243  return mDataProvider;
244 }
245 
247 {
248  if ( mDataProvider )
249  {
250  mDataProvider->reloadData();
251  }
252 }
253 
255 {
256  return new QgsRasterLayerRenderer( this, rendererContext );
257 }
258 
259 
260 void QgsRasterLayer::draw( QPainter *theQPainter,
261  QgsRasterViewPort *rasterViewPort,
262  const QgsMapToPixel *qgsMapToPixel )
263 {
264  QgsDebugMsgLevel( QStringLiteral( " 3 arguments" ), 4 );
265  QElapsedTimer time;
266  time.start();
267  //
268  //
269  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
270  // so that we can maximise performance of the rendering process. So now we check which drawing
271  // procedure to use :
272  //
273 
274  QgsRasterProjector *projector = mPipe.projector();
275  bool restoreOldResamplingStage = false;
276  QgsRasterPipe::ResamplingStage oldResamplingState = resamplingStage();
277  // TODO add a method to interface to get provider and get provider
278  // params in QgsRasterProjector
279 
280  if ( projector )
281  {
282  // Force provider resampling if reprojection is needed
283  if ( mDataProvider != nullptr &&
285  rasterViewPort->mSrcCRS != rasterViewPort->mDestCRS &&
286  oldResamplingState != QgsRasterPipe::ResamplingStage::Provider )
287  {
288  restoreOldResamplingStage = true;
290  }
291  projector->setCrs( rasterViewPort->mSrcCRS, rasterViewPort->mDestCRS, rasterViewPort->mTransformContext );
292  }
293 
294  // Drawer to pipe?
295  QgsRasterIterator iterator( mPipe.last() );
296  QgsRasterDrawer drawer( &iterator );
297  drawer.draw( theQPainter, rasterViewPort, qgsMapToPixel );
298 
299  if ( restoreOldResamplingStage )
300  {
301  setResamplingStage( oldResamplingState );
302  }
303 
304  QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
305 }
306 
308 {
310  return renderer ? renderer->legendSymbologyItems() : QList< QPair< QString, QColor > >();;
311 }
312 
314 {
315  QgsLayerMetadataFormatter htmlFormatter( metadata() );
316  QString myMetadata = QStringLiteral( "<html><head></head>\n<body>\n" );
317 
318  // Begin Provider section
319  myMetadata += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
320  QStringLiteral( "<table class=\"list-view\">\n" ) %
321 
322  // name
323  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Name" ) % QStringLiteral( "</td><td>" ) % name() % QStringLiteral( "</td></tr>\n" );
324 
325  // local path
326  QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( mProviderKey, publicSource() );
327  QString path;
328  bool isLocalPath = false;
329  if ( uriComponents.contains( QStringLiteral( "path" ) ) )
330  {
331  path = uriComponents[QStringLiteral( "path" )].toString();
332  if ( QFile::exists( path ) )
333  {
334  isLocalPath = true;
335  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Path" ) % QStringLiteral( "</td><td>%1" ).arg( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( path ).toString(), QDir::toNativeSeparators( path ) ) ) + QStringLiteral( "</td></tr>\n" );
336  }
337  }
338  if ( uriComponents.contains( QStringLiteral( "url" ) ) )
339  {
340  const QString url = uriComponents[QStringLiteral( "url" )].toString();
341  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "URL" ) % QStringLiteral( "</td><td>%1" ).arg( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( QUrl( url ).toString(), url ) ) + QStringLiteral( "</td></tr>\n" );
342  }
343 
344  // data source
345  if ( publicSource() != path || !isLocalPath )
346  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>%1" ).arg( publicSource() != path ? publicSource() : path ) + QStringLiteral( "</td></tr>\n" );
347 
348  // EPSG
349  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "CRS" ) + QStringLiteral( "</td><td>" );
350  if ( crs().isValid() )
351  {
352  myMetadata += crs().userFriendlyIdentifier( QgsCoordinateReferenceSystem::FullString ) % QStringLiteral( " - " );
353  if ( crs().isGeographic() )
354  myMetadata += tr( "Geographic" );
355  else
356  myMetadata += tr( "Projected" );
357  }
358  myMetadata += QStringLiteral( "</td></tr>\n" ) %
359 
360  // Extent
361  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Extent" ) % QStringLiteral( "</td><td>" ) % extent().toString() % QStringLiteral( "</td></tr>\n" ) %
362 
363  // unit
364  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Unit" ) % QStringLiteral( "</td><td>" ) % QgsUnitTypes::toString( crs().mapUnits() ) % QStringLiteral( "</td></tr>\n" ) %
365 
366  // Raster Width
367  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Width" ) % QStringLiteral( "</td><td>" );
368  if ( dataProvider()->capabilities() & QgsRasterDataProvider::Size )
369  myMetadata += QString::number( width() );
370  else
371  myMetadata += tr( "n/a" );
372  myMetadata += QStringLiteral( "</td></tr>\n" ) %
373 
374  // Raster height
375  QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Height" ) + QStringLiteral( "</td><td>" );
376  if ( dataProvider()->capabilities() & QgsRasterDataProvider::Size )
377  myMetadata += QString::number( height() );
378  else
379  myMetadata += tr( "n/a" );
380  myMetadata += QStringLiteral( "</td></tr>\n" ) %
381 
382  // Data type
383  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Data type" ) % QStringLiteral( "</td><td>" );
384  // Just use the first band
385  switch ( mDataProvider->sourceDataType( 1 ) )
386  {
387  case Qgis::Byte:
388  myMetadata += tr( "Byte - Eight bit unsigned integer" );
389  break;
390  case Qgis::UInt16:
391  myMetadata += tr( "UInt16 - Sixteen bit unsigned integer " );
392  break;
393  case Qgis::Int16:
394  myMetadata += tr( "Int16 - Sixteen bit signed integer " );
395  break;
396  case Qgis::UInt32:
397  myMetadata += tr( "UInt32 - Thirty two bit unsigned integer " );
398  break;
399  case Qgis::Int32:
400  myMetadata += tr( "Int32 - Thirty two bit signed integer " );
401  break;
402  case Qgis::Float32:
403  myMetadata += tr( "Float32 - Thirty two bit floating point " );
404  break;
405  case Qgis::Float64:
406  myMetadata += tr( "Float64 - Sixty four bit floating point " );
407  break;
408  case Qgis::CInt16:
409  myMetadata += tr( "CInt16 - Complex Int16 " );
410  break;
411  case Qgis::CInt32:
412  myMetadata += tr( "CInt32 - Complex Int32 " );
413  break;
414  case Qgis::CFloat32:
415  myMetadata += tr( "CFloat32 - Complex Float32 " );
416  break;
417  case Qgis::CFloat64:
418  myMetadata += tr( "CFloat64 - Complex Float64 " );
419  break;
420  default:
421  myMetadata += tr( "Could not determine raster data type." );
422  }
423  myMetadata += QStringLiteral( "</td></tr>\n" ) %
424 
425  // Insert provider-specific (e.g. WMS-specific) metadata
426  mDataProvider->htmlMetadata() %
427 
428  // End Provider section
429  QStringLiteral( "</table>\n<br><br>" ) %
430 
431  // Identification section
432  QStringLiteral( "<h1>" ) % tr( "Identification" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
433  htmlFormatter.identificationSectionHtml() %
434  QStringLiteral( "<br><br>\n" ) %
435 
436  // extent section
437  QStringLiteral( "<h1>" ) % tr( "Extent" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
438  htmlFormatter.extentSectionHtml( ) %
439  QStringLiteral( "<br><br>\n" ) %
440 
441  // Start the Access section
442  QStringLiteral( "<h1>" ) % tr( "Access" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
443  htmlFormatter.accessSectionHtml( ) %
444  QStringLiteral( "<br><br>\n" ) %
445 
446  // Bands section
447  QStringLiteral( "</table>\n<br><br><h1>" ) % tr( "Bands" ) % QStringLiteral( "</h1>\n<hr>\n<table class=\"list-view\">\n" ) %
448 
449  // Band count
450  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Band count" ) % QStringLiteral( "</td><td>" ) % QString::number( bandCount() ) % QStringLiteral( "</td></tr>\n" );
451 
452  // Band table
453  myMetadata += QStringLiteral( "</table>\n<br><table width=\"100%\" class=\"tabular-view\">\n" ) %
454  QStringLiteral( "<tr><th>" ) % tr( "Number" ) % QStringLiteral( "</th><th>" ) % tr( "Band" ) % QStringLiteral( "</th><th>" ) % tr( "No-Data" ) % QStringLiteral( "</th><th>" ) %
455  tr( "Min" ) % QStringLiteral( "</th><th>" ) % tr( "Max" ) % QStringLiteral( "</th></tr>\n" );
456 
457  QgsRasterDataProvider *provider = const_cast< QgsRasterDataProvider * >( mDataProvider );
458  for ( int i = 1; i <= bandCount(); i++ )
459  {
460  QString rowClass;
461  if ( i % 2 )
462  rowClass = QStringLiteral( "class=\"odd-row\"" );
463 
464  myMetadata += QStringLiteral( "<tr " ) % rowClass % QStringLiteral( "><td>" ) % QString::number( i ) % QStringLiteral( "</td><td>" ) % bandName( i ) % QStringLiteral( "</td><td>" );
465 
466  if ( dataProvider()->sourceHasNoDataValue( i ) )
467  myMetadata += QString::number( dataProvider()->sourceNoDataValue( i ) );
468  else
469  myMetadata += tr( "n/a" );
470  myMetadata += QLatin1String( "</td>" );
471 
472  if ( provider->hasStatistics( i ) )
473  {
474  QgsRasterBandStats myRasterBandStats = provider->bandStatistics( i );
475  myMetadata += QStringLiteral( "<td>" ) % QString::number( myRasterBandStats.minimumValue, 'f', 10 ) % QStringLiteral( "</td>" ) %
476  QStringLiteral( "<td>" ) % QString::number( myRasterBandStats.maximumValue, 'f', 10 ) % QStringLiteral( "</td>" );
477  }
478  else
479  {
480  myMetadata += QStringLiteral( "<td>" ) % tr( "n/a" ) % QStringLiteral( "</td><td>" ) % tr( "n/a" ) % QStringLiteral( "</td>" );
481  }
482 
483  myMetadata += QLatin1String( "</tr>\n" );
484  }
485 
486  //close previous bands table
487  myMetadata += QStringLiteral( "</table>\n<br><br>" ) %
488 
489  // Start the contacts section
490  QStringLiteral( "<h1>" ) % tr( "Contacts" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
491  htmlFormatter.contactsSectionHtml( ) %
492  QStringLiteral( "<br><br>\n" ) %
493 
494  // Start the links section
495  QStringLiteral( "<h1>" ) % tr( "References" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
496  htmlFormatter.linksSectionHtml( ) %
497  QStringLiteral( "<br><br>\n" ) %
498 
499  // Start the history section
500  QStringLiteral( "<h1>" ) % tr( "History" ) % QStringLiteral( "</h1>\n<hr>\n" ) %
501  htmlFormatter.historySectionHtml( ) %
502  QStringLiteral( "<br><br>\n" ) %
503 
504  QStringLiteral( "\n</body>\n</html>\n" );
505  return myMetadata;
506 }
507 
508 QPixmap QgsRasterLayer::paletteAsPixmap( int bandNumber )
509 {
510  //TODO: This function should take dimensions
511  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
512 
513  // Only do this for the GDAL provider?
514  // Maybe WMS can do this differently using QImage::numColors and QImage::color()
515  if ( mDataProvider &&
516  mDataProvider->colorInterpretation( bandNumber ) == QgsRaster::PaletteIndex )
517  {
518  QgsDebugMsgLevel( QStringLiteral( "....found paletted image" ), 4 );
519  QgsColorRampShader myShader;
520  QList<QgsColorRampShader::ColorRampItem> myColorRampItemList = mDataProvider->colorTable( bandNumber );
521  if ( !myColorRampItemList.isEmpty() )
522  {
523  QgsDebugMsgLevel( QStringLiteral( "....got color ramp item list" ), 4 );
524  myShader.setColorRampItemList( myColorRampItemList );
526  // Draw image
527  int mySize = 100;
528  QPixmap myPalettePixmap( mySize, mySize );
529  QPainter myQPainter( &myPalettePixmap );
530 
531  QImage myQImage = QImage( mySize, mySize, QImage::Format_RGB32 );
532  myQImage.fill( 0 );
533  myPalettePixmap.fill();
534 
535  double myStep = ( static_cast< double >( myColorRampItemList.size() ) - 1 ) / static_cast< double >( mySize * mySize );
536  double myValue = 0.0;
537  for ( int myRow = 0; myRow < mySize; myRow++ )
538  {
539  QRgb *myLineBuffer = reinterpret_cast< QRgb * >( myQImage.scanLine( myRow ) );
540  for ( int myCol = 0; myCol < mySize; myCol++ )
541  {
542  myValue = myStep * static_cast< double >( myCol + myRow * mySize );
543  int c1, c2, c3, c4;
544  myShader.shade( myValue, &c1, &c2, &c3, &c4 );
545  myLineBuffer[ myCol ] = qRgba( c1, c2, c3, c4 );
546  }
547  }
548 
549  myQPainter.drawImage( 0, 0, myQImage );
550  return myPalettePixmap;
551  }
552  QPixmap myNullPixmap;
553  return myNullPixmap;
554  }
555  else
556  {
557  //invalid layer was requested
558  QPixmap myNullPixmap;
559  return myNullPixmap;
560  }
561 }
562 
564 {
565  return mProviderKey;
566 }
567 
569 {
570 // We return one raster pixel per map unit pixel
571 // One raster pixel can have several raster units...
572 
573 // We can only use one of the mGeoTransform[], so go with the
574 // horisontal one.
575 
576  if ( mDataProvider &&
577  mDataProvider->capabilities() & QgsRasterDataProvider::Size && !qgsDoubleNear( mDataProvider->xSize(), 0.0 ) )
578  {
579  return mDataProvider->extent().width() / mDataProvider->xSize();
580  }
581  return 1;
582 }
583 
585 {
586  if ( mDataProvider &&
587  mDataProvider->capabilities() & QgsRasterDataProvider::Size && !qgsDoubleNear( mDataProvider->ySize(), 0.0 ) )
588  {
589  return mDataProvider->extent().height() / mDataProvider->ySize();
590  }
591  return 1;
592 }
593 
594 void QgsRasterLayer::setOpacity( double opacity )
595 {
596  if ( !mPipe.renderer() || mPipe.renderer()->opacity() == opacity )
597  return;
598 
599  mPipe.renderer()->setOpacity( opacity );
600  emit opacityChanged( opacity );
601  emit styleChanged();
602 }
603 
605 {
606  return mPipe.renderer() ? mPipe.renderer()->opacity() : 1.0;
607 }
608 
609 void QgsRasterLayer::init()
610 {
611  mRasterType = QgsRasterLayer::GrayOrUndefined;
612 
614 
615  setRendererForDrawingStyle( QgsRaster::UndefinedDrawingStyle );
616 
617  //Initialize the last view port structure, should really be a class
618  mLastViewPort.mWidth = 0;
619  mLastViewPort.mHeight = 0;
620 }
621 
622 void QgsRasterLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
623 {
624  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
625  setValid( false ); // assume the layer is invalid until we determine otherwise
626 
627  mPipe.remove( mDataProvider ); // deletes if exists
628  mDataProvider = nullptr;
629 
630  // XXX should I check for and possibly delete any pre-existing providers?
631  // XXX How often will that scenario occur?
632 
633  mProviderKey = provider;
634  // set the layer name (uppercase first character)
635  if ( ! mLayerName.isEmpty() ) // XXX shouldn't this happen in parent?
636  {
637  setName( mLayerName );
638  }
639 
640  //mBandCount = 0;
641 
642  std::unique_ptr< QgsScopedRuntimeProfile > profile;
643  if ( QgsApplication::profiler()->groupIsActive( QStringLiteral( "projectload" ) ) )
644  profile = qgis::make_unique< QgsScopedRuntimeProfile >( tr( "Create %1 provider" ).arg( provider ), QStringLiteral( "projectload" ) );
645 
646  mDataProvider = qobject_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mProviderKey, mDataSource, options, flags ) );
647  if ( !mDataProvider )
648  {
649  //QgsMessageLog::logMessage( tr( "Cannot instantiate the data provider" ), tr( "Raster" ) );
650  appendError( ERR( tr( "Cannot instantiate the '%1' data provider" ).arg( mProviderKey ) ) );
651  return;
652  }
653  QgsDebugMsgLevel( QStringLiteral( "Data provider created" ), 4 );
654  mDataProvider->setParent( this );
655 
656  // Set data provider into pipe even if not valid so that it is deleted with pipe (with layer)
657  mPipe.set( mDataProvider );
658  if ( !mDataProvider->isValid() )
659  {
660  setError( mDataProvider->error() );
661  appendError( ERR( tr( "Provider is not valid (provider: %1, URI: %2" ).arg( mProviderKey, mDataSource ) ) );
662  return;
663  }
664 
666  {
667  setMetadata( mDataProvider->layerMetadata() );
668  QgsDebugMsgLevel( QStringLiteral( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 );
669  }
670 
671  if ( provider == QLatin1String( "gdal" ) )
672  {
673  // make sure that the /vsigzip or /vsizip is added to uri, if applicable
674  mDataSource = mDataProvider->dataSourceUri();
675  }
676 
677  // get the extent
678  QgsRectangle mbr = mDataProvider->extent();
679 
680  // show the extent
681  QgsDebugMsgLevel( "Extent of layer: " + mbr.toString(), 4 );
682  // store the extent
683  setExtent( mbr );
684 
685  // upper case the first letter of the layer name
686  QgsDebugMsgLevel( "mLayerName: " + name(), 4 );
687 
688  // set up the raster drawing style
689  // Do not set any 'sensible' style here, the style is set later
690 
691  // Setup source CRS
692  setCrs( QgsCoordinateReferenceSystem( mDataProvider->crs() ) );
693 
694  QgsDebugMsgLevel( "using wkt:\n" + crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), 4 );
695 
696  //defaults - Needs to be set after the Contrast list has been build
697  //Try to read the default contrast enhancement from the config file
698 
699  //decide what type of layer this is...
700  //TODO Change this to look at the color interp and palette interp to decide which type of layer it is
701  QgsDebugMsgLevel( "bandCount = " + QString::number( mDataProvider->bandCount() ), 4 );
702  QgsDebugMsgLevel( "dataType = " + QString::number( mDataProvider->dataType( 1 ) ), 4 );
703  if ( ( mDataProvider->bandCount() > 1 ) )
704  {
705  // handle singleband gray with alpha
706  if ( mDataProvider->bandCount() == 2
707  && ( ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::GrayIndex
708  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::AlphaBand )
709  || ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::AlphaBand
710  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::GrayIndex ) ) )
711  {
712  mRasterType = GrayOrUndefined;
713  }
714  else
715  {
716  mRasterType = Multiband;
717  }
718  }
719  else if ( mDataProvider->dataType( 1 ) == Qgis::ARGB32
720  || mDataProvider->dataType( 1 ) == Qgis::ARGB32_Premultiplied )
721  {
722  mRasterType = ColorLayer;
723  }
724  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
725  {
726  mRasterType = Palette;
727  }
728  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
729  {
730  mRasterType = Palette;
731  }
732  else
733  {
734  mRasterType = GrayOrUndefined;
735  }
736 
737  QgsDebugMsgLevel( "mRasterType = " + QString::number( mRasterType ), 4 );
738  if ( mRasterType == ColorLayer )
739  {
740  QgsDebugMsgLevel( "Setting drawing style to SingleBandColorDataStyle " + QString::number( QgsRaster::SingleBandColorDataStyle ), 4 );
741  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
742  }
743  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
744  {
745  setRendererForDrawingStyle( QgsRaster::PalettedColor ); //sensible default
746  }
747  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
748  {
749  setRendererForDrawingStyle( QgsRaster::SingleBandPseudoColor );
750  // Load color table
751  QList<QgsColorRampShader::ColorRampItem> colorTable = mDataProvider->colorTable( 1 );
753  if ( r )
754  {
755  // TODO: this should go somewhere else
756  QgsRasterShader *shader = new QgsRasterShader();
757  QgsColorRampShader *colorRampShader = new QgsColorRampShader();
759  colorRampShader->setColorRampItemList( colorTable );
760  shader->setRasterShaderFunction( colorRampShader );
761  r->setShader( shader );
762  }
763  }
764  else if ( mRasterType == Multiband )
765  {
766  setRendererForDrawingStyle( QgsRaster::MultiBandColor ); //sensible default
767  }
768  else //GrayOrUndefined
769  {
770  setRendererForDrawingStyle( QgsRaster::SingleBandGray ); //sensible default
771  }
772 
773  // Auto set alpha band
774  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
775  {
776  if ( mDataProvider->colorInterpretation( bandNo ) == QgsRaster::AlphaBand )
777  {
778  if ( auto *lRenderer = mPipe.renderer() )
779  {
780  lRenderer->setAlphaBand( bandNo );
781  }
782  break;
783  }
784  }
785 
786  // brightness filter
788  mPipe.set( brightnessFilter );
789 
790  // hue/saturation filter
792  mPipe.set( hueSaturationFilter );
793 
794  // resampler (must be after renderer)
796  mPipe.set( resampleFilter );
797 
799  {
800  QgsSettings settings;
801  QString resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedInResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
802  if ( resampling == QLatin1String( "bilinear" ) )
803  {
806  }
807  else if ( resampling == QLatin1String( "cubic" ) )
808  {
811  }
812  resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedOutResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
813  if ( resampling == QLatin1String( "bilinear" ) )
814  {
817  }
818 
819  const double maxOversampling = settings.value( QStringLiteral( "/Raster/defaultOversampling" ), 2.0 ).toDouble();
820  resampleFilter->setMaxOversampling( maxOversampling );
821  mDataProvider->setMaxOversampling( maxOversampling );
822 
824  settings.value( QStringLiteral( "/Raster/defaultEarlyResampling" ), false ).toBool() )
825  {
827  }
828  else
829  {
831  }
832  }
833 
834  // projector (may be anywhere in pipe)
835  QgsRasterProjector *projector = new QgsRasterProjector;
836  mPipe.set( projector );
837 
838  // Set default identify format - use the richest format available
839  int capabilities = mDataProvider->capabilities();
841  if ( capabilities & QgsRasterInterface::IdentifyHtml )
842  {
843  // HTML is usually richest
844  identifyFormat = QgsRaster::IdentifyFormatHtml;
845  }
846  else if ( capabilities & QgsRasterInterface::IdentifyFeature )
847  {
848  identifyFormat = QgsRaster::IdentifyFormatFeature;
849  }
850  else if ( capabilities & QgsRasterInterface::IdentifyText )
851  {
852  identifyFormat = QgsRaster::IdentifyFormatText;
853  }
854  else if ( capabilities & QgsRasterInterface::IdentifyValue )
855  {
856  identifyFormat = QgsRaster::IdentifyFormatValue;
857  }
858  setCustomProperty( QStringLiteral( "identify/format" ), QgsRasterDataProvider::identifyFormatName( identifyFormat ) );
859 
860  // Store timestamp
861  // TODO move to provider
862  mLastModified = lastModified( mDataSource );
863 
864  // Do a passthrough for the status bar text
865  connect( mDataProvider, &QgsRasterDataProvider::statusChanged, this, &QgsRasterLayer::statusChanged );
866 
867  //mark the layer as valid
868  setValid( true );
869 
870  if ( mDataProvider->supportsSubsetString() )
871  connect( this, &QgsRasterLayer::subsetStringChanged, this, &QgsMapLayer::configChanged, Qt::UniqueConnection );
872  else
874 
875 
876  QgsDebugMsgLevel( QStringLiteral( "exiting." ), 4 );
877 
878 }
879 
880 void QgsRasterLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag )
881 {
882  bool hadRenderer( renderer() );
883 
884  QDomImplementation domImplementation;
885  QDomDocumentType documentType;
886  QString errorMsg;
887 
888  // Store the original style
889  if ( hadRenderer && ! loadDefaultStyleFlag )
890  {
891  documentType = domImplementation.createDocumentType(
892  QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
893 
894  QDomDocument doc = QDomDocument( documentType );
895  QDomElement styleElem = doc.createElement( QStringLiteral( "qgis" ) );
896  styleElem.setAttribute( QStringLiteral( "version" ), Qgis::version() );
897  QgsReadWriteContext writeContext;
898  if ( ! writeSymbology( styleElem, doc, errorMsg, writeContext ) )
899  {
900  QgsDebugMsg( QStringLiteral( "Could not store symbology for layer %1: %2" )
901  .arg( name(),
902  errorMsg ) );
903  }
904  else
905  {
906  doc.appendChild( styleElem );
907 
908  mOriginalStyleDocument = doc;
909  mOriginalStyleElement = styleElem;
910  }
911  }
912 
913  if ( mDataProvider )
914  closeDataProvider();
915 
916  init();
917 
918  for ( int i = mPipe.size() - 1; i >= 0; --i )
919  {
920  mPipe.remove( i );
921  }
922 
923  mDataSource = dataSource;
924  mLayerName = baseName;
925 
926  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
928  {
930  }
931 
932  setDataProvider( provider, options, flags );
933 
934  if ( mDataProvider )
935  mDataProvider->setDataSourceUri( mDataSource );
936 
937  if ( isValid() )
938  {
939  // load default style
940  bool defaultLoadedFlag = false;
941  bool restoredStyle = false;
942  if ( loadDefaultStyleFlag )
943  {
944  loadDefaultStyle( defaultLoadedFlag );
945  }
946  else if ( !mOriginalStyleElement.isNull() ) // Restore the style
947  {
948  QgsReadWriteContext readContext;
949  if ( ! readSymbology( mOriginalStyleElement, errorMsg, readContext ) )
950  {
951  QgsDebugMsg( QStringLiteral( "Could not restore symbology for layer %1: %2" )
952  .arg( name() )
953  .arg( errorMsg ) );
954 
955  }
956  else
957  {
958  restoredStyle = true;
959  emit repaintRequested();
960  emit styleChanged();
961  emit rendererChanged();
962  }
963  }
964 
965  if ( !defaultLoadedFlag && !restoredStyle )
966  {
968  }
969  }
970  emit dataSourceChanged();
971  emit dataChanged();
972 }
973 
974 void QgsRasterLayer::closeDataProvider()
975 {
976  setValid( false );
977  mPipe.remove( mDataProvider );
978  mDataProvider = nullptr;
979 }
980 
981 void QgsRasterLayer::computeMinMax( int band,
982  const QgsRasterMinMaxOrigin &mmo,
984  const QgsRectangle &extent,
985  int sampleSize,
986  double &min, double &max )
987 {
988 
989  min = std::numeric_limits<double>::quiet_NaN();
990  max = std::numeric_limits<double>::quiet_NaN();
991  if ( !mDataProvider )
992  return;
993 
994  if ( limits == QgsRasterMinMaxOrigin::MinMax )
995  {
996  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Min | QgsRasterBandStats::Max, extent, sampleSize );
997  // Check if statistics were actually gathered, None means a failure
998  if ( myRasterBandStats.statsGathered == QgsRasterBandStats::Stats::None )
999  {
1000  // Best guess we can do
1001  switch ( mDataProvider->dataType( band ) )
1002  {
1003  case Qgis::DataType::Byte:
1004  {
1005  myRasterBandStats.minimumValue = 0;
1006  myRasterBandStats.maximumValue = 255;
1007  break;
1008  }
1009  case Qgis::DataType::UInt16:
1010  {
1011  myRasterBandStats.minimumValue = 0;
1012  myRasterBandStats.maximumValue = std::numeric_limits<uint16_t>::max();
1013  break;
1014  }
1015  case Qgis::DataType::UInt32:
1016  {
1017  myRasterBandStats.minimumValue = 0;
1018  myRasterBandStats.maximumValue = std::numeric_limits<uint32_t>::max();
1019  break;
1020  }
1021  case Qgis::DataType::Int16:
1022  case Qgis::DataType::CInt16:
1023  {
1024  myRasterBandStats.minimumValue = std::numeric_limits<int16_t>::lowest();
1025  myRasterBandStats.maximumValue = std::numeric_limits<int16_t>::max();
1026  break;
1027  }
1028  case Qgis::DataType::Int32:
1029  case Qgis::DataType::CInt32:
1030  {
1031  myRasterBandStats.minimumValue = std::numeric_limits<int32_t>::lowest();
1032  myRasterBandStats.maximumValue = std::numeric_limits<int32_t>::max();
1033  break;
1034  }
1035  case Qgis::DataType::Float32:
1036  case Qgis::DataType::CFloat32:
1037  {
1038  myRasterBandStats.minimumValue = std::numeric_limits<float_t>::lowest();
1039  myRasterBandStats.maximumValue = std::numeric_limits<float_t>::max();
1040  break;
1041  }
1042  case Qgis::DataType::Float64:
1043  case Qgis::DataType::CFloat64:
1044  {
1045  myRasterBandStats.minimumValue = std::numeric_limits<double_t>::lowest();
1046  myRasterBandStats.maximumValue = std::numeric_limits<double_t>::max();
1047  break;
1048  }
1049  case Qgis::DataType::ARGB32:
1050  case Qgis::DataType::ARGB32_Premultiplied:
1051  case Qgis::DataType::UnknownDataType:
1052  {
1053  // Nothing to guess
1054  break;
1055  }
1056  }
1057  }
1058  min = myRasterBandStats.minimumValue;
1059  max = myRasterBandStats.maximumValue;
1060  }
1061  else if ( limits == QgsRasterMinMaxOrigin::StdDev )
1062  {
1063  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, extent, sampleSize );
1064  min = myRasterBandStats.mean - ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
1065  max = myRasterBandStats.mean + ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
1066  }
1067  else if ( limits == QgsRasterMinMaxOrigin::CumulativeCut )
1068  {
1069  const double myLower = mmo.cumulativeCutLower();
1070  const double myUpper = mmo.cumulativeCutUpper();
1071  QgsDebugMsgLevel( QStringLiteral( "myLower = %1 myUpper = %2" ).arg( myLower ).arg( myUpper ), 4 );
1072  mDataProvider->cumulativeCut( band, myLower, myUpper, min, max, extent, sampleSize );
1073  }
1074  QgsDebugMsgLevel( QStringLiteral( "band = %1 min = %2 max = %3" ).arg( band ).arg( min ).arg( max ), 4 );
1075 
1076 }
1077 
1079 {
1080  return mDataProvider ? mDataProvider->ignoreExtents() : false;
1081 }
1082 
1084 {
1085  return mTemporalProperties;
1086 }
1087 
1089 {
1091  limits,
1092  extent,
1093  sampleSize,
1094  generateLookupTableFlag,
1095  mPipe.renderer() );
1096 }
1097 
1100  const QgsRectangle &extent,
1101  int sampleSize,
1102  bool generateLookupTableFlag,
1103  QgsRasterRenderer *rasterRenderer )
1104 {
1105  QgsDebugMsgLevel( QStringLiteral( "theAlgorithm = %1 limits = %2 extent.isEmpty() = %3" ).arg( algorithm ).arg( limits ).arg( extent.isEmpty() ), 4 );
1106  if ( !rasterRenderer || !mDataProvider )
1107  {
1108  return;
1109  }
1110 
1111  QList<int> myBands;
1112  QList<QgsContrastEnhancement *> myEnhancements;
1113  QgsRasterMinMaxOrigin myMinMaxOrigin;
1114  QgsRasterRenderer *myRasterRenderer = nullptr;
1115  QgsSingleBandGrayRenderer *myGrayRenderer = nullptr;
1116  QgsSingleBandPseudoColorRenderer *myPseudoColorRenderer = nullptr;
1117  QgsMultiBandColorRenderer *myMultiBandRenderer = nullptr;
1118  QString rendererType = rasterRenderer->type();
1119  if ( rendererType == QLatin1String( "singlebandgray" ) )
1120  {
1121  myGrayRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer );
1122  if ( !myGrayRenderer )
1123  {
1124  return;
1125  }
1126  myBands << myGrayRenderer->grayBand();
1127  myRasterRenderer = myGrayRenderer;
1128  myMinMaxOrigin = myGrayRenderer->minMaxOrigin();
1129  }
1130  else if ( rendererType == QLatin1String( "multibandcolor" ) )
1131  {
1132  myMultiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer );
1133  if ( !myMultiBandRenderer )
1134  {
1135  return;
1136  }
1137  myBands << myMultiBandRenderer->redBand() << myMultiBandRenderer->greenBand() << myMultiBandRenderer->blueBand();
1138  myRasterRenderer = myMultiBandRenderer;
1139  myMinMaxOrigin = myMultiBandRenderer->minMaxOrigin();
1140  }
1141  else if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
1142  {
1143  myPseudoColorRenderer = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer );
1144  if ( !myPseudoColorRenderer )
1145  {
1146  return;
1147  }
1148  myBands << myPseudoColorRenderer->band();
1149  myRasterRenderer = myPseudoColorRenderer;
1150  myMinMaxOrigin = myPseudoColorRenderer->minMaxOrigin();
1151  }
1152  else
1153  {
1154  return;
1155  }
1156 
1157  const auto constMyBands = myBands;
1158  for ( int myBand : constMyBands )
1159  {
1160  if ( myBand != -1 )
1161  {
1162  Qgis::DataType myType = static_cast< Qgis::DataType >( mDataProvider->dataType( myBand ) );
1163  std::unique_ptr<QgsContrastEnhancement> myEnhancement( new QgsContrastEnhancement( static_cast< Qgis::DataType >( myType ) ) );
1164  myEnhancement->setContrastEnhancementAlgorithm( algorithm, generateLookupTableFlag );
1165 
1166  double min;
1167  double max;
1168  computeMinMax( myBand, myMinMaxOrigin, limits, extent, sampleSize, min, max );
1169 
1170  if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
1171  {
1172  myPseudoColorRenderer->setClassificationMin( min );
1173  myPseudoColorRenderer->setClassificationMax( max );
1174  if ( myPseudoColorRenderer->shader() )
1175  {
1176  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( myPseudoColorRenderer->shader()->rasterShaderFunction() );
1177  if ( colorRampShader )
1178  {
1179  colorRampShader->classifyColorRamp( myPseudoColorRenderer->band(), extent, myPseudoColorRenderer->input() );
1180  }
1181  }
1182  }
1183  else
1184  {
1185  myEnhancement->setMinimumValue( min );
1186  myEnhancement->setMaximumValue( max );
1187  myEnhancements.append( myEnhancement.release() );
1188  }
1189  }
1190  else
1191  {
1192  myEnhancements.append( nullptr );
1193  }
1194  }
1195 
1196  if ( rendererType == QLatin1String( "singlebandgray" ) )
1197  {
1198  if ( myEnhancements.first() ) myGrayRenderer->setContrastEnhancement( myEnhancements.takeFirst() );
1199  }
1200  else if ( rendererType == QLatin1String( "multibandcolor" ) )
1201  {
1202  if ( myEnhancements.first() ) myMultiBandRenderer->setRedContrastEnhancement( myEnhancements.takeFirst() );
1203  if ( myEnhancements.first() ) myMultiBandRenderer->setGreenContrastEnhancement( myEnhancements.takeFirst() );
1204  if ( myEnhancements.first() ) myMultiBandRenderer->setBlueContrastEnhancement( myEnhancements.takeFirst() );
1205  }
1206 
1207  //delete all remaining unused enhancements
1208  qDeleteAll( myEnhancements );
1209 
1210  myMinMaxOrigin.setLimits( limits );
1211  if ( extent != QgsRectangle() &&
1212  myMinMaxOrigin.extent() == QgsRasterMinMaxOrigin::WholeRaster )
1213  {
1215  }
1216  if ( myRasterRenderer )
1217  {
1218  myRasterRenderer->setMinMaxOrigin( myMinMaxOrigin );
1219  }
1220 
1221  if ( rasterRenderer == renderer() )
1222  {
1223  emit repaintRequested();
1224  emit styleChanged();
1225  emit rendererChanged();
1226  }
1227 }
1228 
1230 {
1231  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1232  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1233  const QgsContrastEnhancement *ce = nullptr;
1234  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) ) )
1235  {
1236  ce = singleBandRenderer->contrastEnhancement();
1237  }
1238  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) ) )
1239  {
1240  ce = multiBandRenderer->redContrastEnhancement();
1241  }
1242 
1243  if ( ce )
1244  {
1247  renderer()->minMaxOrigin().limits() == QgsRasterMinMaxOrigin::None ?
1248  QgsRasterMinMaxOrigin::MinMax : renderer()->minMaxOrigin().limits(),
1249  extent,
1250  SAMPLE_SIZE,
1251  true,
1252  renderer() );
1253  }
1254  else
1255  {
1258  if ( defaultContrastEnhancementSettings( myAlgorithm, myLimits ) )
1259  {
1261  myLimits,
1262  extent,
1263  SAMPLE_SIZE,
1264  true,
1265  renderer() );
1266  }
1267  }
1268 }
1269 
1271  const QgsRectangle &extent )
1272 {
1273  if ( mDataProvider &&
1274  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded != extent &&
1275  rasterRenderer->minMaxOrigin().limits() != QgsRasterMinMaxOrigin::None &&
1277  {
1278  refreshRenderer( rasterRenderer, extent );
1279  }
1280 }
1281 
1282 void QgsRasterLayer::refreshRenderer( QgsRasterRenderer *rasterRenderer, const QgsRectangle &extent )
1283 {
1284  if ( mDataProvider )
1285  {
1286  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1287  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1288  QgsSingleBandPseudoColorRenderer *sbpcr = nullptr;
1289  const QgsContrastEnhancement *ce = nullptr;
1290  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer ) ) )
1291  {
1292  ce = singleBandRenderer->contrastEnhancement();
1293  }
1294  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer ) ) )
1295  {
1296  ce = multiBandRenderer->redContrastEnhancement();
1297  }
1298  else if ( ( sbpcr = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer ) ) )
1299  {
1300  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1301  double min;
1302  double max;
1303  computeMinMax( sbpcr->band(),
1304  rasterRenderer->minMaxOrigin(),
1305  rasterRenderer->minMaxOrigin().limits(), extent,
1306  SAMPLE_SIZE, min, max );
1307  sbpcr->setClassificationMin( min );
1308  sbpcr->setClassificationMax( max );
1309 
1310  if ( sbpcr->shader() )
1311  {
1312  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( sbpcr->shader()->rasterShaderFunction() );
1313  if ( colorRampShader )
1314  {
1315  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1316  }
1317  }
1318 
1320  r->setClassificationMin( min );
1321  r->setClassificationMax( max );
1322 
1323  if ( r->shader() )
1324  {
1325  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( r->shader()->rasterShaderFunction() );
1326  if ( colorRampShader )
1327  {
1328  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1329  }
1330  }
1331 
1332  emit repaintRequested();
1333  emit styleChanged();
1334  emit rendererChanged();
1335  return;
1336  }
1337 
1338  if ( ce &&
1340  {
1341  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1342 
1344  rasterRenderer->minMaxOrigin().limits(),
1345  extent,
1346  SAMPLE_SIZE,
1347  true,
1348  rasterRenderer );
1349 
1350  // Update main renderer so that the legends get updated
1351  if ( singleBandRenderer )
1352  static_cast<QgsSingleBandGrayRenderer *>( renderer() )->setContrastEnhancement( new QgsContrastEnhancement( * singleBandRenderer->contrastEnhancement() ) );
1353  else if ( multiBandRenderer )
1354  {
1355  if ( multiBandRenderer->redContrastEnhancement() )
1356  {
1357  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setRedContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->redContrastEnhancement() ) );
1358  }
1359  if ( multiBandRenderer->greenContrastEnhancement() )
1360  {
1361  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setGreenContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->greenContrastEnhancement() ) );
1362  }
1363  if ( multiBandRenderer->blueContrastEnhancement() )
1364  {
1365  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setBlueContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->blueContrastEnhancement() ) );
1366  }
1367  }
1368 
1369  emit styleChanged();
1370  emit rendererChanged();
1371  }
1372  }
1373 }
1374 
1376 {
1377  if ( !isValid() || !mDataProvider )
1378  {
1379  QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider" ), 3 );
1380  return customProperty( QStringLiteral( "storedSubsetString" ) ).toString();
1381  }
1382  if ( !mDataProvider->supportsSubsetString() )
1383  {
1384  return QString();
1385  }
1386  return mDataProvider->subsetString();
1387 }
1388 
1389 bool QgsRasterLayer::setSubsetString( const QString &subset )
1390 {
1391  if ( !isValid() || !mDataProvider )
1392  {
1393  QgsDebugMsgLevel( QStringLiteral( "invoked with invalid layer or null mDataProvider or while editing" ), 3 );
1394  setCustomProperty( QStringLiteral( "storedSubsetString" ), subset );
1395  return false;
1396  }
1397 
1398  if ( !mDataProvider->supportsSubsetString() )
1399  {
1400  return false;
1401  }
1402 
1403  if ( subset == mDataProvider->subsetString() )
1404  return true;
1405 
1406  bool res = mDataProvider->setSubsetString( subset );
1407 
1408  // get the updated data source string from the provider
1409  mDataSource = mDataProvider->dataSourceUri();
1410 
1411  if ( res )
1412  {
1413  setExtent( mDataProvider->extent() );
1414  refreshRenderer( renderer(), extent() );
1415  emit subsetStringChanged();
1416  }
1417 
1418  return res;
1419 }
1420 
1423  QgsRasterMinMaxOrigin::Limits &myLimits ) const
1424 {
1425  QgsSettings mySettings;
1426 
1427  QString key;
1428  QString defaultAlg;
1429  QString defaultLimits;
1430 
1431  // TODO: we should not test renderer class here, move it somehow to renderers
1432  if ( dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) )
1433  {
1434  key = QStringLiteral( "singleBand" );
1437  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1439  }
1440  else if ( dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) )
1441  {
1442  if ( QgsRasterBlock::typeSize( dataProvider()->sourceDataType( 1 ) ) == 1 )
1443  {
1444  key = QStringLiteral( "multiBandSingleByte" );
1447  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1449  }
1450  else
1451  {
1452  key = QStringLiteral( "multiBandMultiByte" );
1455  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1457  }
1458  }
1459 
1460  if ( key.isEmpty() )
1461  {
1462  QgsDebugMsgLevel( QStringLiteral( "No default contrast enhancement for this drawing style" ), 2 );
1464  myLimits = QgsRasterMinMaxOrigin::limitsFromString( QString() );
1465  return false;
1466  }
1467  QgsDebugMsgLevel( "key = " + key, 4 );
1468 
1469  QString myAlgorithmString = mySettings.value( "/Raster/defaultContrastEnhancementAlgorithm/" + key, defaultAlg ).toString();
1470  QgsDebugMsgLevel( "myAlgorithmString = " + myAlgorithmString, 4 );
1471 
1472  myAlgorithm = QgsContrastEnhancement::contrastEnhancementAlgorithmFromString( myAlgorithmString );
1473 
1474  QString myLimitsString = mySettings.value( "/Raster/defaultContrastEnhancementLimits/" + key, defaultLimits ).toString();
1475  QgsDebugMsgLevel( "myLimitsString = " + myLimitsString, 4 );
1476  myLimits = QgsRasterMinMaxOrigin::limitsFromString( myLimitsString );
1477 
1478  return true;
1479 }
1480 
1482 {
1483  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1484 
1487  defaultContrastEnhancementSettings( myAlgorithm, myLimits );
1488 
1489  setContrastEnhancement( myAlgorithm, myLimits );
1490 }
1491 
1492 void QgsRasterLayer::setLayerOrder( QStringList const &layers )
1493 {
1494  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
1495 
1496  if ( mDataProvider )
1497  {
1498  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setLayerOrder(layers)." ), 4 );
1499  mDataProvider->setLayerOrder( layers );
1500  }
1501 
1502 }
1503 
1504 void QgsRasterLayer::setSubLayerVisibility( const QString &name, bool vis )
1505 {
1506 
1507  if ( mDataProvider )
1508  {
1509  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setSubLayerVisibility(name, vis)." ), 4 );
1510  mDataProvider->setSubLayerVisibility( name, vis );
1511  }
1512 
1513 }
1514 
1515 QDateTime QgsRasterLayer::timestamp() const
1516 {
1517  if ( !mDataProvider )
1518  return QDateTime();
1519  return mDataProvider->timestamp();
1520 }
1521 
1523 {
1524  if ( auto *lRenderer = mPipe.renderer() )
1525  {
1526  if ( !lRenderer->accept( visitor ) )
1527  return false;
1528  }
1529  return true;
1530 }
1531 
1532 
1533 bool QgsRasterLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props ) const
1534 {
1535  Q_UNUSED( errorMessage )
1536 
1537  QVariantMap localProps = QVariantMap( props );
1538  if ( hasScaleBasedVisibility() )
1539  {
1540  // TODO: QgsSymbolLayerUtils::mergeScaleDependencies generate SE only and not SLD1.0
1542  }
1543 
1544  if ( isSpatial() ) // TODO: does it make sense this control?
1545  {
1546  // store constraints
1547  QDomElement constraintElem = doc.createElement( QStringLiteral( "sld:LayerFeatureConstraints" ) );
1548  node.appendChild( constraintElem );
1549 
1550  QDomElement featureTypeConstraintElem = doc.createElement( QStringLiteral( "sld:FeatureTypeConstraint" ) );
1551  constraintElem.appendChild( featureTypeConstraintElem );
1552 
1553  QDomElement userStyleElem = doc.createElement( QStringLiteral( "sld:UserStyle" ) );
1554  node.appendChild( userStyleElem );
1555 
1556  if ( !name().isEmpty() )
1557  {
1558  QDomElement nameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1559  nameElem.appendChild( doc.createTextNode( name() ) );
1560  userStyleElem.appendChild( nameElem );
1561  }
1562 
1563  if ( !abstract().isEmpty() )
1564  {
1565  QDomElement abstractElem = doc.createElement( QStringLiteral( "sld:Abstract" ) );
1566  abstractElem.appendChild( doc.createTextNode( abstract() ) );
1567  userStyleElem.appendChild( abstractElem );
1568  }
1569 
1570  if ( !title().isEmpty() )
1571  {
1572  QDomElement titleElem = doc.createElement( QStringLiteral( "sld:Title" ) );
1573  titleElem.appendChild( doc.createTextNode( title() ) );
1574  userStyleElem.appendChild( titleElem );
1575  }
1576 
1577  QDomElement featureTypeStyleElem = doc.createElement( QStringLiteral( "sld:FeatureTypeStyle" ) );
1578  userStyleElem.appendChild( featureTypeStyleElem );
1579 
1580 #if 0
1581  // TODO: Is there a way to fill it's value with the named style?
1582  // by default <sld:Name> under <sld:FeatureTypeStyle> can have 0 occurrences
1583  // the same happen for tags:
1584  // sld:Title
1585  // sld:Abstract
1586  // sld:FeatureTypeName
1587  // sld:SemanticTypeIdentifier
1588  QDomElement typeStyleNameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1589  featureTypeStyleElem.appendChild( typeStyleNameElem );
1590 #endif
1591 
1592  QDomElement typeStyleRuleElem = doc.createElement( QStringLiteral( "sld:Rule" ) );
1593  featureTypeStyleElem.appendChild( typeStyleRuleElem );
1594 
1595  // add ScaleDenominator tags
1596  if ( hasScaleBasedVisibility() )
1597  {
1598  // note that denominator is the inverted value of scale
1599  if ( maximumScale() != 0.0 )
1600  {
1601  QDomElement minScaleElem = doc.createElement( QStringLiteral( "sld:MinScaleDenominator" ) );
1602  minScaleElem.appendChild( doc.createTextNode( QString::number( maximumScale() ) ) );
1603  typeStyleRuleElem.appendChild( minScaleElem );
1604  }
1605 
1606  QDomElement maxScaleElem = doc.createElement( QStringLiteral( "sld:MaxScaleDenominator" ) );
1607  maxScaleElem.appendChild( doc.createTextNode( QString::number( minimumScale() ) ) );
1608  typeStyleRuleElem.appendChild( maxScaleElem );
1609  }
1610 
1611  // export renderer dependent tags
1612  mPipe.renderer()->toSld( doc, typeStyleRuleElem, localProps );
1613 
1614  // inject raster layer parameters in RasterSymbolizer tag because
1615  // they belongs to rasterlayer and not to the renderer => avoid to
1616  // pass many parameters value via localProps
1617  QDomNodeList elements = typeStyleRuleElem.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
1618  if ( elements.size() != 0 )
1619  {
1620  // there SHOULD be only one
1621  QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
1622 
1623  // lamda helper used below to reduce code redundancy
1624  auto vendorOptionWriter = [&]( QString name, QString value )
1625  {
1626  QDomElement vendorOptionElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
1627  vendorOptionElem.setAttribute( QStringLiteral( "name" ), name );
1628  vendorOptionElem.appendChild( doc.createTextNode( value ) );
1629  rasterSymbolizerElem.appendChild( vendorOptionElem );
1630  };
1631 
1632  // add greyScale rendering mode if set
1634  {
1635  QString property;
1636  switch ( hueSaturationFilter()->grayscaleMode() )
1637  {
1639  property = QStringLiteral( "lightness" );
1640  break;
1642  property = QStringLiteral( "luminosity" );
1643  break;
1645  property = QStringLiteral( "average" );
1646  break;
1648  // added just to avoid travis fail
1649  break;
1650  }
1651  if ( !property.isEmpty() )
1652  vendorOptionWriter( QStringLiteral( "grayScale" ), property );
1653  }
1654 
1655  // add Hue, Saturation and Lighting values in props is Hue filter is set
1656  if ( hueSaturationFilter() && hueSaturationFilter()->colorizeOn() )
1657  {
1658  vendorOptionWriter( QStringLiteral( "colorizeOn" ), QString::number( hueSaturationFilter()->colorizeOn() ) );
1659  vendorOptionWriter( QStringLiteral( "colorizeRed" ), QString::number( hueSaturationFilter()->colorizeColor().red() ) );
1660  vendorOptionWriter( QStringLiteral( "colorizeGreen" ), QString::number( hueSaturationFilter()->colorizeColor().green() ) );
1661  vendorOptionWriter( QStringLiteral( "colorizeBlue" ), QString::number( hueSaturationFilter()->colorizeColor().blue() ) );
1662  if ( hueSaturationFilter()->colorizeStrength() != 100.0 )
1663  vendorOptionWriter( QStringLiteral( "colorizeStrength" ), QString::number( hueSaturationFilter()->colorizeStrength() / 100.0 ) );
1664  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( hueSaturationFilter()->colorizeColor().saturationF() ) );
1665  }
1666  else
1667  {
1668  // saturation != 0 (default value)
1669  if ( hueSaturationFilter()->saturation() != 0 )
1670  {
1671  // normlize value [-100:100] -> [0:1]
1672  int s = hueSaturationFilter()->saturation();
1673  double sF = ( s - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1674  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( sF ) );
1675  }
1676  }
1677 
1678  // brightness != 0 (default value)
1679  if ( brightnessFilter()->brightness() != 0 )
1680  {
1681  // normalize value [-255:255] -> [0:1]
1682  int b = brightnessFilter()->brightness();
1683  double bF = ( b - ( -255.0 ) ) / ( 255.0 - ( -255.0 ) );
1684  vendorOptionWriter( QStringLiteral( "brightness" ), QString::number( bF ) );
1685  }
1686 
1687  // contrast != 0 (default value)
1688  if ( brightnessFilter()->contrast() != 0 )
1689  {
1690  // normlize value [-100:100] -> [0:1]
1691  int c = brightnessFilter()->contrast();
1692  double cF = ( c - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1693  vendorOptionWriter( QStringLiteral( "contrast" ), QString::number( cF ) );
1694  }
1695 
1696 #if 0
1697  // TODO: check if the below mapping formula make sense to map QGIS contrast with SLD gamma value
1698  //
1699  // add SLD1.0 ContrastEnhancement GammaValue = QGIS Contrast
1700  // SLD1.0 does only define 1 as neutral/center double value but does not define range.
1701  // because https://en.wikipedia.org/wiki/Gamma_correction assumed gamma is >0.
1702  // whilst QGIS has a -100/100 values centered in 0 => QGIS contrast value will be scaled in the
1703  // following way:
1704  // [-100,0] => [0,1] and [0,100] => [1,100]
1705  // an alternative could be scale [-100,100] => (0,2]
1706  //
1707  if ( newProps.contains( QStringLiteral( "contrast" ) ) )
1708  {
1709  double gamma;
1710  double contrast = newProps[ QStringLiteral( "contrast" ) ].toDouble();
1711  double percentage = ( contrast - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1712  if ( percentage <= 0.5 )
1713  {
1714  // stretch % to [0-1]
1715  gamma = percentage / 0.5;
1716  }
1717  else
1718  {
1719  gamma = contrast;
1720  }
1721 
1722  QDomElement globalContrastEnhancementElem = doc.createElement( QStringLiteral( "sld:ContrastEnhancement" ) );
1723  rasterSymolizerElem.appendChild( globalContrastEnhancementElem );
1724 
1725  QDomElement gammaValueElem = doc.createElement( QStringLiteral( "sld:GammaValue" ) );
1726  gammaValueElem.appendChild( doc.createTextNode( QString::number( gamma ) ) );
1727  globalContrastEnhancementElem.appendChild( gammaValueElem );
1728  }
1729 #endif
1730  }
1731  }
1732  return true;
1733 }
1734 
1735 
1737 {
1738  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1739  if ( !renderer )
1740  {
1741  return;
1742  }
1743 
1744  mPipe.set( renderer );
1745  emit rendererChanged();
1746  emit styleChanged();
1747 }
1748 
1749 void QgsRasterLayer::showStatusMessage( QString const &message )
1750 {
1751  // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
1752 
1753  // Pass-through
1754  // TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
1755  emit statusChanged( message );
1756 }
1757 
1759 {
1760  if ( mDataProvider )
1761  mDataProvider->setTransformContext( transformContext );
1762 }
1763 
1764 QStringList QgsRasterLayer::subLayers() const
1765 {
1766  if ( ! mDataProvider )
1767  return QStringList();
1768  return mDataProvider->subLayers();
1769 }
1770 
1771 // this function should be used when rendering with the MTR engine introduced in 2.3, as QPixmap is not thread safe (see bug #9626)
1772 // note: previewAsImage and previewAsPixmap should use a common low-level fct QgsRasterLayer::previewOnPaintDevice( QSize size, QColor bgColor, QPaintDevice &device )
1773 QImage QgsRasterLayer::previewAsImage( QSize size, const QColor &bgColor, QImage::Format format )
1774 {
1775  QImage image( size, format );
1776 
1777  if ( ! isValid( ) )
1778  return QImage();
1779 
1780  if ( image.format() == QImage::Format_Indexed8 )
1781  {
1782  image.setColor( 0, bgColor.rgba() );
1783  image.fill( 0 ); //defaults to white, set to transparent for rendering on a map
1784  }
1785  else
1786  {
1787  image.fill( bgColor );
1788  }
1789 
1790  QgsRasterViewPort *rasterViewPort = new QgsRasterViewPort();
1791 
1792  double mapUnitsPerPixel;
1793  double x = 0.0;
1794  double y = 0.0;
1795  QgsRectangle extent = mDataProvider->extent();
1796  if ( extent.width() / extent.height() >= static_cast< double >( image.width() ) / image.height() )
1797  {
1798  mapUnitsPerPixel = extent.width() / image.width();
1799  y = ( image.height() - extent.height() / mapUnitsPerPixel ) / 2;
1800  }
1801  else
1802  {
1803  mapUnitsPerPixel = extent.height() / image.height();
1804  x = ( image.width() - extent.width() / mapUnitsPerPixel ) / 2;
1805  }
1806 
1807  const double pixelWidth = extent.width() / mapUnitsPerPixel;
1808  const double pixelHeight = extent.height() / mapUnitsPerPixel;
1809 
1810  rasterViewPort->mTopLeftPoint = QgsPointXY( x, y );
1811  rasterViewPort->mBottomRightPoint = QgsPointXY( pixelWidth, pixelHeight );
1812  rasterViewPort->mWidth = image.width();
1813  rasterViewPort->mHeight = image.height();
1814 
1815  rasterViewPort->mDrawnExtent = extent;
1816  rasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
1817  rasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
1818 
1819  QgsMapToPixel *mapToPixel = new QgsMapToPixel( mapUnitsPerPixel );
1820 
1821  QPainter *painter = new QPainter( &image );
1822  draw( painter, rasterViewPort, mapToPixel );
1823  delete rasterViewPort;
1824  delete mapToPixel;
1825 
1826  painter->end();
1827  delete painter;
1828 
1829  return image;
1830 }
1831 
1833 //
1834 // Protected methods
1835 //
1837 /*
1838  * \param QDomNode node that will contain the symbology definition for this layer.
1839  * \param errorMessage reference to string that will be updated with any error messages
1840  * \return TRUE in case of success.
1841  */
1842 bool QgsRasterLayer::readSymbology( const QDomNode &layer_node, QString &errorMessage,
1843  QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1844 {
1845  Q_UNUSED( errorMessage )
1846  // TODO: implement categories for raster layer
1847 
1848  QDomElement rasterRendererElem;
1849 
1850  QDomElement layerElement = layer_node.toElement();
1851  readCommonStyle( layerElement, context, categories );
1852 
1853  // pipe element was introduced in the end of 1.9 development when there were
1854  // already many project files in use so we support 1.9 backward compatibility
1855  // even it was never officially released -> use pipe element if present, otherwise
1856  // use layer node
1857  QDomNode pipeNode = layer_node.firstChildElement( QStringLiteral( "pipe" ) );
1858  if ( pipeNode.isNull() ) // old project
1859  {
1860  pipeNode = layer_node;
1861  }
1862 
1863  //rasterlayerproperties element there -> old format (1.8 and early 1.9)
1864  if ( !layer_node.firstChildElement( QStringLiteral( "rasterproperties" ) ).isNull() )
1865  {
1866  //copy node because layer_node is const
1867  QDomNode layerNodeCopy = layer_node.cloneNode();
1868  QDomDocument doc = layerNodeCopy.ownerDocument();
1869  QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterproperties" ) );
1870  QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
1871  this );
1872  rasterRendererElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1873  QgsDebugMsgLevel( doc.toString(), 4 );
1874  }
1875  else
1876  {
1877  rasterRendererElem = pipeNode.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1878  }
1879 
1880  if ( !rasterRendererElem.isNull() )
1881  {
1882  QString rendererType = rasterRendererElem.attribute( QStringLiteral( "type" ) );
1883  QgsRasterRendererRegistryEntry rendererEntry;
1884  if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererType, rendererEntry ) )
1885  {
1886  QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
1887  mPipe.set( renderer );
1888  }
1889  }
1890 
1891  //brightness
1893  mPipe.set( brightnessFilter );
1894 
1895  //brightness coefficient
1896  QDomElement brightnessElem = pipeNode.firstChildElement( QStringLiteral( "brightnesscontrast" ) );
1897  if ( !brightnessElem.isNull() )
1898  {
1899  brightnessFilter->readXml( brightnessElem );
1900  }
1901 
1902  //hue/saturation
1904  mPipe.set( hueSaturationFilter );
1905 
1906  //saturation coefficient
1907  QDomElement hueSaturationElem = pipeNode.firstChildElement( QStringLiteral( "huesaturation" ) );
1908  if ( !hueSaturationElem.isNull() )
1909  {
1910  hueSaturationFilter->readXml( hueSaturationElem );
1911  }
1912 
1913  //resampler
1915  mPipe.set( resampleFilter );
1916 
1917  //max oversampling
1918  QDomElement resampleElem = pipeNode.firstChildElement( QStringLiteral( "rasterresampler" ) );
1919  if ( !resampleElem.isNull() )
1920  {
1921  resampleFilter->readXml( resampleElem );
1922  }
1923 
1924  //provider
1925  if ( mDataProvider )
1926  {
1927  QDomElement providerElem = pipeNode.firstChildElement( QStringLiteral( "provider" ) );
1928  if ( !providerElem.isNull() )
1929  {
1930  mDataProvider->readXml( providerElem );
1931  }
1932  }
1933 
1934  // Resampling stage
1935  QDomNode resamplingStageElement = pipeNode.namedItem( QStringLiteral( "resamplingStage" ) );
1936  if ( !resamplingStageElement.isNull() )
1937  {
1938  QDomElement e = resamplingStageElement.toElement();
1939  if ( e.text() == QLatin1String( "provider" ) )
1941  else if ( e.text() == QLatin1String( "resamplingFilter" ) )
1943  }
1944 
1945  // get and set the blend mode if it exists
1946  QDomNode blendModeNode = layer_node.namedItem( QStringLiteral( "blendMode" ) );
1947  if ( !blendModeNode.isNull() )
1948  {
1949  QDomElement e = blendModeNode.toElement();
1950  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
1951  }
1952 
1953  readCustomProperties( layer_node );
1954 
1955  return true;
1956 }
1957 
1958 bool QgsRasterLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1959 {
1960  return readSymbology( node, errorMessage, context, categories );
1961 }
1962 
1963 bool QgsRasterLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1964 {
1965  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1966  // Make sure to read the file first so stats etc are initialized properly!
1967 
1968  //process provider key
1969  QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
1970 
1971  if ( pkeyNode.isNull() )
1972  {
1973  mProviderKey = QStringLiteral( "gdal" );
1974  }
1975  else
1976  {
1977  QDomElement pkeyElt = pkeyNode.toElement();
1978  mProviderKey = pkeyElt.text();
1979  if ( mProviderKey.isEmpty() )
1980  {
1981  mProviderKey = QStringLiteral( "gdal" );
1982  }
1983  }
1984 
1985  // Open the raster source based on provider and datasource
1986 
1987  // Go down the raster-data-provider paradigm
1988 
1989  // Collect provider-specific information
1990 
1991  QDomNode rpNode = layer_node.namedItem( QStringLiteral( "rasterproperties" ) );
1992 
1993  if ( mProviderKey == QLatin1String( "wms" ) )
1994  {
1995  // >>> BACKWARD COMPATIBILITY < 1.9
1996  // The old WMS URI format does not contain all the information, we add them here.
1997  if ( !mDataSource.contains( QLatin1String( "crs=" ) ) && !mDataSource.contains( QLatin1String( "format=" ) ) )
1998  {
1999  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> adding params" ), 4 );
2000  QgsDataSourceUri uri;
2001  uri.setEncodedUri( mDataSource );
2002  QDomElement layerElement = rpNode.firstChildElement( QStringLiteral( "wmsSublayer" ) );
2003  while ( !layerElement.isNull() )
2004  {
2005  // TODO: sublayer visibility - post-0.8 release timeframe
2006 
2007  // collect name for the sublayer
2008  uri.setParam( QStringLiteral( "layers" ), layerElement.namedItem( QStringLiteral( "name" ) ).toElement().text() );
2009 
2010  // collect style for the sublayer
2011  uri.setParam( QStringLiteral( "styles" ), layerElement.namedItem( QStringLiteral( "style" ) ).toElement().text() );
2012 
2013  layerElement = layerElement.nextSiblingElement( QStringLiteral( "wmsSublayer" ) );
2014  }
2015 
2016  // Collect format
2017  uri.setParam( QStringLiteral( "format" ), rpNode.namedItem( QStringLiteral( "wmsFormat" ) ).toElement().text() );
2018 
2019  // WMS CRS URL param should not be mixed with that assigned to the layer.
2020  // In the old WMS URI version there was no CRS and layer crs().authid() was used.
2021  uri.setParam( QStringLiteral( "crs" ), crs().authid() );
2022  mDataSource = uri.encodedUri();
2023  }
2024  // <<< BACKWARD COMPATIBILITY < 1.9
2025  }
2026 
2028  {
2029  QgsDataProvider::ProviderOptions providerOptions { context.transformContext() };
2030  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2032  {
2034  }
2035  setDataProvider( mProviderKey, providerOptions, flags );
2036  }
2037 
2038  mOriginalStyleElement = layer_node.namedItem( QStringLiteral( "originalStyle" ) ).firstChildElement();
2039  if ( mOriginalStyleElement.isNull() )
2040  mOriginalStyleElement = layer_node.toElement();
2041  mOriginalStyleDocument = layer_node.ownerDocument();
2042 
2043  if ( ! mDataProvider )
2044  {
2046  {
2047  QgsDebugMsg( QStringLiteral( "Raster data provider could not be created for %1" ).arg( mDataSource ) );
2048  }
2049  return false;
2050  }
2051 
2052  QString error;
2053  bool res = readSymbology( layer_node, error, context );
2054 
2055  // old wms settings we need to correct
2056  if ( res && mProviderKey == QLatin1String( "wms" ) && ( !renderer() || renderer()->type() != QLatin1String( "singlebandcolordata" ) ) )
2057  {
2058  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
2059  }
2060 
2061  // Check timestamp
2062  // This was probably introduced to reload completely raster if data changed and
2063  // reset completely symbology to reflect new data type etc. It creates however
2064  // problems, because user defined symbology is complete lost if data file time
2065  // changed (the content may be the same). See also 6900.
2066 #if 0
2067  QDomNode stampNode = layer_node.namedItem( "timestamp" );
2068  if ( !stampNode.isNull() )
2069  {
2070  QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
2071  // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
2072  if ( stamp < mDataProvider->dataTimestamp() )
2073  {
2074  QgsDebugMsgLevel( QStringLiteral( "data changed, reload provider" ), 3 );
2075  closeDataProvider();
2076  init();
2078  if ( !isValid() ) return false;
2079  }
2080  }
2081 #endif
2082 
2083  // Load user no data value
2084  QDomElement noDataElement = layer_node.firstChildElement( QStringLiteral( "noData" ) );
2085 
2086  QDomNodeList noDataBandList = noDataElement.elementsByTagName( QStringLiteral( "noDataList" ) );
2087 
2088  for ( int i = 0; i < noDataBandList.size(); ++i )
2089  {
2090  QDomElement bandElement = noDataBandList.at( i ).toElement();
2091  bool ok;
2092  int bandNo = bandElement.attribute( QStringLiteral( "bandNo" ) ).toInt( &ok );
2093  QgsDebugMsgLevel( QStringLiteral( "bandNo = %1" ).arg( bandNo ), 4 );
2094  if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
2095  {
2096  mDataProvider->setUseSourceNoDataValue( bandNo, bandElement.attribute( QStringLiteral( "useSrcNoData" ) ).toInt() );
2097  QgsRasterRangeList myNoDataRangeList;
2098 
2099  QDomNodeList rangeList = bandElement.elementsByTagName( QStringLiteral( "noDataRange" ) );
2100 
2101  myNoDataRangeList.reserve( rangeList.size() );
2102  for ( int j = 0; j < rangeList.size(); ++j )
2103  {
2104  QDomElement rangeElement = rangeList.at( j ).toElement();
2105  QgsRasterRange myNoDataRange( rangeElement.attribute( QStringLiteral( "min" ) ).toDouble(),
2106  rangeElement.attribute( QStringLiteral( "max" ) ).toDouble() );
2107  QgsDebugMsgLevel( QStringLiteral( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ), 4 );
2108  myNoDataRangeList << myNoDataRange;
2109  }
2110  mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
2111  }
2112  }
2113 
2114  readStyleManager( layer_node );
2115 
2116  return res;
2117 }
2118 
2119 bool QgsRasterLayer::writeSymbology( QDomNode &layer_node, QDomDocument &document, QString &errorMessage,
2120  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2121 {
2122  Q_UNUSED( errorMessage )
2123  // TODO: implement categories for raster layer
2124 
2125  QDomElement layerElement = layer_node.toElement();
2126  writeCommonStyle( layerElement, document, context, categories );
2127 
2128  // Store pipe members into pipe element, in future, it will be
2129  // possible to add custom filters into the pipe
2130  QDomElement pipeElement = document.createElement( QStringLiteral( "pipe" ) );
2131 
2132  for ( int i = 0; i < mPipe.size(); i++ )
2133  {
2134  QgsRasterInterface *interface = mPipe.at( i );
2135  if ( !interface ) continue;
2136  interface->writeXml( document, pipeElement );
2137  }
2138 
2139  QDomElement resamplingStageElement = document.createElement( QStringLiteral( "resamplingStage" ) );
2140  QDomText resamplingStageText = document.createTextNode( resamplingStage() == QgsRasterPipe::ResamplingStage::Provider ? QStringLiteral( "provider" ) : QStringLiteral( "resamplingFilter" ) );
2141  resamplingStageElement.appendChild( resamplingStageText );
2142  pipeElement.appendChild( resamplingStageElement );
2143 
2144  layer_node.appendChild( pipeElement );
2145 
2146  if ( !isValid() && !mOriginalStyleElement.isNull() )
2147  {
2148  QDomElement originalStyleElement = document.createElement( QStringLiteral( "originalStyle" ) );
2149  originalStyleElement.appendChild( mOriginalStyleElement );
2150  layer_node.appendChild( originalStyleElement );
2151  }
2152 
2153  // add blend mode node
2154  QDomElement blendModeElement = document.createElement( QStringLiteral( "blendMode" ) );
2155  QDomText blendModeText = document.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
2156  blendModeElement.appendChild( blendModeText );
2157  layer_node.appendChild( blendModeElement );
2158 
2159  return true;
2160 }
2161 
2162 bool QgsRasterLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage,
2163  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2164 {
2165  return writeSymbology( node, doc, errorMessage, context, categories );
2166 } // bool QgsRasterLayer::writeSymbology
2167 
2168 /*
2169  * virtual
2170  * \note Called by QgsMapLayer::writeXml().
2171  */
2172 bool QgsRasterLayer::writeXml( QDomNode &layer_node,
2173  QDomDocument &document,
2174  const QgsReadWriteContext &context ) const
2175 {
2176  // first get the layer element so that we can append the type attribute
2177 
2178  QDomElement mapLayerNode = layer_node.toElement();
2179 
2180  if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
2181  {
2182  QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
2183  return false;
2184  }
2185 
2186  mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::RasterLayer ) );
2187 
2188  // add provider node
2189 
2190  QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
2191  QDomText providerText = document.createTextNode( mProviderKey );
2192  provider.appendChild( providerText );
2193  layer_node.appendChild( provider );
2194 
2195  // User no data
2196  QDomElement noData = document.createElement( QStringLiteral( "noData" ) );
2197 
2198  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
2199  {
2200  QDomElement noDataRangeList = document.createElement( QStringLiteral( "noDataList" ) );
2201  noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), bandNo );
2202  noDataRangeList.setAttribute( QStringLiteral( "useSrcNoData" ), mDataProvider->useSourceNoDataValue( bandNo ) );
2203 
2204  const auto constUserNoDataValues = mDataProvider->userNoDataValues( bandNo );
2205  for ( QgsRasterRange range : constUserNoDataValues )
2206  {
2207  QDomElement noDataRange = document.createElement( QStringLiteral( "noDataRange" ) );
2208 
2209  noDataRange.setAttribute( QStringLiteral( "min" ), QgsRasterBlock::printValue( range.min() ) );
2210  noDataRange.setAttribute( QStringLiteral( "max" ), QgsRasterBlock::printValue( range.max() ) );
2211  noDataRangeList.appendChild( noDataRange );
2212  }
2213 
2214  noData.appendChild( noDataRangeList );
2215 
2216  }
2217  if ( noData.hasChildNodes() )
2218  {
2219  layer_node.appendChild( noData );
2220  }
2221 
2222  writeStyleManager( layer_node, document );
2223 
2224  //write out the symbology
2225  QString errorMsg;
2226  return writeSymbology( layer_node, document, errorMsg, context );
2227 }
2228 
2229 // TODO: this should ideally go to gdal provider (together with most of encodedSource() + decodedSource())
2230 static bool _parseGpkgColons( const QString &src, QString &filename, QString &tablename )
2231 {
2232  // GDAL accepts the following input format: GPKG:filename:table
2233  // (GDAL won't accept quoted filename)
2234 
2235  QStringList lst = src.split( ':' );
2236  if ( lst.count() != 3 && lst.count() != 4 )
2237  return false;
2238 
2239  tablename = lst.last();
2240  if ( lst.count() == 3 )
2241  {
2242  filename = lst[1];
2243  return true;
2244  }
2245  else if ( lst.count() == 4 && lst[1].count() == 1 && ( lst[2][0] == '/' || lst[2][0] == '\\' ) )
2246  {
2247  // a bit of handling to make sure that filename C:\hello.gpkg is parsed correctly
2248  filename = lst[1] + ":" + lst[2];
2249  return true;
2250  }
2251  return false;
2252 }
2253 
2254 
2255 QString QgsRasterLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
2256 {
2257  QString src( source );
2258  bool handled = false;
2259 
2260  // Update path for subdataset
2261  if ( providerType() == QLatin1String( "gdal" ) )
2262  {
2263  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2264  {
2265  // NETCDF:filename:variable
2266  // filename can be quoted with " as it can contain colons
2267  QRegExp r( "NETCDF:(.+):([^:]+)" );
2268  if ( r.exactMatch( src ) )
2269  {
2270  QString filename = r.cap( 1 );
2271  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2272  filename = filename.mid( 1, filename.length() - 2 );
2273  src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
2274  handled = true;
2275  }
2276  }
2277  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2278  {
2279  // GPKG:filename:table
2280  QString filename, tablename;
2281  if ( _parseGpkgColons( src, filename, tablename ) )
2282  {
2283  filename = context.pathResolver().writePath( filename );
2284  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2285  handled = true;
2286  }
2287  }
2288  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2289  {
2290  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2291  // filename can be quoted with " as it can contain colons
2292  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
2293  if ( r.exactMatch( src ) )
2294  {
2295  QString filename = r.cap( 2 );
2296  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2297  filename = filename.mid( 1, filename.length() - 2 );
2298  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 3 );
2299  handled = true;
2300  }
2301  }
2302  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2303  {
2304  // HDF5:file_name:subdataset
2305  // filename can be quoted with " as it can contain colons
2306  QRegExp r( "HDF5:(.+):([^:]+)" );
2307  if ( r.exactMatch( src ) )
2308  {
2309  QString filename = r.cap( 1 );
2310  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2311  filename = filename.mid( 1, filename.length() - 2 );
2312  src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
2313  handled = true;
2314  }
2315  }
2316  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2317  {
2318  // NITF_IM:0:filename
2319  // RADARSAT_2_CALIB:?:filename
2320  QRegExp r( "([^:]+):([^:]+):(.+)" );
2321  if ( r.exactMatch( src ) )
2322  {
2323  src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().writePath( r.cap( 3 ) );
2324  handled = true;
2325  }
2326  }
2327  }
2328  else if ( providerType() == "wms" )
2329  {
2330  // handle relative paths to XYZ tiles
2331  QgsDataSourceUri uri;
2332  uri.setEncodedUri( src );
2333  QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2334  if ( srcUrl.isLocalFile() )
2335  {
2336  // relative path will become "file:./x.txt"
2337  QString relSrcUrl = context.pathResolver().writePath( srcUrl.toLocalFile() );
2338  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2339  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
2340  src = uri.encodedUri();
2341  handled = true;
2342  }
2343  }
2344 
2345  if ( !handled )
2346  src = context.pathResolver().writePath( src );
2347 
2348  return src;
2349 }
2350 
2351 QString QgsRasterLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
2352 {
2353  QString src( source );
2354 
2355  if ( provider == QLatin1String( "wms" ) )
2356  {
2357  // >>> BACKWARD COMPATIBILITY < 1.9
2358  // For project file backward compatibility we must support old format:
2359  // 1. mode: <url>
2360  // example: http://example.org/wms?
2361  // 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
2362  // example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
2363  // example: featureCount=10,http://example.org/wms?
2364  // example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
2365  // This is modified version of old QgsWmsProvider::parseUri
2366  // The new format has always params crs,format,layers,styles and that params
2367  // should not appear in old format url -> use them to identify version
2368  // XYZ tile layers do not need to contain crs,format params, but they have type=xyz
2369  if ( !src.contains( QLatin1String( "type=" ) ) &&
2370  !src.contains( QLatin1String( "crs=" ) ) && !src.contains( QLatin1String( "format=" ) ) )
2371  {
2372  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> converting to new format" ), 2 );
2373  QgsDataSourceUri uri;
2374  if ( !src.startsWith( QLatin1String( "http:" ) ) )
2375  {
2376  QStringList parts = src.split( ',' );
2377  QStringListIterator iter( parts );
2378  while ( iter.hasNext() )
2379  {
2380  QString item = iter.next();
2381  if ( item.startsWith( QLatin1String( "username=" ) ) )
2382  {
2383  uri.setUsername( item.mid( 9 ) );
2384  }
2385  else if ( item.startsWith( QLatin1String( "password=" ) ) )
2386  {
2387  uri.setPassword( item.mid( 9 ) );
2388  }
2389  else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
2390  {
2391  // in < 1.9 tiled= may apper in to variants:
2392  // tiled=width;height - non tiled mode, specifies max width and max height
2393  // tiled=width;height;resolutions-1;resolution2;... - tile mode
2394 
2395  QStringList params = item.mid( 6 ).split( ';' );
2396 
2397  if ( params.size() == 2 ) // non tiled mode
2398  {
2399  uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
2400  uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
2401  }
2402  else if ( params.size() > 2 ) // tiled mode
2403  {
2404  // resolutions are no more needed and size limit is not used for tiles
2405  // we have to tell to the provider however that it is tiled
2406  uri.setParam( QStringLiteral( "tileMatrixSet" ), QString() );
2407  }
2408  }
2409  else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
2410  {
2411  uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
2412  }
2413  else if ( item.startsWith( QLatin1String( "url=" ) ) )
2414  {
2415  uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
2416  }
2417  else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
2418  {
2419  uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
2420  }
2421  }
2422  }
2423  else
2424  {
2425  uri.setParam( QStringLiteral( "url" ), src );
2426  }
2427  src = uri.encodedUri();
2428  // At this point, the URI is obviously incomplete, we add additional params
2429  // in QgsRasterLayer::readXml
2430  }
2431  // <<< BACKWARD COMPATIBILITY < 1.9
2432 
2433  // handle relative paths to XYZ tiles
2434  QgsDataSourceUri uri;
2435  uri.setEncodedUri( src );
2436  QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2437  if ( srcUrl.isLocalFile() ) // file-based URL? convert to relative path
2438  {
2439  QString absSrcUrl = context.pathResolver().readPath( srcUrl.toLocalFile() );
2440  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2441  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
2442  src = uri.encodedUri();
2443  }
2444 
2445  }
2446  else
2447  {
2448  bool handled = false;
2449 
2450  if ( provider == QLatin1String( "gdal" ) )
2451  {
2452  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2453  {
2454  // NETCDF:filename:variable
2455  // filename can be quoted with " as it can contain colons
2456  QRegExp r( "NETCDF:(.+):([^:]+)" );
2457  if ( r.exactMatch( src ) )
2458  {
2459  QString filename = r.cap( 1 );
2460  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2461  filename = filename.mid( 1, filename.length() - 2 );
2462  src = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
2463  handled = true;
2464  }
2465  }
2466  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2467  {
2468  // GPKG:filename:table
2469  QString filename, tablename;
2470  if ( _parseGpkgColons( src, filename, tablename ) )
2471  {
2472  filename = context.pathResolver().readPath( filename );
2473  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2474  handled = true;
2475  }
2476  }
2477  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2478  {
2479  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2480  // filename can be quoted with " as it can contain colons
2481  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
2482  if ( r.exactMatch( src ) )
2483  {
2484  QString filename = r.cap( 2 );
2485  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2486  filename = filename.mid( 1, filename.length() - 2 );
2487  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 3 );
2488  handled = true;
2489  }
2490  }
2491  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2492  {
2493  // HDF5:file_name:subdataset
2494  // filename can be quoted with " as it can contain colons
2495  QRegExp r( "HDF5:(.+):([^:]+)" );
2496  if ( r.exactMatch( src ) )
2497  {
2498  QString filename = r.cap( 1 );
2499  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2500  filename = filename.mid( 1, filename.length() - 2 );
2501  src = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
2502  handled = true;
2503  }
2504  }
2505  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2506  {
2507  // NITF_IM:0:filename
2508  // RADARSAT_2_CALIB:?:filename
2509  QRegExp r( "([^:]+):([^:]+):(.+)" );
2510  if ( r.exactMatch( src ) )
2511  {
2512  src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().readPath( r.cap( 3 ) );
2513  handled = true;
2514  }
2515  }
2516  }
2517 
2518  if ( !handled )
2519  src = context.pathResolver().readPath( src );
2520  }
2521 
2522  return src;
2523 }
2524 
2526 {
2527  if ( !mDataProvider ) return 0;
2528  return mDataProvider->xSize();
2529 }
2530 
2532 {
2533  if ( !mDataProvider ) return 0;
2534  return mDataProvider->ySize();
2535 }
2536 
2538 {
2539  mPipe.setResamplingStage( stage );
2540 }
2541 
2543 //
2544 // Private methods
2545 //
2547 bool QgsRasterLayer::update()
2548 {
2549  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
2550  // Check if data changed
2551  if ( mDataProvider && mDataProvider->dataTimestamp() > mDataProvider->timestamp() )
2552  {
2553  QgsDebugMsgLevel( QStringLiteral( "reload data" ), 4 );
2554  closeDataProvider();
2555  init();
2556  QgsDataProvider::ProviderOptions providerOptions;
2557  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2559  {
2561  }
2562  setDataProvider( mProviderKey, providerOptions, flags );
2563  emit dataChanged();
2564  }
2565  return isValid();
2566 }
static QString version()
Version string.
Definition: qgis.cpp:276
DataType
Raster data types.
Definition: qgis.h:102
@ Int16
Sixteen bit signed integer (qint16)
Definition: qgis.h:106
@ Float32
Thirty two bit floating point (float)
Definition: qgis.h:109
@ CFloat64
Complex Float64.
Definition: qgis.h:114
@ UInt32
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:107
@ Float64
Sixty four bit floating point (double)
Definition: qgis.h:110
@ Int32
Thirty two bit signed integer (qint32)
Definition: qgis.h:108
@ CFloat32
Complex Float32.
Definition: qgis.h:113
@ CInt16
Complex Int16.
Definition: qgis.h:111
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:116
@ CInt32
Complex Int32.
Definition: qgis.h:112
@ UInt16
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:105
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:115
@ Byte
Eight bit unsigned integer (quint8)
Definition: qgis.h:104
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).
@ FullString
Full definition – possibly a very lengthy string, e.g. with no truncation of custom WKT definitions.
QString userFriendlyIdentifier(IdentifierType type=MediumString) const
Returns a user friendly identifier for the 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)
@ FlagTrustDataSource
Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load ti...
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.
QgsHueSaturationFilter::GrayscaleMode grayscaleMode() const
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.
Base class for storage of map layer temporal properties.
Base class for all map layer types.
Definition: qgsmaplayer.h:85
QString name
Definition: qgsmaplayer.h:88
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:1603
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:92
void configChanged()
Emitted whenever the configuration is changed.
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:91
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:1615
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:90
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:1658
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
void styleChanged()
Signal emitted whenever a change affects the layer's style.
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:301
virtual QgsError error() const
Gets current status error.
void dataSourceChanged()
Emitted whenever the layer's data source has been changed.
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 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:93
void appendError(const QgsErrorMessage &error)
Add error message.
Definition: qgsmaplayer.h:1601
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1612
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:585
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:584
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:1663
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 setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
void setProviderType(const QString &providerType)
Sets the providerType (provider key)
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.
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
Definition: qgsmaplayer.h:1670
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::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:44
static void convertRasterProperties(QDomDocument &doc, QDomNode &parentNode, QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer)
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
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(int 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
virtual bool useSourceNoDataValue(int bandNo) const
Returns the source nodata value usage.
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.
virtual QgsRasterInterface * clone() const =0
Clone itself, create deep copy.
@ 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
virtual void writeXml(QDomDocument &doc, QDomElement &parentElem) const
Write base class members to xml.
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...
QgsRasterPipe::ResamplingStage resamplingStage() const
Returns which stage of the pipe should apply resampling.
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.
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 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...
QgsHueSaturationFilter * hueSaturationFilter() const
Returns the raster's hue/saturation filter.
void setSubLayerVisibility(const QString &name, bool vis) override
Set the visibility of the given sublayer name.
void setDataSource(const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag=false) override
Updates the data source of the layer.
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.
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.
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
QgsRasterResampleFilter * resampleFilter() const
Returns the raster's resample filter.
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
void setResamplingStage(QgsRasterPipe::ResamplingStage stage)
Select which stage of the pipe should apply resampling.
QgsBrightnessContrastFilter * brightnessFilter() const
Returns the raster's brightness/contrast filter.
QgsRasterRenderer * renderer() const
Returns the raster's renderer.
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.
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.
bool set(QgsRasterInterface *interface)
Insert a new known interface in default place or replace interface of the same role if it already exi...
int size() const
ResamplingStage
Stage at which resampling occurs.
@ Provider
Resampling occurs in Provider.
@ ResampleFilter
Resampling occurs in ResamplingFilter.
QgsRasterProjector * projector() const
bool remove(int idx)
Remove and delete interface at given index if possible.
QgsRasterInterface * last() const
QgsRasterRenderer * renderer() const
void setResamplingStage(ResamplingStage stage)
Select which stage of the pipe should apply resampling.
QgsRasterInterface * at(int idx) const
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.
double opacity() const
Returns the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
void setOpacity(double opacity)
Sets the opacity for the renderer, where opacity is a value between 0 (totally transparent) and 1....
const QgsRasterMinMaxOrigin & minMaxOrigin() const
Returns const reference to origin of min/max values.
virtual void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
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:209
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:437
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 Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:69
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:316
#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.
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.