QGIS API Documentation  3.20.0-Odense (decaadbb31)
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 "qgsxmlutils.h"
47 #include "qgsrectangle.h"
48 #include "qgsrendercontext.h"
52 #include "qgssettings.h"
53 #include "qgssymbollayerutils.h"
54 #include "qgsgdalprovider.h"
58 #include "qgsruntimeprofiler.h"
59 #include "qgsmaplayerfactory.h"
60 
61 #include <cmath>
62 #include <cstdio>
63 #include <limits>
64 #include <typeinfo>
65 
66 #include <QApplication>
67 #include <QCursor>
68 #include <QDir>
69 #include <QDomElement>
70 #include <QDomNode>
71 #include <QFile>
72 #include <QFileInfo>
73 #include <QFont>
74 #include <QFontMetrics>
75 #include <QFrame>
76 #include <QImage>
77 #include <QLabel>
78 #include <QList>
79 #include <QPainter>
80 #include <QPixmap>
81 #include <QRegExp>
82 #include <QSlider>
83 #include <QUrl>
84 
85 #define ERR(message) QGS_ERROR_MESSAGE(message,"Raster layer")
86 
87 const double QgsRasterLayer::SAMPLE_SIZE = 250000;
88 
95 
102 
105  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
106  , TRSTRING_NOT_SET( tr( "Not Set" ) )
107  , mTemporalProperties( new QgsRasterLayerTemporalProperties( this ) )
108 
109 {
110  init();
111  setValid( false );
112 }
113 
114 QgsRasterLayer::QgsRasterLayer( const QString &uri,
115  const QString &baseName,
116  const QString &providerKey,
117  const LayerOptions &options )
118  : QgsMapLayer( QgsMapLayerType::RasterLayer, baseName, uri )
119  // Constant that signals property not used.
120  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
121  , TRSTRING_NOT_SET( tr( "Not Set" ) )
122  , mTemporalProperties( new QgsRasterLayerTemporalProperties( this ) )
123 {
125 
126  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
127  setProviderType( providerKey );
128 
129  QgsDataProvider::ProviderOptions providerOptions { options.transformContext };
130  QgsDataProvider::ReadFlags providerFlags = QgsDataProvider::ReadFlags();
131  if ( options.loadDefaultStyle )
132  {
133  providerFlags |= QgsDataProvider::FlagLoadDefaultStyle;
134  }
135  setDataSource( uri, baseName, providerKey, providerOptions, providerFlags );
136 
137  if ( isValid() )
138  {
139  mTemporalProperties->setDefaultsFromDataProviderTemporalCapabilities( mDataProvider->temporalCapabilities() );
140  }
141 
142 } // QgsRasterLayer ctor
143 
145 {
146  emit willBeDeleted();
147 
148  setValid( false );
149  // Note: provider and other interfaces are owned and deleted by pipe
150 }
151 
153 {
155  if ( mDataProvider )
156  {
157  options.transformContext = mDataProvider->transformContext();
158  }
159  QgsRasterLayer *layer = new QgsRasterLayer( source(), name(), mProviderKey, options );
160  QgsMapLayer::clone( layer );
161 
162  // do not clone data provider which is the first element in pipe
163  for ( int i = 1; i < mPipe.size(); i++ )
164  {
165  if ( mPipe.at( i ) )
166  layer->pipe()->set( mPipe.at( i )->clone() );
167  }
168 
169  return layer;
170 }
171 
173 //
174 // Static Methods and members
175 //
177 
178 bool QgsRasterLayer::isValidRasterFileName( const QString &fileNameQString, QString &retErrMsg )
179 {
180  bool myIsValid = QgsGdalProvider::isValidRasterFileName( fileNameQString, retErrMsg );
181  return myIsValid;
182 }
183 
184 bool QgsRasterLayer::isValidRasterFileName( QString const &fileNameQString )
185 {
186  QString retErrMsg;
187  return isValidRasterFileName( fileNameQString, retErrMsg );
188 }
189 
190 QDateTime QgsRasterLayer::lastModified( QString const &name )
191 {
192  QgsDebugMsgLevel( "name=" + name, 4 );
193  QDateTime t;
194 
195  QFileInfo fi( name );
196 
197  // Is it file?
198  if ( !fi.exists() )
199  return t;
200 
201  t = fi.lastModified();
202 
203  QgsDebugMsgLevel( "last modified = " + t.toString(), 4 );
204 
205  return t;
206 }
207 
208 void QgsRasterLayer::setDataProvider( const QString &provider )
209 {
211 }
212 
213 // typedef for the QgsDataProvider class factory
214 typedef QgsDataProvider *classFactoryFunction_t( const QString *, const QgsDataProvider::ProviderOptions &options );
215 
217 //
218 // Non Static Public methods
219 //
221 
223 {
224  if ( !mDataProvider ) return 0;
225  return mDataProvider->bandCount();
226 }
227 
228 QString QgsRasterLayer::bandName( int bandNo ) const
229 {
230  if ( !mDataProvider ) return QString();
231  return mDataProvider->generateBandName( bandNo );
232 }
233 
234 void QgsRasterLayer::setRendererForDrawingStyle( QgsRaster::DrawingStyle drawingStyle )
235 {
236  setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( drawingStyle, mDataProvider ) );
237 }
238 
240 {
241  return mDataProvider;
242 }
243 
245 {
246  return mDataProvider;
247 }
248 
250 {
251  if ( mDataProvider )
252  {
253  mDataProvider->reloadData();
254  }
255 }
256 
258 {
259  return new QgsRasterLayerRenderer( this, rendererContext );
260 }
261 
262 
263 void QgsRasterLayer::draw( QPainter *theQPainter,
264  QgsRasterViewPort *rasterViewPort,
265  const QgsMapToPixel *qgsMapToPixel )
266 {
267  QgsDebugMsgLevel( QStringLiteral( " 3 arguments" ), 4 );
268  QElapsedTimer time;
269  time.start();
270  //
271  //
272  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
273  // so that we can maximise performance of the rendering process. So now we check which drawing
274  // procedure to use :
275  //
276 
277  QgsRasterProjector *projector = mPipe.projector();
278  bool restoreOldResamplingStage = false;
279  QgsRasterPipe::ResamplingStage oldResamplingState = resamplingStage();
280  // TODO add a method to interface to get provider and get provider
281  // params in QgsRasterProjector
282 
283  if ( projector )
284  {
285  // Force provider resampling if reprojection is needed
286  if ( mDataProvider != nullptr &&
288  rasterViewPort->mSrcCRS != rasterViewPort->mDestCRS &&
289  oldResamplingState != QgsRasterPipe::ResamplingStage::Provider )
290  {
291  restoreOldResamplingStage = true;
293  }
294  projector->setCrs( rasterViewPort->mSrcCRS, rasterViewPort->mDestCRS, rasterViewPort->mTransformContext );
295  }
296 
297  // Drawer to pipe?
298  QgsRasterIterator iterator( mPipe.last() );
299  QgsRasterDrawer drawer( &iterator );
300  drawer.draw( theQPainter, rasterViewPort, qgsMapToPixel );
301 
302  if ( restoreOldResamplingStage )
303  {
304  setResamplingStage( oldResamplingState );
305  }
306 
307  QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
308 }
309 
311 {
313  return renderer ? renderer->legendSymbologyItems() : QList< QPair< QString, QColor > >();;
314 }
315 
317 {
318  if ( !mDataProvider )
319  return QString();
320 
321  QgsLayerMetadataFormatter htmlFormatter( metadata() );
322  QString myMetadata = QStringLiteral( "<html><head></head>\n<body>\n" );
323 
324  // Begin Provider section
325  myMetadata += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" ) %
326  QStringLiteral( "<table class=\"list-view\">\n" ) %
327 
328  // name
329  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Name" ) % QStringLiteral( "</td><td>" ) % name() % QStringLiteral( "</td></tr>\n" );
330 
331  // local path
332  QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( mProviderKey, publicSource() );
333  QString path;
334  bool isLocalPath = false;
335  if ( uriComponents.contains( QStringLiteral( "path" ) ) )
336  {
337  path = uriComponents[QStringLiteral( "path" )].toString();
338  if ( QFile::exists( path ) )
339  {
340  isLocalPath = true;
341  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" );
342  }
343  }
344  if ( uriComponents.contains( QStringLiteral( "url" ) ) )
345  {
346  const QString url = uriComponents[QStringLiteral( "url" )].toString();
347  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" );
348  }
349 
350  // data source
351  if ( publicSource() != path || !isLocalPath )
352  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>%1" ).arg( publicSource() != path ? publicSource() : path ) + QStringLiteral( "</td></tr>\n" );
353 
354  myMetadata += QLatin1String( "</table>\n<br><br>" );
355 
356  // CRS
357  myMetadata += crsHtmlMetadata();
358 
359  myMetadata += QStringLiteral( "<h1>" ) + tr( "Properties" ) + QStringLiteral( "</h1>\n<hr>\n" ) + QStringLiteral( "<table class=\"list-view\">\n" );
360 
361  myMetadata += QStringLiteral( "\n" ) %
362 
363  // Extent
364  QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Extent" ) % QStringLiteral( "</td><td>" ) % extent().toString() % 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  {
388  myMetadata += tr( "Byte - Eight bit unsigned integer" );
389  break;
391  myMetadata += tr( "UInt16 - Sixteen bit unsigned integer " );
392  break;
394  myMetadata += tr( "Int16 - Sixteen bit signed integer " );
395  break;
397  myMetadata += tr( "UInt32 - Thirty two bit unsigned integer " );
398  break;
400  myMetadata += tr( "Int32 - Thirty two bit signed integer " );
401  break;
403  myMetadata += tr( "Float32 - Thirty two bit floating point " );
404  break;
406  myMetadata += tr( "Float64 - Sixty four bit floating point " );
407  break;
409  myMetadata += tr( "CInt16 - Complex Int16 " );
410  break;
412  myMetadata += tr( "CInt32 - Complex Int32 " );
413  break;
415  myMetadata += tr( "CFloat32 - Complex Float32 " );
416  break;
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 
473  {
474  QgsRasterBandStats myRasterBandStats = provider->bandStatistics( i, QgsRasterBandStats::Min | QgsRasterBandStats::Max, provider->extent(), SAMPLE_SIZE );
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 );
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 = std::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 
678  {
679  // get the extent
680  const QgsRectangle mbr = mDataProvider->extent();
681 
682  // store the extent
683  setExtent( mbr );
684  }
685 
686  // upper case the first letter of the layer name
687  QgsDebugMsgLevel( "mLayerName: " + name(), 4 );
688 
689  // set up the raster drawing style
690  // Do not set any 'sensible' style here, the style is set later
691 
692  // Setup source CRS
693  setCrs( QgsCoordinateReferenceSystem( mDataProvider->crs() ) );
694 
695  QgsDebugMsgLevel( "using wkt:\n" + crs().toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), 4 );
696 
697  //defaults - Needs to be set after the Contrast list has been build
698  //Try to read the default contrast enhancement from the config file
699 
700  //decide what type of layer this is...
701  //TODO Change this to look at the color interp and palette interp to decide which type of layer it is
702  QgsDebugMsgLevel( "bandCount = " + QString::number( mDataProvider->bandCount() ), 4 );
703  QgsDebugMsgLevel( "dataType = " + qgsEnumValueToKey< Qgis::DataType >( mDataProvider->dataType( 1 ) ), 4 );
704  if ( ( mDataProvider->bandCount() > 1 ) )
705  {
706  // handle singleband gray with alpha
707  if ( mDataProvider->bandCount() == 2
708  && ( ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::GrayIndex
709  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::AlphaBand )
710  || ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::AlphaBand
711  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::GrayIndex ) ) )
712  {
713  mRasterType = GrayOrUndefined;
714  }
715  else
716  {
717  mRasterType = Multiband;
718  }
719  }
720  else if ( mDataProvider->dataType( 1 ) == Qgis::DataType::ARGB32
721  || mDataProvider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied )
722  {
723  mRasterType = ColorLayer;
724  }
725  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
726  {
727  mRasterType = Palette;
728  }
729  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
730  {
731  mRasterType = Palette;
732  }
733  else
734  {
735  mRasterType = GrayOrUndefined;
736  }
737 
738  QgsDebugMsgLevel( "mRasterType = " + QString::number( mRasterType ), 4 );
739  if ( mRasterType == ColorLayer )
740  {
741  QgsDebugMsgLevel( "Setting drawing style to SingleBandColorDataStyle " + QString::number( QgsRaster::SingleBandColorDataStyle ), 4 );
742  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
743  }
744  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
745  {
746  setRendererForDrawingStyle( QgsRaster::PalettedColor ); //sensible default
747  }
748  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
749  {
750  setRendererForDrawingStyle( QgsRaster::SingleBandPseudoColor );
751  // Load color table
752  QList<QgsColorRampShader::ColorRampItem> colorTable = mDataProvider->colorTable( 1 );
754  if ( r )
755  {
756  // TODO: this should go somewhere else
757  QgsRasterShader *shader = new QgsRasterShader();
758  QgsColorRampShader *colorRampShader = new QgsColorRampShader();
760  colorRampShader->setColorRampItemList( colorTable );
761  shader->setRasterShaderFunction( colorRampShader );
762  r->setShader( shader );
763  }
764  }
765  else if ( mRasterType == Multiband )
766  {
767  setRendererForDrawingStyle( QgsRaster::MultiBandColor ); //sensible default
768  }
769  else //GrayOrUndefined
770  {
771  setRendererForDrawingStyle( QgsRaster::SingleBandGray ); //sensible default
772  }
773 
774  // Auto set alpha band
775  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
776  {
777  if ( mDataProvider->colorInterpretation( bandNo ) == QgsRaster::AlphaBand )
778  {
779  if ( auto *lRenderer = mPipe.renderer() )
780  {
781  lRenderer->setAlphaBand( bandNo );
782  }
783  break;
784  }
785  }
786 
787  // brightness filter
789  mPipe.set( brightnessFilter );
790 
791  // hue/saturation filter
793  mPipe.set( hueSaturationFilter );
794 
795  // resampler (must be after renderer)
797  mPipe.set( resampleFilter );
798 
800  {
801  QgsSettings settings;
802  QString resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedInResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
803  if ( resampling == QLatin1String( "bilinear" ) )
804  {
807  }
808  else if ( resampling == QLatin1String( "cubic" ) )
809  {
812  }
813  resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedOutResampling" ), QStringLiteral( "nearest neighbour" ) ).toString();
814  if ( resampling == QLatin1String( "bilinear" ) )
815  {
818  }
819 
820  const double maxOversampling = settings.value( QStringLiteral( "/Raster/defaultOversampling" ), 2.0 ).toDouble();
821  resampleFilter->setMaxOversampling( maxOversampling );
822  mDataProvider->setMaxOversampling( maxOversampling );
823 
825  settings.value( QStringLiteral( "/Raster/defaultEarlyResampling" ), false ).toBool() )
826  {
828  }
829  else
830  {
832  }
833  }
834 
835  // projector (may be anywhere in pipe)
836  QgsRasterProjector *projector = new QgsRasterProjector;
837  mPipe.set( projector );
838 
839  // Set default identify format - use the richest format available
840  int capabilities = mDataProvider->capabilities();
842  if ( capabilities & QgsRasterInterface::IdentifyHtml )
843  {
844  // HTML is usually richest
845  identifyFormat = QgsRaster::IdentifyFormatHtml;
846  }
847  else if ( capabilities & QgsRasterInterface::IdentifyFeature )
848  {
849  identifyFormat = QgsRaster::IdentifyFormatFeature;
850  }
851  else if ( capabilities & QgsRasterInterface::IdentifyText )
852  {
853  identifyFormat = QgsRaster::IdentifyFormatText;
854  }
855  else if ( capabilities & QgsRasterInterface::IdentifyValue )
856  {
857  identifyFormat = QgsRaster::IdentifyFormatValue;
858  }
859  setCustomProperty( QStringLiteral( "identify/format" ), QgsRasterDataProvider::identifyFormatName( identifyFormat ) );
860 
861  // Store timestamp
862  // TODO move to provider
863  mLastModified = lastModified( mDataSource );
864 
865  // Do a passthrough for the status bar text
866  connect( mDataProvider, &QgsRasterDataProvider::statusChanged, this, &QgsRasterLayer::statusChanged );
867 
868  //mark the layer as valid
869  setValid( true );
870 
871  if ( mDataProvider->supportsSubsetString() )
872  connect( this, &QgsRasterLayer::subsetStringChanged, this, &QgsMapLayer::configChanged, Qt::UniqueConnection );
873  else
875 
876 
877  QgsDebugMsgLevel( QStringLiteral( "exiting." ), 4 );
878 
879 }
880 
881 void QgsRasterLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider,
882  const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
883 {
884  bool hadRenderer( renderer() );
885 
886  QDomImplementation domImplementation;
887  QDomDocumentType documentType;
888  QString errorMsg;
889 
890  bool loadDefaultStyleFlag = false;
892  {
893  loadDefaultStyleFlag = true;
894  }
895 
896  // Store the original style
897  if ( hadRenderer && ! loadDefaultStyleFlag )
898  {
899  documentType = domImplementation.createDocumentType(
900  QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
901 
902  QDomDocument doc = QDomDocument( documentType );
903  QDomElement styleElem = doc.createElement( QStringLiteral( "qgis" ) );
904  styleElem.setAttribute( QStringLiteral( "version" ), Qgis::version() );
905  QgsReadWriteContext writeContext;
906  if ( ! writeSymbology( styleElem, doc, errorMsg, writeContext ) )
907  {
908  QgsDebugMsg( QStringLiteral( "Could not store symbology for layer %1: %2" )
909  .arg( name(),
910  errorMsg ) );
911  }
912  else
913  {
914  doc.appendChild( styleElem );
915 
916  mOriginalStyleDocument = doc;
917  mOriginalStyleElement = styleElem;
918  }
919  }
920 
921  if ( mDataProvider )
922  closeDataProvider();
923 
924  init();
925 
926  for ( int i = mPipe.size() - 1; i >= 0; --i )
927  {
928  mPipe.remove( i );
929  }
930 
931  mDataSource = dataSource;
932  mLayerName = baseName;
933 
934  setDataProvider( provider, options, flags );
935 
936  if ( mDataProvider )
937  mDataProvider->setDataSourceUri( mDataSource );
938 
939  if ( isValid() )
940  {
941  // load default style
942  bool defaultLoadedFlag = false;
943  bool restoredStyle = false;
944  if ( loadDefaultStyleFlag )
945  {
946  loadDefaultStyle( defaultLoadedFlag );
947  }
948  else if ( !mOriginalStyleElement.isNull() ) // Restore the style
949  {
950  QgsReadWriteContext readContext;
951  if ( ! readSymbology( mOriginalStyleElement, errorMsg, readContext ) )
952  {
953  QgsDebugMsg( QStringLiteral( "Could not restore symbology for layer %1: %2" )
954  .arg( name() )
955  .arg( errorMsg ) );
956 
957  }
958  else
959  {
960  restoredStyle = true;
961  emit repaintRequested();
963  emit rendererChanged();
964  }
965  }
966 
967  if ( !defaultLoadedFlag && !restoredStyle )
968  {
970  }
971  }
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  }
1010  {
1011  myRasterBandStats.minimumValue = 0;
1012  myRasterBandStats.maximumValue = std::numeric_limits<uint16_t>::max();
1013  break;
1014  }
1016  {
1017  myRasterBandStats.minimumValue = 0;
1018  myRasterBandStats.maximumValue = std::numeric_limits<uint32_t>::max();
1019  break;
1020  }
1021  case Qgis::DataType::Int16:
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:
1030  {
1031  myRasterBandStats.minimumValue = std::numeric_limits<int32_t>::lowest();
1032  myRasterBandStats.maximumValue = std::numeric_limits<int32_t>::max();
1033  break;
1034  }
1037  {
1038  myRasterBandStats.minimumValue = std::numeric_limits<float_t>::lowest();
1039  myRasterBandStats.maximumValue = std::numeric_limits<float_t>::max();
1040  break;
1041  }
1044  {
1045  myRasterBandStats.minimumValue = std::numeric_limits<double_t>::lowest();
1046  myRasterBandStats.maximumValue = std::numeric_limits<double_t>::max();
1047  break;
1048  }
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  emitStyleChanged();
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  emitStyleChanged();
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  emitStyleChanged();
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  emitStyleChanged();
1747 }
1748 
1749 void QgsRasterLayer::showStatusMessage( QString const &message )
1750 {
1751  // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
1752 
1753  // Pass-through
1754  // TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
1755  emit statusChanged( message );
1756 }
1757 
1759 {
1760  if ( mDataProvider )
1761  mDataProvider->setTransformContext( transformContext );
1763 }
1764 
1765 QStringList QgsRasterLayer::subLayers() const
1766 {
1767  if ( ! mDataProvider )
1768  return QStringList();
1769  return mDataProvider->subLayers();
1770 }
1771 
1772 // this function should be used when rendering with the MTR engine introduced in 2.3, as QPixmap is not thread safe (see bug #9626)
1773 // note: previewAsImage and previewAsPixmap should use a common low-level fct QgsRasterLayer::previewOnPaintDevice( QSize size, QColor bgColor, QPaintDevice &device )
1774 QImage QgsRasterLayer::previewAsImage( QSize size, const QColor &bgColor, QImage::Format format )
1775 {
1776  QImage image( size, format );
1777 
1778  if ( ! isValid( ) )
1779  return QImage();
1780 
1781  if ( image.format() == QImage::Format_Indexed8 )
1782  {
1783  image.setColor( 0, bgColor.rgba() );
1784  image.fill( 0 ); //defaults to white, set to transparent for rendering on a map
1785  }
1786  else
1787  {
1788  image.fill( bgColor );
1789  }
1790 
1791  QgsRasterViewPort *rasterViewPort = new QgsRasterViewPort();
1792 
1793  double mapUnitsPerPixel;
1794  double x = 0.0;
1795  double y = 0.0;
1796  QgsRectangle extent = mDataProvider->extent();
1797  if ( extent.width() / extent.height() >= static_cast< double >( image.width() ) / image.height() )
1798  {
1799  mapUnitsPerPixel = extent.width() / image.width();
1800  y = ( image.height() - extent.height() / mapUnitsPerPixel ) / 2;
1801  }
1802  else
1803  {
1804  mapUnitsPerPixel = extent.height() / image.height();
1805  x = ( image.width() - extent.width() / mapUnitsPerPixel ) / 2;
1806  }
1807 
1808  const double pixelWidth = extent.width() / mapUnitsPerPixel;
1809  const double pixelHeight = extent.height() / mapUnitsPerPixel;
1810 
1811  rasterViewPort->mTopLeftPoint = QgsPointXY( x, y );
1812  rasterViewPort->mBottomRightPoint = QgsPointXY( pixelWidth, pixelHeight );
1813  rasterViewPort->mWidth = image.width();
1814  rasterViewPort->mHeight = image.height();
1815 
1816  rasterViewPort->mDrawnExtent = extent;
1817  rasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
1818  rasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
1819 
1820  QgsMapToPixel *mapToPixel = new QgsMapToPixel( mapUnitsPerPixel );
1821 
1822  QPainter *painter = new QPainter( &image );
1823  draw( painter, rasterViewPort, mapToPixel );
1824  delete rasterViewPort;
1825  delete mapToPixel;
1826 
1827  painter->end();
1828  delete painter;
1829 
1830  return image;
1831 }
1832 
1834 //
1835 // Protected methods
1836 //
1838 /*
1839  * \param QDomNode node that will contain the symbology definition for this layer.
1840  * \param errorMessage reference to string that will be updated with any error messages
1841  * \return TRUE in case of success.
1842  */
1843 bool QgsRasterLayer::readSymbology( const QDomNode &layer_node, QString &errorMessage,
1844  QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1845 {
1846  Q_UNUSED( errorMessage )
1847  // TODO: implement categories for raster layer
1848 
1849  QDomElement rasterRendererElem;
1850 
1851  QDomElement layerElement = layer_node.toElement();
1852  readCommonStyle( layerElement, context, categories );
1853 
1854  // pipe element was introduced in the end of 1.9 development when there were
1855  // already many project files in use so we support 1.9 backward compatibility
1856  // even it was never officially released -> use pipe element if present, otherwise
1857  // use layer node
1858  QDomNode pipeNode = layer_node.firstChildElement( QStringLiteral( "pipe" ) );
1859  if ( pipeNode.isNull() ) // old project
1860  {
1861  pipeNode = layer_node;
1862  }
1863 
1864  //rasterlayerproperties element there -> old format (1.8 and early 1.9)
1865  if ( !layer_node.firstChildElement( QStringLiteral( "rasterproperties" ) ).isNull() )
1866  {
1867  //copy node because layer_node is const
1868  QDomNode layerNodeCopy = layer_node.cloneNode();
1869  QDomDocument doc = layerNodeCopy.ownerDocument();
1870  QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterproperties" ) );
1871  QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
1872  this );
1873  rasterRendererElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1874  QgsDebugMsgLevel( doc.toString(), 4 );
1875  }
1876  else
1877  {
1878  rasterRendererElem = pipeNode.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1879  }
1880 
1881  if ( !rasterRendererElem.isNull() )
1882  {
1883  QString rendererType = rasterRendererElem.attribute( QStringLiteral( "type" ) );
1884  QgsRasterRendererRegistryEntry rendererEntry;
1885  if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererType, rendererEntry ) )
1886  {
1887  QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
1888  mPipe.set( renderer );
1889  }
1890  }
1891 
1892  //brightness
1894  mPipe.set( brightnessFilter );
1895 
1896  //brightness coefficient
1897  QDomElement brightnessElem = pipeNode.firstChildElement( QStringLiteral( "brightnesscontrast" ) );
1898  if ( !brightnessElem.isNull() )
1899  {
1900  brightnessFilter->readXml( brightnessElem );
1901  }
1902 
1903  //hue/saturation
1905  mPipe.set( hueSaturationFilter );
1906 
1907  //saturation coefficient
1908  QDomElement hueSaturationElem = pipeNode.firstChildElement( QStringLiteral( "huesaturation" ) );
1909  if ( !hueSaturationElem.isNull() )
1910  {
1911  hueSaturationFilter->readXml( hueSaturationElem );
1912  }
1913 
1914  //resampler
1916  mPipe.set( resampleFilter );
1917 
1918  //max oversampling
1919  QDomElement resampleElem = pipeNode.firstChildElement( QStringLiteral( "rasterresampler" ) );
1920  if ( !resampleElem.isNull() )
1921  {
1922  resampleFilter->readXml( resampleElem );
1923  }
1924 
1925  //provider
1926  if ( mDataProvider )
1927  {
1928  QDomElement providerElem = pipeNode.firstChildElement( QStringLiteral( "provider" ) );
1929  if ( !providerElem.isNull() )
1930  {
1931  mDataProvider->readXml( providerElem );
1932  }
1933  }
1934 
1935  // Resampling stage
1936  QDomNode resamplingStageElement = pipeNode.namedItem( QStringLiteral( "resamplingStage" ) );
1937  if ( !resamplingStageElement.isNull() )
1938  {
1939  QDomElement e = resamplingStageElement.toElement();
1940  if ( e.text() == QLatin1String( "provider" ) )
1942  else if ( e.text() == QLatin1String( "resamplingFilter" ) )
1944  }
1945 
1946  // get and set the blend mode if it exists
1947  QDomNode blendModeNode = layer_node.namedItem( QStringLiteral( "blendMode" ) );
1948  if ( !blendModeNode.isNull() )
1949  {
1950  QDomElement e = blendModeNode.toElement();
1951  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
1952  }
1953 
1954  readCustomProperties( layer_node );
1955 
1956  return true;
1957 }
1958 
1959 bool QgsRasterLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1960 {
1961  return readSymbology( node, errorMessage, context, categories );
1962 }
1963 
1964 bool QgsRasterLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1965 {
1966  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1967  // Make sure to read the file first so stats etc are initialized properly!
1968 
1969  //process provider key
1970  QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
1971 
1972  if ( pkeyNode.isNull() )
1973  {
1974  mProviderKey = QStringLiteral( "gdal" );
1975  }
1976  else
1977  {
1978  QDomElement pkeyElt = pkeyNode.toElement();
1979  mProviderKey = pkeyElt.text();
1980  if ( mProviderKey.isEmpty() )
1981  {
1982  mProviderKey = QStringLiteral( "gdal" );
1983  }
1984  }
1985 
1986  // Open the raster source based on provider and datasource
1987 
1988  // Go down the raster-data-provider paradigm
1989 
1990  // Collect provider-specific information
1991 
1992  QDomNode rpNode = layer_node.namedItem( QStringLiteral( "rasterproperties" ) );
1993 
1994  if ( mProviderKey == QLatin1String( "wms" ) )
1995  {
1996  // >>> BACKWARD COMPATIBILITY < 1.9
1997  // The old WMS URI format does not contain all the information, we add them here.
1998  if ( !mDataSource.contains( QLatin1String( "crs=" ) ) && !mDataSource.contains( QLatin1String( "format=" ) ) )
1999  {
2000  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> adding params" ), 4 );
2001  QgsDataSourceUri uri;
2002  uri.setEncodedUri( mDataSource );
2003  QDomElement layerElement = rpNode.firstChildElement( QStringLiteral( "wmsSublayer" ) );
2004  while ( !layerElement.isNull() )
2005  {
2006  // TODO: sublayer visibility - post-0.8 release timeframe
2007 
2008  // collect name for the sublayer
2009  uri.setParam( QStringLiteral( "layers" ), layerElement.namedItem( QStringLiteral( "name" ) ).toElement().text() );
2010 
2011  // collect style for the sublayer
2012  uri.setParam( QStringLiteral( "styles" ), layerElement.namedItem( QStringLiteral( "style" ) ).toElement().text() );
2013 
2014  layerElement = layerElement.nextSiblingElement( QStringLiteral( "wmsSublayer" ) );
2015  }
2016 
2017  // Collect format
2018  uri.setParam( QStringLiteral( "format" ), rpNode.namedItem( QStringLiteral( "wmsFormat" ) ).toElement().text() );
2019 
2020  // WMS CRS URL param should not be mixed with that assigned to the layer.
2021  // In the old WMS URI version there was no CRS and layer crs().authid() was used.
2022  uri.setParam( QStringLiteral( "crs" ), crs().authid() );
2023  mDataSource = uri.encodedUri();
2024  }
2025  // <<< BACKWARD COMPATIBILITY < 1.9
2026  }
2027 
2029  {
2030  QgsDataProvider::ProviderOptions providerOptions { context.transformContext() };
2031  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2033  {
2035  }
2036  // read extent
2038  {
2039  const QDomNode extentNode = layer_node.namedItem( QStringLiteral( "extent" ) );
2040  if ( !extentNode.isNull() )
2041  {
2042  // get the extent
2043  const QgsRectangle mbr = QgsXmlUtils::readRectangle( extentNode.toElement() );
2044 
2045  // store the extent
2046  setExtent( mbr );
2047 
2048  // skip get extent
2050  }
2051  }
2052  setDataProvider( mProviderKey, providerOptions, flags );
2053  }
2054 
2055  mOriginalStyleElement = layer_node.namedItem( QStringLiteral( "originalStyle" ) ).firstChildElement();
2056  if ( mOriginalStyleElement.isNull() )
2057  mOriginalStyleElement = layer_node.toElement();
2058  mOriginalStyleDocument = layer_node.ownerDocument();
2059 
2060  if ( ! mDataProvider )
2061  {
2063  {
2064  QgsDebugMsg( QStringLiteral( "Raster data provider could not be created for %1" ).arg( mDataSource ) );
2065  }
2066  return false;
2067  }
2068 
2069  QString error;
2070  bool res = readSymbology( layer_node, error, context );
2071 
2072  // old wms settings we need to correct
2073  if ( res && mProviderKey == QLatin1String( "wms" ) && ( !renderer() || renderer()->type() != QLatin1String( "singlebandcolordata" ) ) )
2074  {
2075  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
2076  }
2077 
2078  // Check timestamp
2079  // This was probably introduced to reload completely raster if data changed and
2080  // reset completely symbology to reflect new data type etc. It creates however
2081  // problems, because user defined symbology is complete lost if data file time
2082  // changed (the content may be the same). See also 6900.
2083 #if 0
2084  QDomNode stampNode = layer_node.namedItem( "timestamp" );
2085  if ( !stampNode.isNull() )
2086  {
2087  QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
2088  // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
2089  if ( stamp < mDataProvider->dataTimestamp() )
2090  {
2091  QgsDebugMsgLevel( QStringLiteral( "data changed, reload provider" ), 3 );
2092  closeDataProvider();
2093  init();
2095  if ( !isValid() ) return false;
2096  }
2097  }
2098 #endif
2099 
2100  // Load user no data value
2101  QDomElement noDataElement = layer_node.firstChildElement( QStringLiteral( "noData" ) );
2102 
2103  QDomNodeList noDataBandList = noDataElement.elementsByTagName( QStringLiteral( "noDataList" ) );
2104 
2105  for ( int i = 0; i < noDataBandList.size(); ++i )
2106  {
2107  QDomElement bandElement = noDataBandList.at( i ).toElement();
2108  bool ok;
2109  int bandNo = bandElement.attribute( QStringLiteral( "bandNo" ) ).toInt( &ok );
2110  QgsDebugMsgLevel( QStringLiteral( "bandNo = %1" ).arg( bandNo ), 4 );
2111  if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
2112  {
2113  mDataProvider->setUseSourceNoDataValue( bandNo, bandElement.attribute( QStringLiteral( "useSrcNoData" ) ).toInt() );
2114  QgsRasterRangeList myNoDataRangeList;
2115 
2116  QDomNodeList rangeList = bandElement.elementsByTagName( QStringLiteral( "noDataRange" ) );
2117 
2118  myNoDataRangeList.reserve( rangeList.size() );
2119  for ( int j = 0; j < rangeList.size(); ++j )
2120  {
2121  QDomElement rangeElement = rangeList.at( j ).toElement();
2122  QgsRasterRange myNoDataRange( rangeElement.attribute( QStringLiteral( "min" ) ).toDouble(),
2123  rangeElement.attribute( QStringLiteral( "max" ) ).toDouble() );
2124  QgsDebugMsgLevel( QStringLiteral( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ), 4 );
2125  myNoDataRangeList << myNoDataRange;
2126  }
2127  mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
2128  }
2129  }
2130 
2131  readStyleManager( layer_node );
2132 
2133  return res;
2134 }
2135 
2136 bool QgsRasterLayer::writeSymbology( QDomNode &layer_node, QDomDocument &document, QString &errorMessage,
2137  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2138 {
2139  Q_UNUSED( errorMessage )
2140  // TODO: implement categories for raster layer
2141 
2142  QDomElement layerElement = layer_node.toElement();
2143  writeCommonStyle( layerElement, document, context, categories );
2144 
2145  // Store pipe members into pipe element, in future, it will be
2146  // possible to add custom filters into the pipe
2147  QDomElement pipeElement = document.createElement( QStringLiteral( "pipe" ) );
2148 
2149  for ( int i = 0; i < mPipe.size(); i++ )
2150  {
2151  QgsRasterInterface *interface = mPipe.at( i );
2152  if ( !interface ) continue;
2153  interface->writeXml( document, pipeElement );
2154  }
2155 
2156  QDomElement resamplingStageElement = document.createElement( QStringLiteral( "resamplingStage" ) );
2157  QDomText resamplingStageText = document.createTextNode( resamplingStage() == QgsRasterPipe::ResamplingStage::Provider ? QStringLiteral( "provider" ) : QStringLiteral( "resamplingFilter" ) );
2158  resamplingStageElement.appendChild( resamplingStageText );
2159  pipeElement.appendChild( resamplingStageElement );
2160 
2161  layer_node.appendChild( pipeElement );
2162 
2163  if ( !isValid() && !mOriginalStyleElement.isNull() )
2164  {
2165  QDomElement originalStyleElement = document.createElement( QStringLiteral( "originalStyle" ) );
2166  originalStyleElement.appendChild( mOriginalStyleElement );
2167  layer_node.appendChild( originalStyleElement );
2168  }
2169 
2170  // add blend mode node
2171  QDomElement blendModeElement = document.createElement( QStringLiteral( "blendMode" ) );
2172  QDomText blendModeText = document.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
2173  blendModeElement.appendChild( blendModeText );
2174  layer_node.appendChild( blendModeElement );
2175 
2176  return true;
2177 }
2178 
2179 bool QgsRasterLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage,
2180  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2181 {
2182  return writeSymbology( node, doc, errorMessage, context, categories );
2183 } // bool QgsRasterLayer::writeSymbology
2184 
2185 /*
2186  * virtual
2187  * \note Called by QgsMapLayer::writeXml().
2188  */
2189 bool QgsRasterLayer::writeXml( QDomNode &layer_node,
2190  QDomDocument &document,
2191  const QgsReadWriteContext &context ) const
2192 {
2193  if ( !mDataProvider )
2194  return false;
2195 
2196  // first get the layer element so that we can append the type attribute
2197 
2198  QDomElement mapLayerNode = layer_node.toElement();
2199 
2200  if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
2201  {
2202  QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
2203  return false;
2204  }
2205 
2206  mapLayerNode.setAttribute( QStringLiteral( "type" ), QgsMapLayerFactory::typeToString( QgsMapLayerType::RasterLayer ) );
2207 
2208  // add provider node
2209 
2210  QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
2211  QDomText providerText = document.createTextNode( mProviderKey );
2212  provider.appendChild( providerText );
2213  layer_node.appendChild( provider );
2214 
2215  // User no data
2216  QDomElement noData = document.createElement( QStringLiteral( "noData" ) );
2217 
2218  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
2219  {
2220  QDomElement noDataRangeList = document.createElement( QStringLiteral( "noDataList" ) );
2221  noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), bandNo );
2222  noDataRangeList.setAttribute( QStringLiteral( "useSrcNoData" ), mDataProvider->useSourceNoDataValue( bandNo ) );
2223 
2224  const auto constUserNoDataValues = mDataProvider->userNoDataValues( bandNo );
2225  for ( QgsRasterRange range : constUserNoDataValues )
2226  {
2227  QDomElement noDataRange = document.createElement( QStringLiteral( "noDataRange" ) );
2228 
2229  noDataRange.setAttribute( QStringLiteral( "min" ), QgsRasterBlock::printValue( range.min() ) );
2230  noDataRange.setAttribute( QStringLiteral( "max" ), QgsRasterBlock::printValue( range.max() ) );
2231  noDataRangeList.appendChild( noDataRange );
2232  }
2233 
2234  noData.appendChild( noDataRangeList );
2235 
2236  }
2237  if ( noData.hasChildNodes() )
2238  {
2239  layer_node.appendChild( noData );
2240  }
2241 
2242  writeStyleManager( layer_node, document );
2243 
2244  //write out the symbology
2245  QString errorMsg;
2246  return writeSymbology( layer_node, document, errorMsg, context );
2247 }
2248 
2249 // TODO: this should ideally go to gdal provider (together with most of encodedSource() + decodedSource())
2250 static bool _parseGpkgColons( const QString &src, QString &filename, QString &tablename )
2251 {
2252  // GDAL accepts the following input format: GPKG:filename:table
2253  // (GDAL won't accept quoted filename)
2254 
2255  QStringList lst = src.split( ':' );
2256  if ( lst.count() != 3 && lst.count() != 4 )
2257  return false;
2258 
2259  tablename = lst.last();
2260  if ( lst.count() == 3 )
2261  {
2262  filename = lst[1];
2263  return true;
2264  }
2265  else if ( lst.count() == 4 && lst[1].count() == 1 && ( lst[2][0] == '/' || lst[2][0] == '\\' ) )
2266  {
2267  // a bit of handling to make sure that filename C:\hello.gpkg is parsed correctly
2268  filename = lst[1] + ":" + lst[2];
2269  return true;
2270  }
2271  return false;
2272 }
2273 
2274 
2275 QString QgsRasterLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
2276 {
2277  QString src( source );
2278  bool handled = false;
2279 
2280  // Update path for subdataset
2281  if ( providerType() == QLatin1String( "gdal" ) )
2282  {
2283  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2284  {
2285  // NETCDF:filename:variable
2286  // filename can be quoted with " as it can contain colons
2287  QRegExp r( "NETCDF:(.+):([^:]+)" );
2288  if ( r.exactMatch( src ) )
2289  {
2290  QString filename = r.cap( 1 );
2291  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2292  filename = filename.mid( 1, filename.length() - 2 );
2293  src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
2294  handled = true;
2295  }
2296  }
2297  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2298  {
2299  // GPKG:filename:table
2300  QString filename, tablename;
2301  if ( _parseGpkgColons( src, filename, tablename ) )
2302  {
2303  filename = context.pathResolver().writePath( filename );
2304  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2305  handled = true;
2306  }
2307  }
2308  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2309  {
2310  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2311  // filename can be quoted with " as it can contain colons
2312  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
2313  if ( r.exactMatch( src ) )
2314  {
2315  QString filename = r.cap( 2 );
2316  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2317  filename = filename.mid( 1, filename.length() - 2 );
2318  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 3 );
2319  handled = true;
2320  }
2321  }
2322  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2323  {
2324  // HDF5:file_name:subdataset
2325  // filename can be quoted with " as it can contain colons
2326  QRegExp r( "HDF5:(.+):([^:]+)" );
2327  if ( r.exactMatch( src ) )
2328  {
2329  QString filename = r.cap( 1 );
2330  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2331  filename = filename.mid( 1, filename.length() - 2 );
2332  src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
2333  handled = true;
2334  }
2335  }
2336  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2337  {
2338  // NITF_IM:0:filename
2339  // RADARSAT_2_CALIB:?:filename
2340  QRegExp r( "([^:]+):([^:]+):(.+)" );
2341  if ( r.exactMatch( src ) )
2342  {
2343  src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().writePath( r.cap( 3 ) );
2344  handled = true;
2345  }
2346  }
2347  }
2348  else if ( providerType() == "wms" )
2349  {
2350  // handle relative paths to XYZ tiles
2351  QgsDataSourceUri uri;
2352  uri.setEncodedUri( src );
2353  QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2354  if ( srcUrl.isLocalFile() )
2355  {
2356  // relative path will become "file:./x.txt"
2357  QString relSrcUrl = context.pathResolver().writePath( srcUrl.toLocalFile() );
2358  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2359  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( relSrcUrl ).toString() );
2360  src = uri.encodedUri();
2361  handled = true;
2362  }
2363  }
2364 
2365  if ( !handled )
2366  src = context.pathResolver().writePath( src );
2367 
2368  return src;
2369 }
2370 
2371 QString QgsRasterLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
2372 {
2373  QString src( source );
2374 
2375  if ( provider == QLatin1String( "wms" ) )
2376  {
2377  // >>> BACKWARD COMPATIBILITY < 1.9
2378  // For project file backward compatibility we must support old format:
2379  // 1. mode: <url>
2380  // example: http://example.org/wms?
2381  // 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
2382  // example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
2383  // example: featureCount=10,http://example.org/wms?
2384  // example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
2385  // This is modified version of old QgsWmsProvider::parseUri
2386  // The new format has always params crs,format,layers,styles and that params
2387  // should not appear in old format url -> use them to identify version
2388  // XYZ tile layers do not need to contain crs,format params, but they have type=xyz
2389  if ( !src.contains( QLatin1String( "type=" ) ) &&
2390  !src.contains( QLatin1String( "crs=" ) ) && !src.contains( QLatin1String( "format=" ) ) )
2391  {
2392  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> converting to new format" ), 2 );
2393  QgsDataSourceUri uri;
2394  if ( !src.startsWith( QLatin1String( "http:" ) ) )
2395  {
2396  QStringList parts = src.split( ',' );
2397  QStringListIterator iter( parts );
2398  while ( iter.hasNext() )
2399  {
2400  QString item = iter.next();
2401  if ( item.startsWith( QLatin1String( "username=" ) ) )
2402  {
2403  uri.setUsername( item.mid( 9 ) );
2404  }
2405  else if ( item.startsWith( QLatin1String( "password=" ) ) )
2406  {
2407  uri.setPassword( item.mid( 9 ) );
2408  }
2409  else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
2410  {
2411  // in < 1.9 tiled= may apper in to variants:
2412  // tiled=width;height - non tiled mode, specifies max width and max height
2413  // tiled=width;height;resolutions-1;resolution2;... - tile mode
2414 
2415  QStringList params = item.mid( 6 ).split( ';' );
2416 
2417  if ( params.size() == 2 ) // non tiled mode
2418  {
2419  uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
2420  uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
2421  }
2422  else if ( params.size() > 2 ) // tiled mode
2423  {
2424  // resolutions are no more needed and size limit is not used for tiles
2425  // we have to tell to the provider however that it is tiled
2426  uri.setParam( QStringLiteral( "tileMatrixSet" ), QString() );
2427  }
2428  }
2429  else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
2430  {
2431  uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
2432  }
2433  else if ( item.startsWith( QLatin1String( "url=" ) ) )
2434  {
2435  uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
2436  }
2437  else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
2438  {
2439  uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
2440  }
2441  }
2442  }
2443  else
2444  {
2445  uri.setParam( QStringLiteral( "url" ), src );
2446  }
2447  src = uri.encodedUri();
2448  // At this point, the URI is obviously incomplete, we add additional params
2449  // in QgsRasterLayer::readXml
2450  }
2451  // <<< BACKWARD COMPATIBILITY < 1.9
2452 
2453  // handle relative paths to XYZ tiles
2454  QgsDataSourceUri uri;
2455  uri.setEncodedUri( src );
2456  QUrl srcUrl( uri.param( QStringLiteral( "url" ) ) );
2457  if ( srcUrl.isLocalFile() ) // file-based URL? convert to relative path
2458  {
2459  QString absSrcUrl = context.pathResolver().readPath( srcUrl.toLocalFile() );
2460  uri.removeParam( QStringLiteral( "url" ) ); // needed because setParam() would insert second "url" key
2461  uri.setParam( QStringLiteral( "url" ), QUrl::fromLocalFile( absSrcUrl ).toString() );
2462  src = uri.encodedUri();
2463  }
2464 
2465  }
2466  else
2467  {
2468  bool handled = false;
2469 
2470  if ( provider == QLatin1String( "gdal" ) )
2471  {
2472  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2473  {
2474  // NETCDF:filename:variable
2475  // filename can be quoted with " as it can contain colons
2476  QRegExp r( "NETCDF:(.+):([^:]+)" );
2477  if ( r.exactMatch( src ) )
2478  {
2479  QString filename = r.cap( 1 );
2480  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2481  filename = filename.mid( 1, filename.length() - 2 );
2482  src = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
2483  handled = true;
2484  }
2485  }
2486  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2487  {
2488  // GPKG:filename:table
2489  QString filename, tablename;
2490  if ( _parseGpkgColons( src, filename, tablename ) )
2491  {
2492  filename = context.pathResolver().readPath( filename );
2493  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2494  handled = true;
2495  }
2496  }
2497  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2498  {
2499  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2500  // filename can be quoted with " as it can contain colons
2501  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
2502  if ( r.exactMatch( src ) )
2503  {
2504  QString filename = r.cap( 2 );
2505  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2506  filename = filename.mid( 1, filename.length() - 2 );
2507  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 3 );
2508  handled = true;
2509  }
2510  }
2511  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2512  {
2513  // HDF5:file_name:subdataset
2514  // filename can be quoted with " as it can contain colons
2515  QRegExp r( "HDF5:(.+):([^:]+)" );
2516  if ( r.exactMatch( src ) )
2517  {
2518  QString filename = r.cap( 1 );
2519  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2520  filename = filename.mid( 1, filename.length() - 2 );
2521  src = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
2522  handled = true;
2523  }
2524  }
2525  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2526  {
2527  // NITF_IM:0:filename
2528  // RADARSAT_2_CALIB:?:filename
2529  QRegExp r( "([^:]+):([^:]+):(.+)" );
2530  if ( r.exactMatch( src ) )
2531  {
2532  src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().readPath( r.cap( 3 ) );
2533  handled = true;
2534  }
2535  }
2536  }
2537 
2538  if ( !handled )
2539  src = context.pathResolver().readPath( src );
2540  }
2541 
2542  return src;
2543 }
2544 
2546 {
2547  if ( !mDataProvider ) return 0;
2548  return mDataProvider->xSize();
2549 }
2550 
2552 {
2553  if ( !mDataProvider ) return 0;
2554  return mDataProvider->ySize();
2555 }
2556 
2558 {
2559  mPipe.setResamplingStage( stage );
2560 }
2561 
2563 //
2564 // Private methods
2565 //
2567 bool QgsRasterLayer::update()
2568 {
2569  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
2570  // Check if data changed
2571  if ( mDataProvider && mDataProvider->dataTimestamp() > mDataProvider->timestamp() )
2572  {
2573  QgsDebugMsgLevel( QStringLiteral( "reload data" ), 4 );
2574  closeDataProvider();
2575  init();
2576  QgsDataProvider::ProviderOptions providerOptions;
2577  QgsDataProvider::ReadFlags flags = QgsDataProvider::ReadFlags();
2579  {
2581  }
2582  setDataProvider( mProviderKey, providerOptions, flags );
2583  emit dataChanged();
2584  }
2585  return isValid();
2586 }
static QString version()
Version string.
Definition: qgis.cpp:285
DataType
Raster data types.
Definition: qgis.h:119
@ CInt32
Complex Int32.
@ Float32
Thirty two bit floating point (float)
@ CFloat64
Complex Float64.
@ Int16
Sixteen bit signed integer (qint16)
@ ARGB32_Premultiplied
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ UnknownDataType
Unknown or unspecified type.
@ ARGB32
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ CFloat32
Complex Float32.
@ CInt16
Complex Int16.
@ UInt32
Thirty two bit unsigned integer (quint32)
static QgsRasterRendererRegistry * rasterRendererRegistry()
Returns the application's raster renderer registry, used for managing raster layer renderers.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
Bilinear Raster Resampler.
Brightness/contrast and gamma correction filter pipe for rasters.
int contrast() const
Returns current contrast level.
int brightness() const
Returns current brightness level.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlphaValue) const override
Generates and new RGB value based on one input value.
@ Interpolated
Interpolates the color between two class breaks linearly.
@ Discrete
Assigns the color of the higher class for every pixel between two class breaks.
void classifyColorRamp(int classes=0, int band=-1, const QgsRectangle &extent=QgsRectangle(), QgsRasterInterface *input=nullptr)
Classify color ramp shader.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom colormap.
void setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
Manipulates raster or point cloud pixel values so that they enhanceContrast or clip into a specified ...
ContrastEnhancementAlgorithm
This enumerator describes the types of contrast enhancement algorithms that can be used.
@ StretchToMinimumMaximum
Linear histogram.
@ NoEnhancement
Default color scaling algorithm, no scaling is applied.
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
Returns a string to serialize ContrastEnhancementAlgorithm.
static ContrastEnhancementAlgorithm contrastEnhancementAlgorithmFromString(const QString &contrastEnhancementString)
Deserialize ContrastEnhancementAlgorithm.
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
This class represents a coordinate reference system (CRS).
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Contains information about the context in which a coordinate transform is executed.
Cubic Raster Resampler.
Abstract base class for spatial data provider implementations.
virtual void setLayerOrder(const QStringList &layers)
Reorder the list of layer names to be rendered by this provider (in order from bottom to top)
@ FlagLoadDefaultStyle
Reset the layer's style to the default for the datasource.
@ FlagTrustDataSource
Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load ti...
@ SkipGetExtent
Skip the extent from provider.
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the coordinate system for the data source.
virtual QString subsetString() const
Returns the subset definition string (typically sql) currently in use by the layer and used by the pr...
virtual QgsLayerMetadata layerMetadata() const
Returns layer metadata collected from the provider's source.
virtual bool isValid() const =0
Returns true if this is a valid layer.
virtual void setDataSourceUri(const QString &uri)
Set the data source specification.
virtual QgsError error() const
Gets current status error.
virtual bool supportsSubsetString() const
Returns true if the provider supports setting of subset strings.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
virtual bool setSubsetString(const QString &subset, bool updateFeatureCount=true)
Set the subset string used to create a subset of features in the layer.
virtual void reloadData()
Reloads the data from the source for providers with data caches to synchronize, changes in the data s...
virtual void setSubLayerVisibility(const QString &name, bool vis)
Set the visibility of the given sublayer name.
virtual void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets data coordinate transform context to transformContext.
Class for storing the component parts of a RDBMS data source URI (e.g.
QByteArray encodedUri() const
Returns the complete encoded URI as a byte array.
int removeParam(const QString &key)
Removes a generic parameter by key.
void setEncodedUri(const QByteArray &uri)
Sets the complete encoded uri.
void setUsername(const QString &username)
Sets the username for the URI.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
void setPassword(const QString &password)
Sets the password for the URI.
Color and saturation filter pipe for rasters.
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:70
QString name
Definition: qgsmaplayer.h:73
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:1651
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:77
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:76
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:1668
QString crsHtmlMetadata() const
Returns a HTML fragment containing the layer's CRS metadata, for use in the htmlMetadata() method.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:75
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:1711
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
void rendererChanged()
Signal emitted when renderer is changed.
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:287
virtual QgsError error() const
Gets current status error.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
void opacityChanged(double opacity)
Emitted when the layer's opacity is changed, where opacity is a value between 0 (transparent) and 1 (...
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
void dataChanged()
Data of layer changed.
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
void setName(const QString &name)
Set the display name of the layer.
virtual void setExtent(const QgsRectangle &rect)
Sets the extent.
bool isValid
Definition: qgsmaplayer.h:78
void appendError(const QgsErrorMessage &error)
Add error message.
Definition: qgsmaplayer.h:1649
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1665
@ FlagReadExtentFromXml
Read extent from xml and skip get extent from provider.
Definition: qgsmaplayer.h:582
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:581
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:580
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:1716
void setDataSource(const QString &dataSource, const QString &baseName, const QString &provider, bool loadDefaultStyleFlag=false)
Updates the data source of the layer.
double minimumScale() const
Returns the minimum map scale (i.e.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void 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.
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
Definition: qgsmaplayer.h:1723
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:39
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Renderer for multiband images with the color components.
const QgsContrastEnhancement * redContrastEnhancement() const
Returns the contrast enhancement to use for the red channel.
const QgsContrastEnhancement * blueContrastEnhancement() const
Returns the contrast enhancement to use for the blue channel.
void setGreenContrastEnhancement(QgsContrastEnhancement *ce)
Sets the contrast enhancement to use for the green channel.
void setBlueContrastEnhancement(QgsContrastEnhancement *ce)
Sets the contrast enhancement to use for the blue channel.
void setRedContrastEnhancement(QgsContrastEnhancement *ce)
Sets the contrast enhancement to use for the red channel.
const QgsContrastEnhancement * greenContrastEnhancement() const
Returns the contrast enhancement to use for the green channel.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
BlendMode
Blending modes enum defining the available composition modes that can be used when rendering a layer.
Definition: qgspainting.h:37
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
A class to represent a 2D point.
Definition: qgspointxy.h:59
static void convertRasterProperties(QDomDocument &doc, QDomNode &parentNode, QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer)
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(Qgis::DataType dataType) SIP_HOLDGIL
Returns the size in bytes for the specified dataType.
static QString printValue(double value)
Print double value with all necessary significant digits.
Base class for raster data providers.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
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.
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:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:469
Contains information about the context of a rendering operation.
Raster renderer pipe for single band gray.
void setContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
const QgsContrastEnhancement * contrastEnhancement() const
Raster renderer pipe for single band pseudocolor.
int band() const
Returns the band used by the renderer.
QgsRasterShader * shader()
Returns the raster shader.
void setShader(QgsRasterShader *shader)
Takes ownership of the shader.
An interface for classes which can visit style entity (e.g.
static void mergeScaleDependencies(double mScaleMinDenom, double mScaleMaxDenom, QVariantMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
static QgsRectangle readRectangle(const QDomElement &element)
Definition: qgsxmlutils.cpp:39
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:46
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:598
#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.