QGIS API Documentation  3.6.0-Noosa (5873452)
qgsrasterlayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrasterlayer.cpp - description
3  -------------------
4 begin : Sat Jun 22 2002
5 copyright : (C) 2003 by Tim Sutton, Steve Halasz and Gary E.Sherman
6 email : tim at linfiniti.com
7  ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 #include "qgsapplication.h"
19 #include "qgscolorrampshader.h"
21 #include "qgscoordinatetransform.h"
22 #include "qgsdatasourceuri.h"
23 #include "qgshuesaturationfilter.h"
25 #include "qgslogger.h"
26 #include "qgsmaplayerlegend.h"
27 #include "qgsmaptopixel.h"
28 #include "qgsmessagelog.h"
30 #include "qgspainting.h"
32 #include "qgspathresolver.h"
34 #include "qgsproviderregistry.h"
35 #include "qgsrasterdataprovider.h"
36 #include "qgsrasterdrawer.h"
37 #include "qgsrasteriterator.h"
38 #include "qgsrasterlayer.h"
39 #include "qgsrasterlayerrenderer.h"
40 #include "qgsrasterprojector.h"
41 #include "qgsrasterrange.h"
44 #include "qgsrastershader.h"
45 #include "qgsreadwritecontext.h"
46 #include "qgsrectangle.h"
47 #include "qgsrendercontext.h"
51 #include "qgssettings.h"
52 #include "qgssymbollayerutils.h"
53 
54 #include <cmath>
55 #include <cstdio>
56 #include <limits>
57 #include <typeinfo>
58 
59 #include <QApplication>
60 #include <QCursor>
61 #include <QDir>
62 #include <QDomElement>
63 #include <QDomNode>
64 #include <QFile>
65 #include <QFileInfo>
66 #include <QFont>
67 #include <QFontMetrics>
68 #include <QFrame>
69 #include <QImage>
70 #include <QLabel>
71 #include <QLibrary>
72 #include <QList>
73 #include <QMatrix>
74 #include <QMessageBox>
75 #include <QPainter>
76 #include <QPixmap>
77 #include <QRegExp>
78 #include <QSlider>
79 #include <QTime>
80 
81 // typedefs for provider plugin functions of interest
82 typedef bool isvalidrasterfilename_t( QString const &fileNameQString, QString &retErrMsg );
83 
84 #define ERR(message) QGS_ERROR_MESSAGE(message,"Raster layer")
85 
86 const double QgsRasterLayer::SAMPLE_SIZE = 250000;
87 
94 
101 
103  : QgsMapLayer( RasterLayer )
104  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
105  , TRSTRING_NOT_SET( tr( "Not Set" ) )
106 
107 {
108  init();
109  mValid = false;
110 }
111 
112 QgsRasterLayer::QgsRasterLayer( const QString &uri,
113  const QString &baseName,
114  const QString &providerKey,
115  const LayerOptions &options )
116  : QgsMapLayer( RasterLayer, baseName, uri )
117  // Constant that signals property not used.
118  , QSTRING_NOT_SET( QStringLiteral( "Not Set" ) )
119  , TRSTRING_NOT_SET( tr( "Not Set" ) )
120 {
121  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
122  setProviderType( providerKey );
123 
124  QgsDataProvider::ProviderOptions providerOptions;
125 
126  setDataSource( uri, baseName, providerKey, providerOptions, options.loadDefaultStyle );
127 
128 } // QgsRasterLayer ctor
129 
131 {
132  emit willBeDeleted();
133 
134  mValid = false;
135  // Note: provider and other interfaces are owned and deleted by pipe
136 }
137 
139 {
140  QgsRasterLayer *layer = new QgsRasterLayer( source(), name(), mProviderKey );
141  QgsMapLayer::clone( layer );
142 
143  // do not clone data provider which is the first element in pipe
144  for ( int i = 1; i < mPipe.size(); i++ )
145  {
146  if ( mPipe.at( i ) )
147  layer->pipe()->set( mPipe.at( i )->clone() );
148  }
149 
150  return layer;
151 }
152 
154 //
155 // Static Methods and members
156 //
158 
159 bool QgsRasterLayer::isValidRasterFileName( const QString &fileNameQString, QString &retErrMsg )
160 {
161  isvalidrasterfilename_t *pValid = reinterpret_cast< isvalidrasterfilename_t * >( cast_to_fptr( QgsProviderRegistry::instance()->function( "gdal", "isValidRasterFileName" ) ) );
162  if ( ! pValid )
163  {
164  QgsDebugMsg( QStringLiteral( "Could not resolve isValidRasterFileName in gdal provider library" ) );
165  return false;
166  }
167 
168  bool myIsValid = pValid( fileNameQString, retErrMsg );
169  return myIsValid;
170 }
171 
172 bool QgsRasterLayer::isValidRasterFileName( QString const &fileNameQString )
173 {
174  QString retErrMsg;
175  return isValidRasterFileName( fileNameQString, retErrMsg );
176 }
177 
178 QDateTime QgsRasterLayer::lastModified( QString const &name )
179 {
180  QgsDebugMsgLevel( "name=" + name, 4 );
181  QDateTime t;
182 
183  QFileInfo fi( name );
184 
185  // Is it file?
186  if ( !fi.exists() )
187  return t;
188 
189  t = fi.lastModified();
190 
191  QgsDebugMsgLevel( "last modified = " + t.toString(), 4 );
192 
193  return t;
194 }
195 
196 void QgsRasterLayer::setDataProvider( const QString &provider )
197 {
199 }
200 
201 // typedef for the QgsDataProvider class factory
202 typedef QgsDataProvider *classFactoryFunction_t( const QString *, const QgsDataProvider::ProviderOptions &options );
203 
205 //
206 // Non Static Public methods
207 //
209 
211 {
212  if ( !mDataProvider ) return 0;
213  return mDataProvider->bandCount();
214 }
215 
216 QString QgsRasterLayer::bandName( int bandNo ) const
217 {
218  return dataProvider()->generateBandName( bandNo );
219 }
220 
221 void QgsRasterLayer::setRendererForDrawingStyle( QgsRaster::DrawingStyle drawingStyle )
222 {
223  setRenderer( QgsApplication::rasterRendererRegistry()->defaultRendererForDrawingStyle( drawingStyle, mDataProvider ) );
224 }
225 
227 {
228  return mDataProvider;
229 }
230 
232 {
233  return mDataProvider;
234 }
235 
237 {
238  if ( mDataProvider )
239  {
240  mDataProvider->reloadData();
241  }
242 }
243 
245 {
246  return new QgsRasterLayerRenderer( this, rendererContext );
247 }
248 
249 
250 void QgsRasterLayer::draw( QPainter *theQPainter,
251  QgsRasterViewPort *rasterViewPort,
252  const QgsMapToPixel *qgsMapToPixel )
253 {
254  QgsDebugMsgLevel( QStringLiteral( " 3 arguments" ), 4 );
255  QTime time;
256  time.start();
257  //
258  //
259  // The goal here is to make as many decisions as possible early on (outside of the rendering loop)
260  // so that we can maximise performance of the rendering process. So now we check which drawing
261  // procedure to use :
262  //
263 
264  QgsRasterProjector *projector = mPipe.projector();
265 
266  // TODO add a method to interface to get provider and get provider
267  // params in QgsRasterProjector
268  if ( projector )
269  {
270  projector->setCrs( rasterViewPort->mSrcCRS, rasterViewPort->mDestCRS, rasterViewPort->mSrcDatumTransform, rasterViewPort->mDestDatumTransform );
271  }
272 
273  // Drawer to pipe?
274  QgsRasterIterator iterator( mPipe.last() );
275  QgsRasterDrawer drawer( &iterator );
276  drawer.draw( theQPainter, rasterViewPort, qgsMapToPixel );
277 
278  QgsDebugMsgLevel( QStringLiteral( "total raster draw time (ms): %1" ).arg( time.elapsed(), 5 ), 4 );
279 } //end of draw method
280 
282 {
283  QList< QPair< QString, QColor > > symbolList;
285  if ( renderer )
286  {
287  renderer->legendSymbologyItems( symbolList );
288  }
289  return symbolList;
290 }
291 
293 {
294  QgsLayerMetadataFormatter htmlFormatter( metadata() );
295  QString myMetadata = QStringLiteral( "<html>\n<body>\n" );
296 
297  // Begin Provider section
298  myMetadata += QStringLiteral( "<h1>" ) + tr( "Information from provider" ) + QStringLiteral( "</h1>\n<hr>\n" );
299  myMetadata += QLatin1String( "<table class=\"list-view\">\n" );
300 
301  // name
302  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
303 
304  // local path
305  QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( mProviderKey, publicSource() );
306  QString path;
307  if ( uriComponents.contains( QStringLiteral( "path" ) ) )
308  {
309  path = uriComponents[QStringLiteral( "path" )].toString();
310  if ( QFile::exists( path ) )
311  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" );
312  }
313  if ( uriComponents.contains( QStringLiteral( "url" ) ) )
314  {
315  const QString url = uriComponents[QStringLiteral( "url" )].toString();
316  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" );
317  }
318 
319  // data source
320  if ( publicSource() != path )
321  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>%1" ).arg( publicSource() ) + QStringLiteral( "</td></tr>\n" );
322 
323  // data source
324  if ( publicSource() != path )
325  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>%1" ).arg( publicSource() ) + QStringLiteral( "</td></tr>\n" );
326 
327  // EPSG
328  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "CRS" ) + QStringLiteral( "</td><td>" );
329  if ( crs().isValid() )
330  {
331  myMetadata += crs().authid() + QStringLiteral( " - " );
332  myMetadata += crs().description() + QStringLiteral( " - " );
333  if ( crs().isGeographic() )
334  myMetadata += tr( "Geographic" );
335  else
336  myMetadata += tr( "Projected" );
337  }
338  myMetadata += QLatin1String( "</td></tr>\n" );
339 
340  // Extent
341  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Extent" ) + QStringLiteral( "</td><td>" ) + extent().toString() + QStringLiteral( "</td></tr>\n" );
342 
343  // unit
344  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Unit" ) + QStringLiteral( "</td><td>" ) + QgsUnitTypes::toString( crs().mapUnits() ) + QStringLiteral( "</td></tr>\n" );
345 
346  // Raster Width
347  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Width" ) + QStringLiteral( "</td><td>" );
348  if ( dataProvider()->capabilities() & QgsRasterDataProvider::Size )
349  myMetadata += QString::number( width() );
350  else
351  myMetadata += tr( "n/a" );
352  myMetadata += QLatin1String( "</td></tr>\n" );
353 
354  // Raster height
355  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Height" ) + QStringLiteral( "</td><td>" );
356  if ( dataProvider()->capabilities() & QgsRasterDataProvider::Size )
357  myMetadata += QString::number( height() );
358  else
359  myMetadata += tr( "n/a" );
360  myMetadata += QLatin1String( "</td></tr>\n" );
361 
362  // Data type
363  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Data type" ) + QStringLiteral( "</td><td>" );
364  // Just use the first band
365  switch ( mDataProvider->sourceDataType( 1 ) )
366  {
367  case Qgis::Byte:
368  myMetadata += tr( "Byte - Eight bit unsigned integer" );
369  break;
370  case Qgis::UInt16:
371  myMetadata += tr( "UInt16 - Sixteen bit unsigned integer " );
372  break;
373  case Qgis::Int16:
374  myMetadata += tr( "Int16 - Sixteen bit signed integer " );
375  break;
376  case Qgis::UInt32:
377  myMetadata += tr( "UInt32 - Thirty two bit unsigned integer " );
378  break;
379  case Qgis::Int32:
380  myMetadata += tr( "Int32 - Thirty two bit signed integer " );
381  break;
382  case Qgis::Float32:
383  myMetadata += tr( "Float32 - Thirty two bit floating point " );
384  break;
385  case Qgis::Float64:
386  myMetadata += tr( "Float64 - Sixty four bit floating point " );
387  break;
388  case Qgis::CInt16:
389  myMetadata += tr( "CInt16 - Complex Int16 " );
390  break;
391  case Qgis::CInt32:
392  myMetadata += tr( "CInt32 - Complex Int32 " );
393  break;
394  case Qgis::CFloat32:
395  myMetadata += tr( "CFloat32 - Complex Float32 " );
396  break;
397  case Qgis::CFloat64:
398  myMetadata += tr( "CFloat64 - Complex Float64 " );
399  break;
400  default:
401  myMetadata += tr( "Could not determine raster data type." );
402  }
403  myMetadata += QLatin1String( "</td></tr>\n" );
404 
405  // Insert provider-specific (e.g. WMS-specific) metadata
406  myMetadata += mDataProvider->htmlMetadata();
407 
408  // End Provider section
409  myMetadata += QLatin1String( "</table>\n<br><br>" );
410 
411  // Identification section
412  myMetadata += QStringLiteral( "<h1>" ) + tr( "Identification" ) + QStringLiteral( "</h1>\n<hr>\n" );
413  myMetadata += htmlFormatter.identificationSectionHtml();
414  myMetadata += QLatin1String( "<br><br>\n" );
415 
416  // extent section
417  myMetadata += QStringLiteral( "<h1>" ) + tr( "Extent" ) + QStringLiteral( "</h1>\n<hr>\n" );
418  myMetadata += htmlFormatter.extentSectionHtml( );
419  myMetadata += QLatin1String( "<br><br>\n" );
420 
421  // Start the Access section
422  myMetadata += QStringLiteral( "<h1>" ) + tr( "Access" ) + QStringLiteral( "</h1>\n<hr>\n" );
423  myMetadata += htmlFormatter.accessSectionHtml( );
424  myMetadata += QLatin1String( "<br><br>\n" );
425 
426  // Bands section
427  myMetadata += QStringLiteral( "</table>\n<br><br><h1>" ) + tr( "Bands" ) + QStringLiteral( "</h1>\n<hr>\n<table class=\"list-view\">\n" );
428 
429  // Band count
430  myMetadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Band count" ) + QStringLiteral( "</td><td>" ) + QString::number( bandCount() ) + QStringLiteral( "</td></tr>\n" );
431 
432  // Band table
433  myMetadata += QLatin1String( "</table>\n<br><table width=\"100%\" class=\"tabular-view\">\n" );
434  myMetadata += QLatin1String( "<tr><th>" ) + tr( "Number" ) + QLatin1String( "</th><th>" ) + tr( "Band" ) + QLatin1String( "</th><th>" ) + tr( "No-Data" ) + QLatin1String( "</th><th>" ) + tr( "Min" ) + QLatin1String( "</th><th>" ) + tr( "Max" ) + QLatin1String( "</th></tr>\n" );
435 
436  QgsRasterDataProvider *provider = const_cast< QgsRasterDataProvider * >( mDataProvider );
437  for ( int i = 1; i <= bandCount(); i++ )
438  {
439  QString rowClass;
440  if ( i % 2 )
441  rowClass = QStringLiteral( "class=\"odd-row\"" );
442  myMetadata += QLatin1String( "<tr " ) + rowClass + QLatin1String( "><td>" ) + QString::number( i ) + QLatin1String( "</td><td>" ) + bandName( i ) + QLatin1String( "</td><td>" );
443 
444  if ( dataProvider()->sourceHasNoDataValue( i ) )
445  myMetadata += QString::number( dataProvider()->sourceNoDataValue( i ) );
446  else
447  myMetadata += tr( "n/a" );
448  myMetadata += QLatin1String( "</td>" );
449 
450  if ( provider->hasStatistics( i ) )
451  {
452  QgsRasterBandStats myRasterBandStats = provider->bandStatistics( i );
453  myMetadata += QLatin1String( "<td>" ) + QString::number( myRasterBandStats.minimumValue, 'f', 10 ) + QLatin1String( "</td>" );
454  myMetadata += QLatin1String( "<td>" ) + QString::number( myRasterBandStats.maximumValue, 'f', 10 ) + QLatin1String( "</td>" );
455  }
456  else
457  {
458  myMetadata += QLatin1String( "<td>" ) + tr( "n/a" ) + QLatin1String( "</td><td>" ) + tr( "n/a" ) + QLatin1String( "</td>" );
459  }
460 
461  myMetadata += QLatin1String( "</tr>\n" );
462  }
463 
464  //close previous bands table
465  myMetadata += QLatin1String( "</table>\n<br><br>" );
466 
467  // Start the contacts section
468  myMetadata += QStringLiteral( "<h1>" ) + tr( "Contacts" ) + QStringLiteral( "</h1>\n<hr>\n" );
469  myMetadata += htmlFormatter.contactsSectionHtml( );
470  myMetadata += QLatin1String( "<br><br>\n" );
471 
472  // Start the links section
473  myMetadata += QStringLiteral( "<h1>" ) + tr( "References" ) + QStringLiteral( "</h1>\n<hr>\n" );
474  myMetadata += htmlFormatter.linksSectionHtml( );
475  myMetadata += QLatin1String( "<br><br>\n" );
476 
477  // Start the history section
478  myMetadata += QStringLiteral( "<h1>" ) + tr( "History" ) + QStringLiteral( "</h1>\n<hr>\n" );
479  myMetadata += htmlFormatter.historySectionHtml( );
480  myMetadata += QLatin1String( "<br><br>\n" );
481 
482  myMetadata += QStringLiteral( "\n</body>\n</html>\n" );
483  return myMetadata;
484 }
485 
486 QPixmap QgsRasterLayer::paletteAsPixmap( int bandNumber )
487 {
488  //TODO: This function should take dimensions
489  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
490 
491  // Only do this for the GDAL provider?
492  // Maybe WMS can do this differently using QImage::numColors and QImage::color()
493  if ( mDataProvider->colorInterpretation( bandNumber ) == QgsRaster::PaletteIndex )
494  {
495  QgsDebugMsgLevel( QStringLiteral( "....found paletted image" ), 4 );
496  QgsColorRampShader myShader;
497  QList<QgsColorRampShader::ColorRampItem> myColorRampItemList = mDataProvider->colorTable( bandNumber );
498  if ( !myColorRampItemList.isEmpty() )
499  {
500  QgsDebugMsgLevel( QStringLiteral( "....got color ramp item list" ), 4 );
501  myShader.setColorRampItemList( myColorRampItemList );
503  // Draw image
504  int mySize = 100;
505  QPixmap myPalettePixmap( mySize, mySize );
506  QPainter myQPainter( &myPalettePixmap );
507 
508  QImage myQImage = QImage( mySize, mySize, QImage::Format_RGB32 );
509  myQImage.fill( 0 );
510  myPalettePixmap.fill();
511 
512  double myStep = ( static_cast< double >( myColorRampItemList.size() ) - 1 ) / static_cast< double >( mySize * mySize );
513  double myValue = 0.0;
514  for ( int myRow = 0; myRow < mySize; myRow++ )
515  {
516  QRgb *myLineBuffer = reinterpret_cast< QRgb * >( myQImage.scanLine( myRow ) );
517  for ( int myCol = 0; myCol < mySize; myCol++ )
518  {
519  myValue = myStep * static_cast< double >( myCol + myRow * mySize );
520  int c1, c2, c3, c4;
521  myShader.shade( myValue, &c1, &c2, &c3, &c4 );
522  myLineBuffer[ myCol ] = qRgba( c1, c2, c3, c4 );
523  }
524  }
525 
526  myQPainter.drawImage( 0, 0, myQImage );
527  return myPalettePixmap;
528  }
529  QPixmap myNullPixmap;
530  return myNullPixmap;
531  }
532  else
533  {
534  //invalid layer was requested
535  QPixmap myNullPixmap;
536  return myNullPixmap;
537  }
538 }
539 
541 {
542  return mProviderKey;
543 }
544 
546 {
547 // We return one raster pixel per map unit pixel
548 // One raster pixel can have several raster units...
549 
550 // We can only use one of the mGeoTransform[], so go with the
551 // horisontal one.
552 
553  if ( mDataProvider->capabilities() & QgsRasterDataProvider::Size && !qgsDoubleNear( mDataProvider->xSize(), 0.0 ) )
554  {
555  return mDataProvider->extent().width() / mDataProvider->xSize();
556  }
557  return 1;
558 }
559 
561 {
562  if ( mDataProvider->capabilities() & QgsRasterDataProvider::Size && !qgsDoubleNear( mDataProvider->ySize(), 0.0 ) )
563  {
564  return mDataProvider->extent().height() / mDataProvider->ySize();
565  }
566  return 1;
567 }
568 
569 void QgsRasterLayer::init()
570 {
571  mRasterType = QgsRasterLayer::GrayOrUndefined;
572 
574 
575  setRendererForDrawingStyle( QgsRaster::UndefinedDrawingStyle );
576 
577  //Initialize the last view port structure, should really be a class
578  mLastViewPort.mWidth = 0;
579  mLastViewPort.mHeight = 0;
580 }
581 
582 void QgsRasterLayer::setDataProvider( QString const &provider, const QgsDataProvider::ProviderOptions &options )
583 {
584  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
585  mValid = false; // assume the layer is invalid until we determine otherwise
586 
587  mPipe.remove( mDataProvider ); // deletes if exists
588  mDataProvider = nullptr;
589 
590  // XXX should I check for and possibly delete any pre-existing providers?
591  // XXX How often will that scenario occur?
592 
593  mProviderKey = provider;
594  // set the layer name (uppercase first character)
595  if ( ! mLayerName.isEmpty() ) // XXX shouldn't this happen in parent?
596  {
597  setName( mLayerName );
598  }
599 
600  //mBandCount = 0;
601 
602  mDataProvider = dynamic_cast< QgsRasterDataProvider * >( QgsProviderRegistry::instance()->createProvider( mProviderKey, mDataSource, options ) );
603  if ( !mDataProvider )
604  {
605  //QgsMessageLog::logMessage( tr( "Cannot instantiate the data provider" ), tr( "Raster" ) );
606  appendError( ERR( tr( "Cannot instantiate the '%1' data provider" ).arg( mProviderKey ) ) );
607  return;
608  }
609  QgsDebugMsgLevel( QStringLiteral( "Data provider created" ), 4 );
610  mDataProvider->setParent( this );
611 
612  // Set data provider into pipe even if not valid so that it is deleted with pipe (with layer)
613  mPipe.set( mDataProvider );
614  if ( !mDataProvider->isValid() )
615  {
616  setError( mDataProvider->error() );
617  appendError( ERR( tr( "Provider is not valid (provider: %1, URI: %2" ).arg( mProviderKey, mDataSource ) ) );
618  return;
619  }
620 
622  {
623  setMetadata( mDataProvider->layerMetadata() );
624  QgsDebugMsgLevel( QStringLiteral( "Set Data provider QgsLayerMetadata identifier[%1]" ).arg( metadata().identifier() ), 4 );
625  }
626 
627  if ( provider == QLatin1String( "gdal" ) )
628  {
629  // make sure that the /vsigzip or /vsizip is added to uri, if applicable
630  mDataSource = mDataProvider->dataSourceUri();
631  }
632 
633  // get the extent
634  QgsRectangle mbr = mDataProvider->extent();
635 
636  // show the extent
637  QgsDebugMsgLevel( "Extent of layer: " + mbr.toString(), 4 );
638  // store the extent
639  setExtent( mbr );
640 
641  // upper case the first letter of the layer name
642  QgsDebugMsgLevel( "mLayerName: " + name(), 4 );
643 
644  // set up the raster drawing style
645  // Do not set any 'sensible' style here, the style is set later
646 
647  // Setup source CRS
648  setCrs( QgsCoordinateReferenceSystem( mDataProvider->crs() ) );
649 
650  QgsDebugMsgLevel( "using wkt:\n" + crs().toWkt(), 4 );
651 
652  //defaults - Needs to be set after the Contrast list has been build
653  //Try to read the default contrast enhancement from the config file
654 
655  //decide what type of layer this is...
656  //TODO Change this to look at the color interp and palette interp to decide which type of layer it is
657  QgsDebugMsgLevel( "bandCount = " + QString::number( mDataProvider->bandCount() ), 4 );
658  QgsDebugMsgLevel( "dataType = " + QString::number( mDataProvider->dataType( 1 ) ), 4 );
659  if ( ( mDataProvider->bandCount() > 1 ) )
660  {
661  // handle singleband gray with alpha
662  if ( mDataProvider->bandCount() == 2
663  && ( ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::GrayIndex
664  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::AlphaBand )
665  || ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::AlphaBand
666  && mDataProvider->colorInterpretation( 2 ) == QgsRaster::GrayIndex ) ) )
667  {
668  mRasterType = GrayOrUndefined;
669  }
670  else
671  {
672  mRasterType = Multiband;
673  }
674  }
675  else if ( mDataProvider->dataType( 1 ) == Qgis::ARGB32
676  || mDataProvider->dataType( 1 ) == Qgis::ARGB32_Premultiplied )
677  {
678  mRasterType = ColorLayer;
679  }
680  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
681  {
682  mRasterType = Palette;
683  }
684  else if ( mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
685  {
686  mRasterType = Palette;
687  }
688  else
689  {
690  mRasterType = GrayOrUndefined;
691  }
692 
693  QgsDebugMsgLevel( "mRasterType = " + QString::number( mRasterType ), 4 );
694  if ( mRasterType == ColorLayer )
695  {
696  QgsDebugMsgLevel( "Setting drawing style to SingleBandColorDataStyle " + QString::number( QgsRaster::SingleBandColorDataStyle ), 4 );
697  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
698  }
699  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::PaletteIndex )
700  {
701  setRendererForDrawingStyle( QgsRaster::PalettedColor ); //sensible default
702  }
703  else if ( mRasterType == Palette && mDataProvider->colorInterpretation( 1 ) == QgsRaster::ContinuousPalette )
704  {
705  setRendererForDrawingStyle( QgsRaster::SingleBandPseudoColor );
706  // Load color table
707  QList<QgsColorRampShader::ColorRampItem> colorTable = mDataProvider->colorTable( 1 );
709  if ( r )
710  {
711  // TODO: this should go somewhere else
712  QgsRasterShader *shader = new QgsRasterShader();
713  QgsColorRampShader *colorRampShader = new QgsColorRampShader();
715  colorRampShader->setColorRampItemList( colorTable );
716  shader->setRasterShaderFunction( colorRampShader );
717  r->setShader( shader );
718  }
719  }
720  else if ( mRasterType == Multiband )
721  {
722  setRendererForDrawingStyle( QgsRaster::MultiBandColor ); //sensible default
723  }
724  else //GrayOrUndefined
725  {
726  setRendererForDrawingStyle( QgsRaster::SingleBandGray ); //sensible default
727  }
728 
729  // Auto set alpha band
730  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
731  {
732  if ( mDataProvider->colorInterpretation( bandNo ) == QgsRaster::AlphaBand )
733  {
734  if ( mPipe.renderer() )
735  {
736  mPipe.renderer()->setAlphaBand( bandNo );
737  }
738  break;
739  }
740  }
741 
742  // brightness filter
744  mPipe.set( brightnessFilter );
745 
746  // hue/saturation filter
748  mPipe.set( hueSaturationFilter );
749 
750  //resampler (must be after renderer)
752  mPipe.set( resampleFilter );
753 
754  // projector (may be anywhere in pipe)
755  QgsRasterProjector *projector = new QgsRasterProjector;
756  mPipe.set( projector );
757 
758  // Set default identify format - use the richest format available
759  int capabilities = mDataProvider->capabilities();
761  if ( capabilities & QgsRasterInterface::IdentifyHtml )
762  {
763  // HTML is usually richest
764  identifyFormat = QgsRaster::IdentifyFormatHtml;
765  }
766  else if ( capabilities & QgsRasterInterface::IdentifyFeature )
767  {
768  identifyFormat = QgsRaster::IdentifyFormatFeature;
769  }
770  else if ( capabilities & QgsRasterInterface::IdentifyText )
771  {
772  identifyFormat = QgsRaster::IdentifyFormatText;
773  }
774  else if ( capabilities & QgsRasterInterface::IdentifyValue )
775  {
776  identifyFormat = QgsRaster::IdentifyFormatValue;
777  }
778  setCustomProperty( QStringLiteral( "identify/format" ), QgsRasterDataProvider::identifyFormatName( identifyFormat ) );
779 
780  // Store timestamp
781  // TODO move to provider
782  mLastModified = lastModified( mDataSource );
783 
784  // Do a passthrough for the status bar text
785  connect( mDataProvider, &QgsRasterDataProvider::statusChanged, this, &QgsRasterLayer::statusChanged );
786 
787  //mark the layer as valid
788  mValid = true;
789 
790  QgsDebugMsgLevel( QStringLiteral( "exiting." ), 4 );
791 }
792 
793 void QgsRasterLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag )
794 {
795 
796  bool wasValid( isValid() );
797 
798  QDomImplementation domImplementation;
799  QDomDocumentType documentType;
800  QDomDocument doc;
801  QString errorMsg;
802  QDomElement rootNode;
803 
804  // Store the original style
805  if ( wasValid && ! loadDefaultStyleFlag )
806  {
807  documentType = domImplementation.createDocumentType(
808  QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
809  doc = QDomDocument( documentType );
810  rootNode = doc.createElement( QStringLiteral( "qgis" ) );
811  rootNode.setAttribute( QStringLiteral( "version" ), Qgis::QGIS_VERSION );
812  doc.appendChild( rootNode );
813  QgsReadWriteContext writeContext;
814  if ( ! writeSymbology( rootNode, doc, errorMsg, writeContext ) )
815  {
816  QgsDebugMsg( QStringLiteral( "Could not store symbology for layer %1: %2" )
817  .arg( name() )
818  .arg( errorMsg ) );
819  }
820  }
821 
822  if ( mDataProvider )
823  closeDataProvider();
824 
825  init();
826 
827  for ( int i = mPipe.size() - 1; i >= 0; --i )
828  {
829  mPipe.remove( i );
830  }
831 
832  mDataSource = dataSource;
833  mLayerName = baseName;
834 
835  setDataProvider( provider, options );
836 
837  if ( mValid )
838  {
839  // load default style
840  bool defaultLoadedFlag = false;
841  if ( loadDefaultStyleFlag )
842  {
843  loadDefaultStyle( defaultLoadedFlag );
844  }
845  else if ( wasValid && errorMsg.isEmpty() ) // Restore the style
846  {
847  QgsReadWriteContext readContext;
848  if ( ! readSymbology( rootNode, errorMsg, readContext ) )
849  {
850  QgsDebugMsg( QStringLiteral( "Could not restore symbology for layer %1: %2" )
851  .arg( name() )
852  .arg( errorMsg ) );
853 
854  }
855  }
856 
857  if ( !defaultLoadedFlag )
858  {
860  }
861  emit statusChanged( tr( "QgsRasterLayer created" ) );
862  }
863  emit dataSourceChanged();
864  emit dataChanged();
865 }
866 
867 void QgsRasterLayer::closeDataProvider()
868 {
869  mValid = false;
870  mPipe.remove( mDataProvider );
871  mDataProvider = nullptr;
872 }
873 
874 void QgsRasterLayer::computeMinMax( int band,
875  const QgsRasterMinMaxOrigin &mmo,
877  const QgsRectangle &extent,
878  int sampleSize,
879  double &min, double &max )
880 {
881 
882  min = std::numeric_limits<double>::quiet_NaN();
883  max = std::numeric_limits<double>::quiet_NaN();
884 
885  if ( limits == QgsRasterMinMaxOrigin::MinMax )
886  {
887  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Min | QgsRasterBandStats::Max, extent, sampleSize );
888  min = myRasterBandStats.minimumValue;
889  max = myRasterBandStats.maximumValue;
890  }
891  else if ( limits == QgsRasterMinMaxOrigin::StdDev )
892  {
893  QgsRasterBandStats myRasterBandStats = mDataProvider->bandStatistics( band, QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev, extent, sampleSize );
894  min = myRasterBandStats.mean - ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
895  max = myRasterBandStats.mean + ( mmo.stdDevFactor() * myRasterBandStats.stdDev );
896  }
897  else if ( limits == QgsRasterMinMaxOrigin::CumulativeCut )
898  {
899  const double myLower = mmo.cumulativeCutLower();
900  const double myUpper = mmo.cumulativeCutUpper();
901  QgsDebugMsgLevel( QStringLiteral( "myLower = %1 myUpper = %2" ).arg( myLower ).arg( myUpper ), 4 );
902  mDataProvider->cumulativeCut( band, myLower, myUpper, min, max, extent, sampleSize );
903  }
904  QgsDebugMsgLevel( QStringLiteral( "band = %1 min = %2 max = %3" ).arg( band ).arg( min ).arg( max ), 4 );
905 
906 }
907 
908 
910 {
911  setContrastEnhancement( algorithm,
912  limits,
913  extent,
914  sampleSize,
915  generateLookupTableFlag,
916  mPipe.renderer() );
917 }
918 
921  const QgsRectangle &extent,
922  int sampleSize,
923  bool generateLookupTableFlag,
924  QgsRasterRenderer *rasterRenderer )
925 {
926  QgsDebugMsgLevel( QStringLiteral( "theAlgorithm = %1 limits = %2 extent.isEmpty() = %3" ).arg( algorithm ).arg( limits ).arg( extent.isEmpty() ), 4 );
927  if ( !rasterRenderer || !mDataProvider )
928  {
929  return;
930  }
931 
932  QList<int> myBands;
933  QList<QgsContrastEnhancement *> myEnhancements;
934  QgsRasterMinMaxOrigin myMinMaxOrigin;
935  QgsRasterRenderer *myRasterRenderer = nullptr;
936  QgsSingleBandGrayRenderer *myGrayRenderer = nullptr;
937  QgsSingleBandPseudoColorRenderer *myPseudoColorRenderer = nullptr;
938  QgsMultiBandColorRenderer *myMultiBandRenderer = nullptr;
939  QString rendererType = rasterRenderer->type();
940  if ( rendererType == QLatin1String( "singlebandgray" ) )
941  {
942  myGrayRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer );
943  if ( !myGrayRenderer )
944  {
945  return;
946  }
947  myBands << myGrayRenderer->grayBand();
948  myRasterRenderer = myGrayRenderer;
949  myMinMaxOrigin = myGrayRenderer->minMaxOrigin();
950  }
951  else if ( rendererType == QLatin1String( "multibandcolor" ) )
952  {
953  myMultiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer );
954  if ( !myMultiBandRenderer )
955  {
956  return;
957  }
958  myBands << myMultiBandRenderer->redBand() << myMultiBandRenderer->greenBand() << myMultiBandRenderer->blueBand();
959  myRasterRenderer = myMultiBandRenderer;
960  myMinMaxOrigin = myMultiBandRenderer->minMaxOrigin();
961  }
962  else if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
963  {
964  myPseudoColorRenderer = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer );
965  if ( !myPseudoColorRenderer )
966  {
967  return;
968  }
969  myBands << myPseudoColorRenderer->band();
970  myRasterRenderer = myPseudoColorRenderer;
971  myMinMaxOrigin = myPseudoColorRenderer->minMaxOrigin();
972  }
973  else
974  {
975  return;
976  }
977 
978  Q_FOREACH ( int myBand, myBands )
979  {
980  if ( myBand != -1 )
981  {
982  Qgis::DataType myType = static_cast< Qgis::DataType >( mDataProvider->dataType( myBand ) );
983  std::unique_ptr<QgsContrastEnhancement> myEnhancement( new QgsContrastEnhancement( static_cast< Qgis::DataType >( myType ) ) );
984  myEnhancement->setContrastEnhancementAlgorithm( algorithm, generateLookupTableFlag );
985 
986  double min;
987  double max;
988  computeMinMax( myBand, myMinMaxOrigin, limits, extent, sampleSize, min, max );
989 
990  if ( rendererType == QLatin1String( "singlebandpseudocolor" ) )
991  {
992  myPseudoColorRenderer->setClassificationMin( min );
993  myPseudoColorRenderer->setClassificationMax( max );
994  if ( myPseudoColorRenderer->shader() )
995  {
996  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( myPseudoColorRenderer->shader()->rasterShaderFunction() );
997  if ( colorRampShader )
998  {
999  colorRampShader->classifyColorRamp( myPseudoColorRenderer->band(), extent, myPseudoColorRenderer->input() );
1000  }
1001  }
1002  }
1003  else
1004  {
1005  myEnhancement->setMinimumValue( min );
1006  myEnhancement->setMaximumValue( max );
1007  myEnhancements.append( myEnhancement.release() );
1008  }
1009  }
1010  else
1011  {
1012  myEnhancements.append( nullptr );
1013  }
1014  }
1015 
1016  if ( rendererType == QLatin1String( "singlebandgray" ) )
1017  {
1018  if ( myEnhancements.first() ) myGrayRenderer->setContrastEnhancement( myEnhancements.takeFirst() );
1019  }
1020  else if ( rendererType == QLatin1String( "multibandcolor" ) )
1021  {
1022  if ( myEnhancements.first() ) myMultiBandRenderer->setRedContrastEnhancement( myEnhancements.takeFirst() );
1023  if ( myEnhancements.first() ) myMultiBandRenderer->setGreenContrastEnhancement( myEnhancements.takeFirst() );
1024  if ( myEnhancements.first() ) myMultiBandRenderer->setBlueContrastEnhancement( myEnhancements.takeFirst() );
1025  }
1026 
1027  //delete all remaining unused enhancements
1028  qDeleteAll( myEnhancements );
1029 
1030  myMinMaxOrigin.setLimits( limits );
1031  if ( extent != QgsRectangle() &&
1032  myMinMaxOrigin.extent() == QgsRasterMinMaxOrigin::WholeRaster )
1033  {
1035  }
1036  if ( myRasterRenderer )
1037  {
1038  myRasterRenderer->setMinMaxOrigin( myMinMaxOrigin );
1039  }
1040 
1041  if ( rasterRenderer == renderer() )
1042  {
1043  emit repaintRequested();
1044  emit styleChanged();
1045  emit rendererChanged();
1046  }
1047 }
1048 
1050 {
1051  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1052  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1053  const QgsContrastEnhancement *ce = nullptr;
1054  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) ) )
1055  {
1056  ce = singleBandRenderer->contrastEnhancement();
1057  }
1058  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) ) )
1059  {
1060  ce = multiBandRenderer->redContrastEnhancement();
1061  }
1062 
1063  if ( ce )
1064  {
1069  extent,
1070  SAMPLE_SIZE,
1071  true,
1072  renderer() );
1073  }
1074  else
1075  {
1078  if ( defaultContrastEnhancementSettings( myAlgorithm, myLimits ) )
1079  {
1081  myLimits,
1082  extent,
1083  SAMPLE_SIZE,
1084  true,
1085  renderer() );
1086  }
1087  }
1088 }
1089 
1091  const QgsRectangle &extent )
1092 {
1093  if ( !( mDataProvider &&
1094  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded != extent &&
1095  rasterRenderer->minMaxOrigin().limits() != QgsRasterMinMaxOrigin::None &&
1096  rasterRenderer->minMaxOrigin().extent() == QgsRasterMinMaxOrigin::UpdatedCanvas ) )
1097  return;
1098 
1099  QgsSingleBandGrayRenderer *singleBandRenderer = nullptr;
1100  QgsMultiBandColorRenderer *multiBandRenderer = nullptr;
1101  QgsSingleBandPseudoColorRenderer *sbpcr = nullptr;
1102  const QgsContrastEnhancement *ce = nullptr;
1103  if ( ( singleBandRenderer = dynamic_cast<QgsSingleBandGrayRenderer *>( rasterRenderer ) ) )
1104  {
1105  ce = singleBandRenderer->contrastEnhancement();
1106  }
1107  else if ( ( multiBandRenderer = dynamic_cast<QgsMultiBandColorRenderer *>( rasterRenderer ) ) )
1108  {
1109  ce = multiBandRenderer->redContrastEnhancement();
1110  }
1111  else if ( ( sbpcr = dynamic_cast<QgsSingleBandPseudoColorRenderer *>( rasterRenderer ) ) )
1112  {
1113  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1114  double min;
1115  double max;
1116  computeMinMax( sbpcr->band(),
1117  rasterRenderer->minMaxOrigin(),
1118  rasterRenderer->minMaxOrigin().limits(), extent,
1119  SAMPLE_SIZE, min, max );
1120  sbpcr->setClassificationMin( min );
1121  sbpcr->setClassificationMax( max );
1122 
1123  if ( sbpcr->shader() )
1124  {
1125  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( sbpcr->shader()->rasterShaderFunction() );
1126  if ( colorRampShader )
1127  {
1128  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1129  }
1130  }
1131 
1133  r->setClassificationMin( min );
1134  r->setClassificationMax( max );
1135 
1136  if ( r->shader() )
1137  {
1138  QgsColorRampShader *colorRampShader = dynamic_cast<QgsColorRampShader *>( r->shader()->rasterShaderFunction() );
1139  if ( colorRampShader )
1140  {
1141  colorRampShader->classifyColorRamp( sbpcr->band(), extent, rasterRenderer->input() );
1142  }
1143  }
1144 
1145  emit repaintRequested();
1146  emit styleChanged();
1147  emit rendererChanged();
1148  return;
1149  }
1150 
1151  if ( ce &&
1153  {
1154  mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent;
1155 
1157  rasterRenderer->minMaxOrigin().limits(),
1158  extent,
1159  SAMPLE_SIZE,
1160  true,
1161  rasterRenderer );
1162 
1163  // Update main renderer so that the legends get updated
1164  if ( singleBandRenderer )
1165  static_cast<QgsSingleBandGrayRenderer *>( renderer() )->setContrastEnhancement( new QgsContrastEnhancement( * singleBandRenderer->contrastEnhancement() ) );
1166  else if ( multiBandRenderer )
1167  {
1168  if ( multiBandRenderer->redContrastEnhancement() )
1169  {
1170  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setRedContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->redContrastEnhancement() ) );
1171  }
1172  if ( multiBandRenderer->greenContrastEnhancement() )
1173  {
1174  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setGreenContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->greenContrastEnhancement() ) );
1175  }
1176  if ( multiBandRenderer->blueContrastEnhancement() )
1177  {
1178  static_cast<QgsMultiBandColorRenderer *>( renderer() )->setBlueContrastEnhancement( new QgsContrastEnhancement( *multiBandRenderer->blueContrastEnhancement() ) );
1179  }
1180  }
1181 
1182  emit styleChanged();
1183  emit rendererChanged();
1184  }
1185 }
1186 
1189  QgsRasterMinMaxOrigin::Limits &myLimits ) const
1190 {
1191  QgsSettings mySettings;
1192 
1193  QString key;
1194  QString defaultAlg;
1195  QString defaultLimits;
1196 
1197  // TODO: we should not test renderer class here, move it somehow to renderers
1198  if ( dynamic_cast<QgsSingleBandGrayRenderer *>( renderer() ) )
1199  {
1200  key = QStringLiteral( "singleBand" );
1203  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1205  }
1206  else if ( dynamic_cast<QgsMultiBandColorRenderer *>( renderer() ) )
1207  {
1208  if ( QgsRasterBlock::typeSize( dataProvider()->sourceDataType( 1 ) ) == 1 )
1209  {
1210  key = QStringLiteral( "multiBandSingleByte" );
1213  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1215  }
1216  else
1217  {
1218  key = QStringLiteral( "multiBandMultiByte" );
1221  defaultLimits = QgsRasterMinMaxOrigin::limitsString(
1223  }
1224  }
1225 
1226  if ( key.isEmpty() )
1227  {
1228  QgsDebugMsg( QStringLiteral( "No default contrast enhancement for this drawing style" ) );
1230  myLimits = QgsRasterMinMaxOrigin::limitsFromString( QString() );
1231  return false;
1232  }
1233  QgsDebugMsgLevel( "key = " + key, 4 );
1234 
1235  QString myAlgorithmString = mySettings.value( "/Raster/defaultContrastEnhancementAlgorithm/" + key, defaultAlg ).toString();
1236  QgsDebugMsgLevel( "myAlgorithmString = " + myAlgorithmString, 4 );
1237 
1238  myAlgorithm = QgsContrastEnhancement::contrastEnhancementAlgorithmFromString( myAlgorithmString );
1239 
1240  QString myLimitsString = mySettings.value( "/Raster/defaultContrastEnhancementLimits/" + key, defaultLimits ).toString();
1241  QgsDebugMsgLevel( "myLimitsString = " + myLimitsString, 4 );
1242  myLimits = QgsRasterMinMaxOrigin::limitsFromString( myLimitsString );
1243 
1244  return true;
1245 }
1246 
1248 {
1249  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1250 
1253  defaultContrastEnhancementSettings( myAlgorithm, myLimits );
1254 
1255  setContrastEnhancement( myAlgorithm, myLimits );
1256 }
1257 
1258 void QgsRasterLayer::setLayerOrder( QStringList const &layers )
1259 {
1260  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
1261 
1262  if ( mDataProvider )
1263  {
1264  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setLayerOrder(layers)." ), 4 );
1265  mDataProvider->setLayerOrder( layers );
1266  }
1267 
1268 }
1269 
1270 void QgsRasterLayer::setSubLayerVisibility( const QString &name, bool vis )
1271 {
1272 
1273  if ( mDataProvider )
1274  {
1275  QgsDebugMsgLevel( QStringLiteral( "About to mDataProvider->setSubLayerVisibility(name, vis)." ), 4 );
1276  mDataProvider->setSubLayerVisibility( name, vis );
1277  }
1278 
1279 }
1280 
1281 QDateTime QgsRasterLayer::timestamp() const
1282 {
1283  return mDataProvider->timestamp();
1284 }
1285 
1286 
1287 bool QgsRasterLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsStringMap &props ) const
1288 {
1289  Q_UNUSED( errorMessage );
1290 
1291  QgsStringMap localProps = QgsStringMap( props );
1292  if ( hasScaleBasedVisibility() )
1293  {
1294  // TODO: QgsSymbolLayerUtils::mergeScaleDependencies generate SE only and not SLD1.0
1296  }
1297 
1298  if ( isSpatial() ) // TODO: does it make sense this control?
1299  {
1300  // store constraints
1301  QDomElement constraintElem = doc.createElement( QStringLiteral( "sld:LayerFeatureConstraints" ) );
1302  node.appendChild( constraintElem );
1303 
1304  QDomElement featureTypeConstraintElem = doc.createElement( QStringLiteral( "sld:FeatureTypeConstraint" ) );
1305  constraintElem.appendChild( featureTypeConstraintElem );
1306 
1307  QDomElement userStyleElem = doc.createElement( QStringLiteral( "sld:UserStyle" ) );
1308  node.appendChild( userStyleElem );
1309 
1310  if ( !name().isEmpty() )
1311  {
1312  QDomElement nameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1313  nameElem.appendChild( doc.createTextNode( name() ) );
1314  userStyleElem.appendChild( nameElem );
1315  }
1316 
1317  if ( !abstract().isEmpty() )
1318  {
1319  QDomElement abstractElem = doc.createElement( QStringLiteral( "sld:Abstract" ) );
1320  abstractElem.appendChild( doc.createTextNode( abstract() ) );
1321  userStyleElem.appendChild( abstractElem );
1322  }
1323 
1324  if ( !title().isEmpty() )
1325  {
1326  QDomElement titleElem = doc.createElement( QStringLiteral( "sld:Title" ) );
1327  titleElem.appendChild( doc.createTextNode( title() ) );
1328  userStyleElem.appendChild( titleElem );
1329  }
1330 
1331  QDomElement featureTypeStyleElem = doc.createElement( QStringLiteral( "sld:FeatureTypeStyle" ) );
1332  userStyleElem.appendChild( featureTypeStyleElem );
1333 
1334 #if 0
1335  // TODO: Is there a way to fill it's value with the named style?
1336  // by default <sld:Name> under <sld:FeatureTypeStyle> can have 0 occurrences
1337  // the same happen for tags:
1338  // sld:Title
1339  // sld:Abstract
1340  // sld:FeatureTypeName
1341  // sld:SemanticTypeIdentifier
1342  QDomElement typeStyleNameElem = doc.createElement( QStringLiteral( "sld:Name" ) );
1343  featureTypeStyleElem.appendChild( typeStyleNameElem );
1344 #endif
1345 
1346  QDomElement typeStyleRuleElem = doc.createElement( QStringLiteral( "sld:Rule" ) );
1347  featureTypeStyleElem.appendChild( typeStyleRuleElem );
1348 
1349  // add ScaleDenominator tags
1350  if ( hasScaleBasedVisibility() )
1351  {
1352  // note that denominator is the inverted value of scale
1353  if ( maximumScale() != 0.0 )
1354  {
1355  QDomElement minScaleElem = doc.createElement( QStringLiteral( "sld:MinScaleDenominator" ) );
1356  minScaleElem.appendChild( doc.createTextNode( QString::number( maximumScale() ) ) );
1357  typeStyleRuleElem.appendChild( minScaleElem );
1358  }
1359 
1360  QDomElement maxScaleElem = doc.createElement( QStringLiteral( "sld:MaxScaleDenominator" ) );
1361  maxScaleElem.appendChild( doc.createTextNode( QString::number( minimumScale() ) ) );
1362  typeStyleRuleElem.appendChild( maxScaleElem );
1363  }
1364 
1365  // export renderer dependent tags
1366  mPipe.renderer()->toSld( doc, typeStyleRuleElem, localProps );
1367 
1368  // inject raster layer parameters in RasterSymbolizer tag because
1369  // they belongs to rasterlayer and not to the renderer => avoid to
1370  // pass many parameters value via localProps
1371  QDomNodeList elements = typeStyleRuleElem.elementsByTagName( QStringLiteral( "sld:RasterSymbolizer" ) );
1372  if ( elements.size() != 0 )
1373  {
1374  // there SHOULD be only one
1375  QDomElement rasterSymbolizerElem = elements.at( 0 ).toElement();
1376 
1377  // lamda helper used below to reduce code redundancy
1378  auto vendorOptionWriter = [&]( QString name, QString value )
1379  {
1380  QDomElement vendorOptionElem = doc.createElement( QStringLiteral( "sld:VendorOption" ) );
1381  vendorOptionElem.setAttribute( QStringLiteral( "name" ), name );
1382  vendorOptionElem.appendChild( doc.createTextNode( value ) );
1383  rasterSymbolizerElem.appendChild( vendorOptionElem );
1384  };
1385 
1386  // add greyScale rendering mode if set
1387  if ( hueSaturationFilter()->grayscaleMode() != QgsHueSaturationFilter::GrayscaleOff )
1388  {
1389  QString property;
1390  switch ( hueSaturationFilter()->grayscaleMode() )
1391  {
1393  property = QStringLiteral( "lightness" );
1394  break;
1396  property = QStringLiteral( "luminosity" );
1397  break;
1399  property = QStringLiteral( "average" );
1400  break;
1402  // added just to avoid travis fail
1403  break;
1404  }
1405  if ( !property.isEmpty() )
1406  vendorOptionWriter( QStringLiteral( "grayScale" ), property );
1407  }
1408 
1409  // add Hue, Saturation and Lighting values in props is Hue filter is set
1411  {
1412  vendorOptionWriter( QStringLiteral( "colorizeOn" ), QString::number( hueSaturationFilter()->colorizeOn() ) );
1413  vendorOptionWriter( QStringLiteral( "colorizeRed" ), QString::number( hueSaturationFilter()->colorizeColor().red() ) );
1414  vendorOptionWriter( QStringLiteral( "colorizeGreen" ), QString::number( hueSaturationFilter()->colorizeColor().green() ) );
1415  vendorOptionWriter( QStringLiteral( "colorizeBlue" ), QString::number( hueSaturationFilter()->colorizeColor().blue() ) );
1416  if ( hueSaturationFilter()->colorizeStrength() != 100.0 )
1417  vendorOptionWriter( QStringLiteral( "colorizeStrength" ), QString::number( hueSaturationFilter()->colorizeStrength() / 100.0 ) );
1418  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( hueSaturationFilter()->colorizeColor().saturationF() ) );
1419  }
1420  else
1421  {
1422  // saturation != 0 (default value)
1423  if ( hueSaturationFilter()->saturation() != 0 )
1424  {
1425  // normlize value [-100:100] -> [0:1]
1426  int s = hueSaturationFilter()->saturation();
1427  double sF = ( s - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1428  vendorOptionWriter( QStringLiteral( "saturation" ), QString::number( sF ) );
1429  }
1430  }
1431 
1432  // brightness != 0 (default value)
1433  if ( brightnessFilter()->brightness() != 0 )
1434  {
1435  // normalize value [-255:255] -> [0:1]
1436  int b = brightnessFilter()->brightness();
1437  double bF = ( b - ( -255.0 ) ) / ( 255.0 - ( -255.0 ) );
1438  vendorOptionWriter( QStringLiteral( "brightness" ), QString::number( bF ) );
1439  }
1440 
1441  // contrast != 0 (default value)
1442  if ( brightnessFilter()->contrast() != 0 )
1443  {
1444  // normlize value [-100:100] -> [0:1]
1445  int c = brightnessFilter()->contrast();
1446  double cF = ( c - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1447  vendorOptionWriter( QStringLiteral( "contrast" ), QString::number( cF ) );
1448  }
1449 
1450 #if 0
1451  // TODO: check if the below mapping formula make sense to map QGIS contrast with SLD gamma value
1452  //
1453  // add SLD1.0 ContrastEnhancement GammaValue = QGIS Contrast
1454  // SLD1.0 does only define 1 as neutral/center double value but does not define range.
1455  // because https://en.wikipedia.org/wiki/Gamma_correction assumed gamma is >0.
1456  // whilst QGIS has a -100/100 values centered in 0 => QGIS contrast value will be scaled in the
1457  // following way:
1458  // [-100,0] => [0,1] and [0,100] => [1,100]
1459  // an alternative could be scale [-100,100] => (0,2]
1460  //
1461  if ( newProps.contains( QStringLiteral( "contrast" ) ) )
1462  {
1463  double gamma;
1464  double contrast = newProps[ QStringLiteral( "contrast" ) ].toDouble();
1465  double percentage = ( contrast - ( -100.0 ) ) / ( 100.0 - ( -100.0 ) );
1466  if ( percentage <= 0.5 )
1467  {
1468  // stretch % to [0-1]
1469  gamma = percentage / 0.5;
1470  }
1471  else
1472  {
1473  gamma = contrast;
1474  }
1475 
1476  QDomElement globalContrastEnhancementElem = doc.createElement( QStringLiteral( "sld:ContrastEnhancement" ) );
1477  rasterSymolizerElem.appendChild( globalContrastEnhancementElem );
1478 
1479  QDomElement gammaValueElem = doc.createElement( QStringLiteral( "sld:GammaValue" ) );
1480  gammaValueElem.appendChild( doc.createTextNode( QString::number( gamma ) ) );
1481  globalContrastEnhancementElem.appendChild( gammaValueElem );
1482  }
1483 #endif
1484  }
1485  }
1486  return true;
1487 }
1488 
1489 
1491 {
1492  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1493  if ( !renderer )
1494  {
1495  return;
1496  }
1497 
1498  mPipe.set( renderer );
1499  emit rendererChanged();
1500  emit styleChanged();
1501 }
1502 
1503 void QgsRasterLayer::showStatusMessage( QString const &message )
1504 {
1505  // QgsDebugMsg(QString("entered with '%1'.").arg(theMessage));
1506 
1507  // Pass-through
1508  // TODO: See if we can connect signal-to-signal. This is a kludge according to the Qt doc.
1509  emit statusChanged( message );
1510 }
1511 
1512 QStringList QgsRasterLayer::subLayers() const
1513 {
1514  return mDataProvider->subLayers();
1515 }
1516 
1517 // this function should be used when rendering with the MTR engine introduced in 2.3, as QPixmap is not thread safe (see bug #9626)
1518 // note: previewAsImage and previewAsPixmap should use a common low-level fct QgsRasterLayer::previewOnPaintDevice( QSize size, QColor bgColor, QPaintDevice &device )
1519 QImage QgsRasterLayer::previewAsImage( QSize size, const QColor &bgColor, QImage::Format format )
1520 {
1521  QImage myQImage( size, format );
1522 
1523  if ( ! isValid( ) )
1524  return QImage();
1525 
1526  myQImage.setColor( 0, bgColor.rgba() );
1527  myQImage.fill( 0 ); //defaults to white, set to transparent for rendering on a map
1528 
1529 
1530  QgsRasterViewPort *myRasterViewPort = new QgsRasterViewPort();
1531 
1532  double myMapUnitsPerPixel;
1533  double myX = 0.0;
1534  double myY = 0.0;
1535  QgsRectangle myExtent = mDataProvider->extent();
1536  if ( myExtent.width() / myExtent.height() >= static_cast< double >( myQImage.width() ) / myQImage.height() )
1537  {
1538  myMapUnitsPerPixel = myExtent.width() / myQImage.width();
1539  myY = ( myQImage.height() - myExtent.height() / myMapUnitsPerPixel ) / 2;
1540  }
1541  else
1542  {
1543  myMapUnitsPerPixel = myExtent.height() / myQImage.height();
1544  myX = ( myQImage.width() - myExtent.width() / myMapUnitsPerPixel ) / 2;
1545  }
1546 
1547  double myPixelWidth = myExtent.width() / myMapUnitsPerPixel;
1548  double myPixelHeight = myExtent.height() / myMapUnitsPerPixel;
1549 
1550  myRasterViewPort->mTopLeftPoint = QgsPointXY( myX, myY );
1551  myRasterViewPort->mBottomRightPoint = QgsPointXY( myPixelWidth, myPixelHeight );
1552  myRasterViewPort->mWidth = myQImage.width();
1553  myRasterViewPort->mHeight = myQImage.height();
1554 
1555  myRasterViewPort->mDrawnExtent = myExtent;
1556  myRasterViewPort->mSrcCRS = QgsCoordinateReferenceSystem(); // will be invalid
1557  myRasterViewPort->mDestCRS = QgsCoordinateReferenceSystem(); // will be invalid
1558  myRasterViewPort->mSrcDatumTransform = -1;
1559  myRasterViewPort->mDestDatumTransform = -1;
1560 
1561  QgsMapToPixel *myMapToPixel = new QgsMapToPixel( myMapUnitsPerPixel );
1562 
1563  QPainter *myQPainter = new QPainter( &myQImage );
1564  draw( myQPainter, myRasterViewPort, myMapToPixel );
1565  delete myRasterViewPort;
1566  delete myMapToPixel;
1567  myQPainter->end();
1568  delete myQPainter;
1569 
1570  return myQImage;
1571 }
1572 
1574 //
1575 // Protected methods
1576 //
1578 /*
1579  * \param QDomNode node that will contain the symbology definition for this layer.
1580  * \param errorMessage reference to string that will be updated with any error messages
1581  * \return true in case of success.
1582  */
1583 bool QgsRasterLayer::readSymbology( const QDomNode &layer_node, QString &errorMessage,
1584  QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1585 {
1586  Q_UNUSED( errorMessage );
1587  // TODO: implement categories for raster layer
1588 
1589  QDomElement rasterRendererElem;
1590 
1591  QDomElement layerElement = layer_node.toElement();
1592  readCommonStyle( layerElement, context, categories );
1593 
1594  // pipe element was introduced in the end of 1.9 development when there were
1595  // already many project files in use so we support 1.9 backward compatibility
1596  // even it was never officially released -> use pipe element if present, otherwise
1597  // use layer node
1598  QDomNode pipeNode = layer_node.firstChildElement( QStringLiteral( "pipe" ) );
1599  if ( pipeNode.isNull() ) // old project
1600  {
1601  pipeNode = layer_node;
1602  }
1603 
1604  //rasterlayerproperties element there -> old format (1.8 and early 1.9)
1605  if ( !layer_node.firstChildElement( QStringLiteral( "rasterproperties" ) ).isNull() )
1606  {
1607  //copy node because layer_node is const
1608  QDomNode layerNodeCopy = layer_node.cloneNode();
1609  QDomDocument doc = layerNodeCopy.ownerDocument();
1610  QDomElement rasterPropertiesElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterproperties" ) );
1611  QgsProjectFileTransform::convertRasterProperties( doc, layerNodeCopy, rasterPropertiesElem,
1612  this );
1613  rasterRendererElem = layerNodeCopy.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1614  QgsDebugMsgLevel( doc.toString(), 4 );
1615  }
1616  else
1617  {
1618  rasterRendererElem = pipeNode.firstChildElement( QStringLiteral( "rasterrenderer" ) );
1619  }
1620 
1621  if ( !rasterRendererElem.isNull() )
1622  {
1623  QString rendererType = rasterRendererElem.attribute( QStringLiteral( "type" ) );
1624  QgsRasterRendererRegistryEntry rendererEntry;
1625  if ( QgsApplication::rasterRendererRegistry()->rendererData( rendererType, rendererEntry ) )
1626  {
1627  QgsRasterRenderer *renderer = rendererEntry.rendererCreateFunction( rasterRendererElem, dataProvider() );
1628  mPipe.set( renderer );
1629  }
1630  }
1631 
1632  //brightness
1634  mPipe.set( brightnessFilter );
1635 
1636  //brightness coefficient
1637  QDomElement brightnessElem = pipeNode.firstChildElement( QStringLiteral( "brightnesscontrast" ) );
1638  if ( !brightnessElem.isNull() )
1639  {
1640  brightnessFilter->readXml( brightnessElem );
1641  }
1642 
1643  //hue/saturation
1645  mPipe.set( hueSaturationFilter );
1646 
1647  //saturation coefficient
1648  QDomElement hueSaturationElem = pipeNode.firstChildElement( QStringLiteral( "huesaturation" ) );
1649  if ( !hueSaturationElem.isNull() )
1650  {
1651  hueSaturationFilter->readXml( hueSaturationElem );
1652  }
1653 
1654  //resampler
1656  mPipe.set( resampleFilter );
1657 
1658  //max oversampling
1659  QDomElement resampleElem = pipeNode.firstChildElement( QStringLiteral( "rasterresampler" ) );
1660  if ( !resampleElem.isNull() )
1661  {
1662  resampleFilter->readXml( resampleElem );
1663  }
1664 
1665  // get and set the blend mode if it exists
1666  QDomNode blendModeNode = layer_node.namedItem( QStringLiteral( "blendMode" ) );
1667  if ( !blendModeNode.isNull() )
1668  {
1669  QDomElement e = blendModeNode.toElement();
1670  setBlendMode( QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( e.text().toInt() ) ) );
1671  }
1672 
1673  readCustomProperties( layer_node );
1674 
1675  return true;
1676 }
1677 
1678 bool QgsRasterLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
1679 {
1680  return readSymbology( node, errorMessage, context, categories );
1681 }
1682 
1683 bool QgsRasterLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
1684 {
1685  QgsDebugMsgLevel( QStringLiteral( "Entered" ), 4 );
1686  // Make sure to read the file first so stats etc are initialized properly!
1687 
1688  //process provider key
1689  QDomNode pkeyNode = layer_node.namedItem( QStringLiteral( "provider" ) );
1690 
1691  if ( pkeyNode.isNull() )
1692  {
1693  mProviderKey = QStringLiteral( "gdal" );
1694  }
1695  else
1696  {
1697  QDomElement pkeyElt = pkeyNode.toElement();
1698  mProviderKey = pkeyElt.text();
1699  if ( mProviderKey.isEmpty() )
1700  {
1701  mProviderKey = QStringLiteral( "gdal" );
1702  }
1703  }
1704 
1705  // Open the raster source based on provider and datasource
1706 
1707  // Go down the raster-data-provider paradigm
1708 
1709  // Collect provider-specific information
1710 
1711  QDomNode rpNode = layer_node.namedItem( QStringLiteral( "rasterproperties" ) );
1712 
1713  if ( mProviderKey == QLatin1String( "wms" ) )
1714  {
1715  // >>> BACKWARD COMPATIBILITY < 1.9
1716  // The old WMS URI format does not contain all the information, we add them here.
1717  if ( !mDataSource.contains( QLatin1String( "crs=" ) ) && !mDataSource.contains( QLatin1String( "format=" ) ) )
1718  {
1719  QgsDebugMsgLevel( QStringLiteral( "Old WMS URI format detected -> adding params" ), 4 );
1720  QgsDataSourceUri uri;
1721  uri.setEncodedUri( mDataSource );
1722  QDomElement layerElement = rpNode.firstChildElement( QStringLiteral( "wmsSublayer" ) );
1723  while ( !layerElement.isNull() )
1724  {
1725  // TODO: sublayer visibility - post-0.8 release timeframe
1726 
1727  // collect name for the sublayer
1728  uri.setParam( QStringLiteral( "layers" ), layerElement.namedItem( QStringLiteral( "name" ) ).toElement().text() );
1729 
1730  // collect style for the sublayer
1731  uri.setParam( QStringLiteral( "styles" ), layerElement.namedItem( QStringLiteral( "style" ) ).toElement().text() );
1732 
1733  layerElement = layerElement.nextSiblingElement( QStringLiteral( "wmsSublayer" ) );
1734  }
1735 
1736  // Collect format
1737  QDomNode formatNode = rpNode.namedItem( QStringLiteral( "wmsFormat" ) );
1738  uri.setParam( QStringLiteral( "format" ), rpNode.namedItem( QStringLiteral( "wmsFormat" ) ).toElement().text() );
1739 
1740  // WMS CRS URL param should not be mixed with that assigned to the layer.
1741  // In the old WMS URI version there was no CRS and layer crs().authid() was used.
1742  uri.setParam( QStringLiteral( "crs" ), crs().authid() );
1743  mDataSource = uri.encodedUri();
1744  }
1745  // <<< BACKWARD COMPATIBILITY < 1.9
1746  }
1747 
1748  QgsDataProvider::ProviderOptions providerOptions;
1749  setDataProvider( mProviderKey, providerOptions );
1750 
1751  if ( ! mDataProvider )
1752  {
1753  QgsDebugMsg( QStringLiteral( "Raster data provider could not be created for %1" ).arg( mDataSource ) );
1754  return false;
1755  }
1756 
1757  QString error;
1758  bool res = readSymbology( layer_node, error, context );
1759 
1760  // old wms settings we need to correct
1761  if ( res && mProviderKey == QLatin1String( "wms" ) && ( !renderer() || renderer()->type() != QLatin1String( "singlebandcolordata" ) ) )
1762  {
1763  setRendererForDrawingStyle( QgsRaster::SingleBandColorDataStyle );
1764  }
1765 
1766  // Check timestamp
1767  // This was probably introduced to reload completely raster if data changed and
1768  // reset completely symbology to reflect new data type etc. It creates however
1769  // problems, because user defined symbology is complete lost if data file time
1770  // changed (the content may be the same). See also 6900.
1771 #if 0
1772  QDomNode stampNode = layer_node.namedItem( "timestamp" );
1773  if ( !stampNode.isNull() )
1774  {
1775  QDateTime stamp = QDateTime::fromString( stampNode.toElement().text(), Qt::ISODate );
1776  // TODO: very bad, we have to load twice!!! Make QgsDataProvider::timestamp() static?
1777  if ( stamp < mDataProvider->dataTimestamp() )
1778  {
1779  QgsDebugMsg( QStringLiteral( "data changed, reload provider" ) );
1780  closeDataProvider();
1781  init();
1783  if ( !mValid ) return false;
1784  }
1785  }
1786 #endif
1787 
1788  // Load user no data value
1789  QDomElement noDataElement = layer_node.firstChildElement( QStringLiteral( "noData" ) );
1790 
1791  QDomNodeList noDataBandList = noDataElement.elementsByTagName( QStringLiteral( "noDataList" ) );
1792 
1793  for ( int i = 0; i < noDataBandList.size(); ++i )
1794  {
1795  QDomElement bandElement = noDataBandList.at( i ).toElement();
1796  bool ok;
1797  int bandNo = bandElement.attribute( QStringLiteral( "bandNo" ) ).toInt( &ok );
1798  QgsDebugMsgLevel( QStringLiteral( "bandNo = %1" ).arg( bandNo ), 4 );
1799  if ( ok && ( bandNo > 0 ) && ( bandNo <= mDataProvider->bandCount() ) )
1800  {
1801  mDataProvider->setUseSourceNoDataValue( bandNo, bandElement.attribute( QStringLiteral( "useSrcNoData" ) ).toInt() );
1802  QgsRasterRangeList myNoDataRangeList;
1803 
1804  QDomNodeList rangeList = bandElement.elementsByTagName( QStringLiteral( "noDataRange" ) );
1805 
1806  myNoDataRangeList.reserve( rangeList.size() );
1807  for ( int j = 0; j < rangeList.size(); ++j )
1808  {
1809  QDomElement rangeElement = rangeList.at( j ).toElement();
1810  QgsRasterRange myNoDataRange( rangeElement.attribute( QStringLiteral( "min" ) ).toDouble(),
1811  rangeElement.attribute( QStringLiteral( "max" ) ).toDouble() );
1812  QgsDebugMsgLevel( QStringLiteral( "min = %1 %2" ).arg( rangeElement.attribute( "min" ) ).arg( myNoDataRange.min() ), 4 );
1813  myNoDataRangeList << myNoDataRange;
1814  }
1815  mDataProvider->setUserNoDataValue( bandNo, myNoDataRangeList );
1816  }
1817  }
1818 
1819  readStyleManager( layer_node );
1820 
1821  return res;
1822 } // QgsRasterLayer::readXml( QDomNode & layer_node )
1823 
1824 /*
1825  * \param QDomNode the node that will have the style element added to it.
1826  * \param QDomDocument the document that will have the QDomNode added.
1827  * \param errorMessage reference to string that will be updated with any error messages
1828  * \return true in case of success.
1829  */
1830 bool QgsRasterLayer::writeSymbology( QDomNode &layer_node, QDomDocument &document, QString &errorMessage,
1831  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1832 {
1833  Q_UNUSED( errorMessage );
1834  // TODO: implement categories for raster layer
1835 
1836  QDomElement layerElement = layer_node.toElement();
1837  writeCommonStyle( layerElement, document, context, categories );
1838 
1839  // Store pipe members (except provider) into pipe element, in future, it will be
1840  // possible to add custom filters into the pipe
1841  QDomElement pipeElement = document.createElement( QStringLiteral( "pipe" ) );
1842 
1843  for ( int i = 1; i < mPipe.size(); i++ )
1844  {
1845  QgsRasterInterface *interface = mPipe.at( i );
1846  if ( !interface ) continue;
1847  interface->writeXml( document, pipeElement );
1848  }
1849 
1850  layer_node.appendChild( pipeElement );
1851 
1852  // add blend mode node
1853  QDomElement blendModeElement = document.createElement( QStringLiteral( "blendMode" ) );
1854  QDomText blendModeText = document.createTextNode( QString::number( QgsPainting::getBlendModeEnum( blendMode() ) ) );
1855  blendModeElement.appendChild( blendModeText );
1856  layer_node.appendChild( blendModeElement );
1857 
1858  return true;
1859 }
1860 
1861 bool QgsRasterLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage,
1862  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1863 {
1864  return writeSymbology( node, doc, errorMessage, context, categories );
1865 } // bool QgsRasterLayer::writeSymbology
1866 
1867 /*
1868  * virtual
1869  * \note Called by QgsMapLayer::writeXml().
1870  */
1871 bool QgsRasterLayer::writeXml( QDomNode &layer_node,
1872  QDomDocument &document,
1873  const QgsReadWriteContext &context ) const
1874 {
1875  // first get the layer element so that we can append the type attribute
1876 
1877  QDomElement mapLayerNode = layer_node.toElement();
1878 
1879  if ( mapLayerNode.isNull() || "maplayer" != mapLayerNode.nodeName() )
1880  {
1881  QgsMessageLog::logMessage( tr( "<maplayer> not found." ), tr( "Raster" ) );
1882  return false;
1883  }
1884 
1885  mapLayerNode.setAttribute( QStringLiteral( "type" ), QStringLiteral( "raster" ) );
1886 
1887  // add provider node
1888 
1889  QDomElement provider = document.createElement( QStringLiteral( "provider" ) );
1890  QDomText providerText = document.createTextNode( mProviderKey );
1891  provider.appendChild( providerText );
1892  layer_node.appendChild( provider );
1893 
1894  // User no data
1895  QDomElement noData = document.createElement( QStringLiteral( "noData" ) );
1896 
1897  for ( int bandNo = 1; bandNo <= mDataProvider->bandCount(); bandNo++ )
1898  {
1899  QDomElement noDataRangeList = document.createElement( QStringLiteral( "noDataList" ) );
1900  noDataRangeList.setAttribute( QStringLiteral( "bandNo" ), bandNo );
1901  noDataRangeList.setAttribute( QStringLiteral( "useSrcNoData" ), mDataProvider->useSourceNoDataValue( bandNo ) );
1902 
1903  Q_FOREACH ( QgsRasterRange range, mDataProvider->userNoDataValues( bandNo ) )
1904  {
1905  QDomElement noDataRange = document.createElement( QStringLiteral( "noDataRange" ) );
1906 
1907  noDataRange.setAttribute( QStringLiteral( "min" ), QgsRasterBlock::printValue( range.min() ) );
1908  noDataRange.setAttribute( QStringLiteral( "max" ), QgsRasterBlock::printValue( range.max() ) );
1909  noDataRangeList.appendChild( noDataRange );
1910  }
1911 
1912  noData.appendChild( noDataRangeList );
1913 
1914  }
1915  if ( noData.hasChildNodes() )
1916  {
1917  layer_node.appendChild( noData );
1918  }
1919 
1920  writeStyleManager( layer_node, document );
1921 
1922  //write out the symbology
1923  QString errorMsg;
1924  return writeSymbology( layer_node, document, errorMsg, context );
1925 }
1926 
1927 // TODO: this should ideally go to gdal provider (together with most of encodedSource() + decodedSource())
1928 static bool _parseGpkgColons( const QString &src, QString &filename, QString &tablename )
1929 {
1930  // GDAL accepts the following input format: GPKG:filename:table
1931  // (GDAL won't accept quoted filename)
1932 
1933  QStringList lst = src.split( ':' );
1934  if ( lst.count() != 3 && lst.count() != 4 )
1935  return false;
1936 
1937  tablename = lst.last();
1938  if ( lst.count() == 3 )
1939  {
1940  filename = lst[1];
1941  return true;
1942  }
1943  else if ( lst.count() == 4 && lst[1].count() == 1 && ( lst[2][0] == '/' || lst[2][0] == '\\' ) )
1944  {
1945  // a bit of handling to make sure that filename C:\hello.gpkg is parsed correctly
1946  filename = lst[1] + ":" + lst[2];
1947  return true;
1948  }
1949  return false;
1950 }
1951 
1952 
1953 QString QgsRasterLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
1954 {
1955  QString src( source );
1956  bool handled = false;
1957 
1958  // Update path for subdataset
1959  if ( providerType() == QLatin1String( "gdal" ) )
1960  {
1961  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
1962  {
1963  // NETCDF:filename:variable
1964  // filename can be quoted with " as it can contain colons
1965  QRegExp r( "NETCDF:(.+):([^:]+)" );
1966  if ( r.exactMatch( src ) )
1967  {
1968  QString filename = r.cap( 1 );
1969  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
1970  filename = filename.mid( 1, filename.length() - 2 );
1971  src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
1972  handled = true;
1973  }
1974  }
1975  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
1976  {
1977  // GPKG:filename:table
1978  QString filename, tablename;
1979  if ( _parseGpkgColons( src, filename, tablename ) )
1980  {
1981  filename = context.pathResolver().writePath( filename );
1982  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
1983  handled = true;
1984  }
1985  }
1986  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
1987  {
1988  // HDF4_SDS:subdataset_type:file_name:subdataset_index
1989  // filename can be quoted with " as it can contain colons
1990  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
1991  if ( r.exactMatch( src ) )
1992  {
1993  QString filename = r.cap( 2 );
1994  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
1995  filename = filename.mid( 1, filename.length() - 2 );
1996  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 3 );
1997  handled = true;
1998  }
1999  }
2000  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2001  {
2002  // HDF5:file_name:subdataset
2003  // filename can be quoted with " as it can contain colons
2004  QRegExp r( "HDF5:(.+):([^:]+)" );
2005  if ( r.exactMatch( src ) )
2006  {
2007  QString filename = r.cap( 1 );
2008  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2009  filename = filename.mid( 1, filename.length() - 2 );
2010  src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
2011  handled = true;
2012  }
2013  }
2014  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2015  {
2016  // NITF_IM:0:filename
2017  // RADARSAT_2_CALIB:?:filename
2018  QRegExp r( "([^:]+):([^:]+):(.+)" );
2019  if ( r.exactMatch( src ) )
2020  {
2021  src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().writePath( r.cap( 3 ) );
2022  handled = true;
2023  }
2024  }
2025  }
2026 
2027  if ( !handled )
2028  src = context.pathResolver().writePath( src );
2029 
2030  return src;
2031 }
2032 
2033 QString QgsRasterLayer::decodedSource( const QString &source, const QString &provider, const QgsReadWriteContext &context ) const
2034 {
2035  QString src( source );
2036 
2037  if ( provider == QLatin1String( "wms" ) )
2038  {
2039  // >>> BACKWARD COMPATIBILITY < 1.9
2040  // For project file backward compatibility we must support old format:
2041  // 1. mode: <url>
2042  // example: http://example.org/wms?
2043  // 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
2044  // example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
2045  // example: featureCount=10,http://example.org/wms?
2046  // example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
2047  // This is modified version of old QgsWmsProvider::parseUri
2048  // The new format has always params crs,format,layers,styles and that params
2049  // should not appear in old format url -> use them to identify version
2050  // XYZ tile layers do not need to contain crs,format params, but they have type=xyz
2051  if ( !src.contains( QLatin1String( "type=" ) ) &&
2052  !src.contains( QLatin1String( "crs=" ) ) && !src.contains( QLatin1String( "format=" ) ) )
2053  {
2054  QgsDebugMsg( QStringLiteral( "Old WMS URI format detected -> converting to new format" ) );
2055  QgsDataSourceUri uri;
2056  if ( !src.startsWith( QLatin1String( "http:" ) ) )
2057  {
2058  QStringList parts = src.split( ',' );
2059  QStringListIterator iter( parts );
2060  while ( iter.hasNext() )
2061  {
2062  QString item = iter.next();
2063  if ( item.startsWith( QLatin1String( "username=" ) ) )
2064  {
2065  uri.setParam( QStringLiteral( "username" ), item.mid( 9 ) );
2066  }
2067  else if ( item.startsWith( QLatin1String( "password=" ) ) )
2068  {
2069  uri.setParam( QStringLiteral( "password" ), item.mid( 9 ) );
2070  }
2071  else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
2072  {
2073  // in < 1.9 tiled= may apper in to variants:
2074  // tiled=width;height - non tiled mode, specifies max width and max height
2075  // tiled=width;height;resolutions-1;resolution2;... - tile mode
2076 
2077  QStringList params = item.mid( 6 ).split( ';' );
2078 
2079  if ( params.size() == 2 ) // non tiled mode
2080  {
2081  uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
2082  uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
2083  }
2084  else if ( params.size() > 2 ) // tiled mode
2085  {
2086  // resolutions are no more needed and size limit is not used for tiles
2087  // we have to tell to the provider however that it is tiled
2088  uri.setParam( QStringLiteral( "tileMatrixSet" ), QString() );
2089  }
2090  }
2091  else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
2092  {
2093  uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
2094  }
2095  else if ( item.startsWith( QLatin1String( "url=" ) ) )
2096  {
2097  uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
2098  }
2099  else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
2100  {
2101  uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
2102  }
2103  }
2104  }
2105  else
2106  {
2107  uri.setParam( QStringLiteral( "url" ), src );
2108  }
2109  src = uri.encodedUri();
2110  // At this point, the URI is obviously incomplete, we add additional params
2111  // in QgsRasterLayer::readXml
2112  }
2113  // <<< BACKWARD COMPATIBILITY < 1.9
2114  }
2115  else
2116  {
2117  bool handled = false;
2118 
2119  if ( provider == QLatin1String( "gdal" ) )
2120  {
2121  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
2122  {
2123  // NETCDF:filename:variable
2124  // filename can be quoted with " as it can contain colons
2125  QRegExp r( "NETCDF:(.+):([^:]+)" );
2126  if ( r.exactMatch( src ) )
2127  {
2128  QString filename = r.cap( 1 );
2129  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2130  filename = filename.mid( 1, filename.length() - 2 );
2131  src = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
2132  handled = true;
2133  }
2134  }
2135  else if ( src.startsWith( QLatin1String( "GPKG:" ) ) )
2136  {
2137  // GPKG:filename:table
2138  QString filename, tablename;
2139  if ( _parseGpkgColons( src, filename, tablename ) )
2140  {
2141  filename = context.pathResolver().readPath( filename );
2142  src = QStringLiteral( "GPKG:%1:%2" ).arg( filename, tablename );
2143  handled = true;
2144  }
2145  }
2146  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
2147  {
2148  // HDF4_SDS:subdataset_type:file_name:subdataset_index
2149  // filename can be quoted with " as it can contain colons
2150  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
2151  if ( r.exactMatch( src ) )
2152  {
2153  QString filename = r.cap( 2 );
2154  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2155  filename = filename.mid( 1, filename.length() - 2 );
2156  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 3 );
2157  handled = true;
2158  }
2159  }
2160  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
2161  {
2162  // HDF5:file_name:subdataset
2163  // filename can be quoted with " as it can contain colons
2164  QRegExp r( "HDF5:(.+):([^:]+)" );
2165  if ( r.exactMatch( src ) )
2166  {
2167  QString filename = r.cap( 1 );
2168  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
2169  filename = filename.mid( 1, filename.length() - 2 );
2170  src = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
2171  handled = true;
2172  }
2173  }
2174  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
2175  {
2176  // NITF_IM:0:filename
2177  // RADARSAT_2_CALIB:?:filename
2178  QRegExp r( "([^:]+):([^:]+):(.+)" );
2179  if ( r.exactMatch( src ) )
2180  {
2181  src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().readPath( r.cap( 3 ) );
2182  handled = true;
2183  }
2184  }
2185  }
2186 
2187  if ( !handled )
2188  src = context.pathResolver().readPath( src );
2189  }
2190 
2191  return src;
2192 }
2193 
2195 {
2196  if ( !mDataProvider ) return 0;
2197  return mDataProvider->xSize();
2198 }
2199 
2201 {
2202  if ( !mDataProvider ) return 0;
2203  return mDataProvider->ySize();
2204 }
2205 
2207 //
2208 // Private methods
2209 //
2211 bool QgsRasterLayer::update()
2212 {
2213  QgsDebugMsgLevel( QStringLiteral( "entered." ), 4 );
2214  // Check if data changed
2215  if ( mDataProvider->dataTimestamp() > mDataProvider->timestamp() )
2216  {
2217  QgsDebugMsgLevel( QStringLiteral( "reload data" ), 4 );
2218  closeDataProvider();
2219  init();
2220  QgsDataProvider::ProviderOptions providerOptions;
2221  setDataProvider( mProviderKey, providerOptions );
2222  emit dataChanged();
2223  }
2224  return mValid;
2225 }
static void mergeScaleDependencies(double mScaleMinDenom, double mScaleMaxDenom, QgsStringMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
QgsDataProvider * classFactoryFunction_t(const QString *, const QgsDataProvider::ProviderOptions &options)
static void convertRasterProperties(QDomDocument &doc, QDomNode &parentNode, QDomElement &rasterPropertiesElem, QgsRasterLayer *rlayer)
int width() const
Returns the width of the (unclipped) raster.
QgsRasterMinMaxOrigin::Limits limits() const
Returns the raster limits.
bool shade(double value, int *returnRedValue, int *returnGreenValue, int *returnBlueValue, int *returnAlphaValue) const override
Generates and new RGB value based on one input value.
virtual int bandCount() const =0
Gets number of bands.
QgsRasterMinMaxOrigin::Extent extent() const
Returns the raster extent.
virtual void setSubLayerVisibility(const QString &name, bool vis)
Set the visibility of the given sublayer name.
The class is used as a container of context for various read/write operations on other objects...
#define ERR(message)
QString extentSectionHtml(const bool showSpatialExtent=true) const
Formats the "Extents" section according to a metadata object (extent and temporal).
IdentifyFormat
Definition: qgsraster.h:57
A rectangle specified with double values.
Definition: qgsrectangle.h:41
Base class for all map layer types.
Definition: qgsmaplayer.h:64
Thirty two bit signed integer (qint32)
Definition: qgis.h:86
Interface for all raster shaders.
void setColorRampItemList(const QList< QgsColorRampShader::ColorRampItem > &list)
Sets a custom colormap.
QgsRasterLayer * clone() const override
Returns a new instance equivalent to this one.
QStringList subLayers() const override
Returns the sublayers of this layer.
static const QString QGIS_VERSION
Version string.
Definition: qgis.h:51
static QString printValue(double value)
Print double value with all necessary significant digits.
void refreshContrastEnhancement(const QgsRectangle &extent)
Refresh contrast enhancement with new extent.
virtual void setUseSourceNoDataValue(int bandNo, bool use)
Sets the source nodata value usage.
void setProviderType(const QString &providerType)
Sets the providerType (provider key)
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
QString contactsSectionHtml() const
Formats the "Contacts" section according to a metadata object.
QString historySectionHtml() const
Formats the "History" section according to a metadata object.
void setDataSource(const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag=false) override
Updates the data source of the layer.
const QgsContrastEnhancement * blueContrastEnhancement() const
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
Iterator for sequentially processing raster cells.
QString htmlMetadata() const override
Obtain a formatted HTML string containing assorted metadata for this layer.
static const QgsContrastEnhancement::ContrastEnhancementAlgorithm MULTIPLE_BAND_MULTI_BYTE_ENHANCEMENT_ALGORITHM
Default enhancement algorithm for multiple band raster of type different from Byte.
int bandCount() const
Returns the number of bands in this layer.
virtual void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const
Used from subclasses to create SLD Rule elements following SLD v1.0 specs.
bool readStyle(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) override
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QgsHueSaturationFilter * hueSaturationFilter() const
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
DrawingStyle
This enumerator describes the different kinds of drawing we can do.
Definition: qgsraster.h:89
A ramp shader will color a raster pixel based on a list of values ranges in a ramp.
virtual QList< QgsColorRampShader::ColorRampItem > colorTable(int bandNo) const
virtual QgsError error() const
Gets current status error.
QgsRasterRenderer * renderer() const
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setRenderer(QgsRasterRenderer *renderer)
Sets raster renderer. Takes ownership of the renderer object.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
virtual QgsError error() const
Gets current status error.
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
void setLayerOrder(const QStringList &layers) override
Reorders the previously selected sublayers of this layer from bottom to top.
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
QString mProviderKey
Data provider key (name of the data provider)
Definition: qgsmaplayer.h:1459
QString accessSectionHtml() const
Formats the "Access" section according to a metadata object.
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
QgsRasterPipe * pipe()
Returns the raster pipe.
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...
A class to represent a 2D point.
Definition: qgspointxy.h:43
void setDefaultContrastEnhancement()
Sets the default contrast enhancement.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
QString linksSectionHtml() const
Formats the "Links" section according to a metadata object.
void showStatusMessage(const QString &message)
virtual QgsRasterInterface * input() const
Current input.
QgsRasterProjector * projector() const
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
virtual QString type() const
Thirty two bit unsigned integer (quint32)
Definition: qgis.h:85
void readCustomProperties(const QDomNode &layerNode, const QString &keyStartsWith=QString())
Read custom properties from project file.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
DataType
Raster data types.
Definition: qgis.h:79
QgsRasterInterface * last() const
double maximumValue
The maximum cell value in the raster band.
static Limits limitsFromString(const QString &limits)
Deserialize Limits.
const QgsContrastEnhancement * contrastEnhancement() const
virtual int ySize() const
int mWidth
Width, number of columns to be rendered.
bool readXml(const QDomNode &layer_node, QgsReadWriteContext &context) override
Called by readLayerXML(), used by children to read state specific to them from project files...
virtual const QgsLayerMetadata & metadata() const
Returns a reference to the layer&#39;s metadata store.
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
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...
static QDateTime lastModified(const QString &name)
Returns time stamp for given file name.
double max() const
Returns the maximum value for the range.
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
Raster values range container.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
Resample filter pipe for rasters.
Abstract base class for spatial data provider implementations.
const QgsRasterMinMaxOrigin & minMaxOrigin() const
Returns const reference to origin of min/max values.
QgsRasterRenderer * renderer() const
int height() const
Returns the height of the (unclipped) raster.
static const double SAMPLE_SIZE
Default sample size (number of pixels) for estimated statistics/histogram calculation.
Thirty two bit floating point (float)
Definition: qgis.h:87
bool isValid() const
Returns the status of the layer.
Implementation of threaded rendering for raster layers.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
QgsRasterShader * shader()
Returns the raster shader.
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.
QgsMapLayer::LayerType type() const
Returns the type of the layer.
QPixmap paletteAsPixmap(int bandNumber=1)
Returns a 100x100 pixmap of the color palette.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
Sixteen bit signed integer (qint16)
Definition: qgis.h:84
QDateTime timestamp() const override
Time stamp of data source in the moment when data/metadata were loaded by provider.
Complex Int16.
Definition: qgis.h:89
QString mLayerName
Name of the layer - used for display.
Definition: qgsmaplayer.h:1420
void setShader(QgsRasterShader *shader)
Takes ownership of the shader.
void styleChanged()
Signal emitted whenever a change affects the layer&#39;s style.
void setGreenContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
void statusChanged(const QString &) const
Emit a message to be displayed on status bar, usually used by network providers (WMS,WCS)
Sixty four bit floating point (double)
Definition: qgis.h:88
Current extent of the canvas (at the time of computation) is used to compute statistics.
static QString identifyFormatName(QgsRaster::IdentifyFormat format)
virtual QgsRectangle extent() const
Returns the extent of the layer.
void setExtent(QgsRasterMinMaxOrigin::Extent extent)
Sets the extent.
QgsCoordinateReferenceSystem mDestCRS
Target coordinate system.
virtual QgsLayerMetadata layerMetadata() const
Returns layer metadata collected from the provider&#39;s source.
QgsDataProvider * createProvider(const QString &providerKey, const QString &dataSource, const QgsDataProvider::ProviderOptions &options=QgsDataProvider::ProviderOptions())
Creates a new instance of a provider.
QgsBrightnessContrastFilter * brightnessFilter() const
void setCrs(const QgsCoordinateReferenceSystem &srcCRS, const QgsCoordinateReferenceSystem &destCRS, int srcDatumTransform=-1, int destDatumTransform=-1)
Sets the source and destination CRS.
QDateTime timestamp() const override
Time stamp of data source in the moment when data/metadata were loaded by provider.
void statusChanged(const QString &status)
Emit a signal with status (e.g. to be caught by QgisApp and display a msg on status bar) ...
double cumulativeCutLower() const
Returns the lower bound of cumulative cut method (between 0 and 1).
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
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32_Premultiplied.
Definition: qgis.h:94
Perform transforms between map coordinates and device coordinates.
Definition: qgsmaptopixel.h:37
static const QgsRasterMinMaxOrigin::Limits MULTIPLE_BAND_SINGLE_BYTE_MIN_MAX_LIMITS
Default enhancement limits for multiple band raster of type Byte.
The drawing pipe for raster layers.
double stdDev
The standard deviation of the cell values.
Paletted (see associated color table)
Definition: qgsraster.h:39
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 setColorRampType(QgsColorRampShader::Type colorRampType)
Sets the color ramp type.
Alpha (0=transparent, 255=opaque)
Definition: qgsraster.h:43
QgsRasterDataProvider * dataProvider() override
Returns the layer&#39;s data provider, it may be null.
This class describes the origin of min/max values.
The RasterBandStats struct is a container for statistics about a single raster band.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
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...
double mean
The mean cell value for the band. NO_DATA values are excluded.
Continuous palette, QGIS addition, GRASS.
Definition: qgsraster.h:54
bool writeSld(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsStringMap &props=QgsStringMap()) const
Writes the symbology of the layer into the document provided in SLD 1.0.0 format. ...
void writeCommonStyle(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write style data common to all layer types.
void readStyleManager(const QDomNode &layerNode)
Read style manager&#39;s configuration (if any). To be called by subclasses.
void setRedContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
bool isEmpty() const
Returns true if the rectangle is empty.
Definition: qgsrectangle.h:426
virtual void setMetadata(const QgsLayerMetadata &metadata)
Sets the layer&#39;s metadata store.
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer&#39;s spatial reference system.
QgsPointXY mBottomRightPoint
Coordinate (in output device coordinate system) of bottom right corner of the part of the raster that...
virtual int capabilities() const
Returns a bitmask containing the supported capabilities.
QgsRectangle extent() const override=0
Returns the extent of the layer.
virtual bool isValid() const =0
Returns true if this is a valid layer.
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the coordinate system for the data source.
static const QgsContrastEnhancement::ContrastEnhancementAlgorithm SINGLE_BAND_ENHANCEMENT_ALGORITHM
Default enhancement algorithm for single band raster.
bool loadDefaultStyle
Sets to true if the default layer style should be loaded.
QgsRasterInterface * at(int idx) const
QString providerType() const
[ data provider interface ] Which provider is being used for this Raster Layer?
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:202
QgsRasterShaderFunction * rasterShaderFunction()
virtual void setExtent(const QgsRectangle &rect)
Sets the extent.
Complex Float32.
Definition: qgis.h:91
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Gets the data source specification.
double cumulativeCutUpper() const
Returns the upper bound of cumulative cut method (between 0 and 1).
QString bandName(int bandNoInt) const
Returns the name of a band given its number.
Constantly updated extent of the canvas is used to compute statistics.
QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
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).
void setEncodedUri(const QByteArray &uri)
set complete encoded uri (generic mode)
Raster renderer pipe for single band pseudocolor.
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
void setRasterShaderFunction(QgsRasterShaderFunction *function)
A public method that allows the user to set their own shader function.
Range is [ mean - stdDevFactor() * stddev, mean + stdDevFactor() * stddev ].
static QString contrastEnhancementAlgorithmString(ContrastEnhancementAlgorithm algorithm)
Returns a string to serialize ContrastEnhancementAlgorithm.
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
bool defaultContrastEnhancementSettings(QgsContrastEnhancement::ContrastEnhancementAlgorithm &myAlgorithm, QgsRasterMinMaxOrigin::Limits &myLimits) const
Returns default contrast enhancement settings for that type of raster.
void dataSourceChanged()
Emitted whenever the layer&#39;s data source has been changed.
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...
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example...
Raster renderer pipe for single band gray.
Complex Int32.
Definition: qgis.h:90
static int typeSize(int dataType)
void setParam(const QString &key, const QString &value)
Set generic param (generic mode)
QgsMapLayerRenderer * createMapRenderer(QgsRenderContext &rendererContext) override
Returns new instance of QgsMapLayerRenderer that will be used for rendering of given context...
Sixteen bit unsigned integer (quint16)
Definition: qgis.h:83
virtual void setLayerOrder(const QStringList &layers)
Reorder the list of layer names to be rendered by this provider (in order from bottom to top) ...
QString decodedSource(const QString &source, const QString &provider, const QgsReadWriteContext &context) const override
Called by readLayerXML(), used by derived classes to decode provider&#39;s specific data source from proj...
void rendererChanged()
Signal emitted when renderer is changed.
double minimumScale() const
Returns the minimum map scale (i.e.
void setAlphaBand(int band)
double maximumScale() const
Returns the maximum map scale (i.e.
bool mValid
Indicates if the layer is valid and can be drawn.
Definition: qgsmaplayer.h:1414
bool writeStyle(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const override
#define cast_to_fptr(f)
Definition: qgis.h:158
void setName(const QString &name)
Set the display name of the layer.
Q_DECL_DEPRECATED void setDataProvider(const QString &provider)
Set the data provider.
Range is [ min + cumulativeCutLower() * (max - min), min + cumulativeCutUpper() * (max - min) ]...
Base class for processing filters like renderers, reprojector, resampler etc.
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
static const QgsRasterMinMaxOrigin::Limits SINGLE_BAND_MIN_MAX_LIMITS
Default enhancement limits for single band raster.
static QString limitsString(Limits limits)
Returns a string to serialize Limits.
void setSubLayerVisibility(const QString &name, bool vis) override
Set the visibility of the given sublayer name.
virtual QString htmlMetadata()=0
Returns metadata in a format suitable for feeding directly into a subset of the GUI raster properties...
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:273
int size() const
static ContrastEnhancementAlgorithm contrastEnhancementAlgorithmFromString(const QString &contrastEnhancementString)
Deserialize ContrastEnhancementAlgorithm.
void classifyColorRamp(int classes=0, int band=-1, const QgsRectangle &extent=QgsRectangle(), QgsRasterInterface *input=nullptr)
Classify color ramp shader.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setMinMaxOrigin(const QgsRasterMinMaxOrigin &origin)
Sets origin of min/max values.
QString identificationSectionHtml() const
Formats the "Identification" section according to a metadata object.
Limits
This enumerator describes the limits used to compute min/max values.
QgsCoordinateReferenceSystem mSrcCRS
Source coordinate system.
void setLimits(QgsRasterMinMaxOrigin::Limits limits)
Sets the limits.
void writeStyleManager(QDomNode &layerNode, QDomDocument &doc) const
Write style manager&#39;s configuration (if exists). To be called by subclasses.
QgsRasterProjector implements approximate projection support for it calculates grid of points in sour...
bool writeSymbology(QDomNode &, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const override
bool set(QgsRasterInterface *interface)
Insert a new known interface in default place or replace interface of the same role if it already exi...
Registry for raster renderer entries.
virtual QgsRasterBandStats bandStatistics(int bandNo, int stats=QgsRasterBandStats::All, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
QList< QPair< QString, QColor > > QgsLegendColorList
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.
void setContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
ContrastEnhancementAlgorithm
This enumerator describes the types of contrast enhancement algorithms that can be used...
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...
Contains information about the context of a rendering operation.
Setting options for creating vector data providers.
QString name() const
Returns the display name of the layer.
static QgsMapLayerLegend * defaultRasterLegend(QgsRasterLayer *rl)
Create new legend implementation for raster layer.
const QgsContrastEnhancement * redContrastEnhancement() const
const QgsContrastEnhancement * greenContrastEnhancement() const
QgsRasterResampleFilter * resampleFilter() const
Sets raster resample filter. Takes ownership of the resample filter object.
bool remove(int idx)
Remove and delete interface at given index if possible.
QList< QgsRasterRange > QgsRasterRangeList
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1417
QString source() const
Returns the source for the layer.
bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) override
virtual QgsRasterInterface * clone() const =0
Clone itself, create deep copy.
Whole raster is used to compute statistics.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
virtual void reloadData()
Reloads the data from the source.
QByteArray encodedUri() const
Returns complete encoded uri (generic mode)
int mHeight
Distance in map units from bottom edge to top edge for the part of the raster that is to be rendered...
Brightness/contrast filter pipe for rasters.
This class represents a coordinate reference system (CRS).
Color and saturation filter pipe for rasters.
void setBlueContrastEnhancement(QgsContrastEnhancement *ce)
Takes ownership.
Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata(...
ContrastEnhancementAlgorithm contrastEnhancementAlgorithm() const
Greyscale.
Definition: qgsraster.h:38
bool isvalidrasterfilename_t(QString const &fileNameQString, QString &retErrMsg)
QStringList subLayers() const override
Returns the sublayers of this layer - useful for providers that manage their own layers, such as WMS.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
double minimumValue
The minimum cell value in the raster band.
Class for metadata formatter.
Renderer for multiband images with the color components.
bool isSpatial() const override
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
void dataChanged()
Data of layer changed.
void appendError(const QgsErrorMessage &error)
Add error message.
Definition: qgsmaplayer.h:1406
virtual QgsRasterRangeList userNoDataValues(int bandNo) const
Returns a list of user no data value ranges.
Base class for utility classes that encapsulate information necessary for rendering of map layers...
Interpolates the color between two class breaks linearly.
Manipulates raster pixel values so that they enhanceContrast or clip into a specified numerical range...
QString encodedSource(const QString &source, const QgsReadWriteContext &context) const override
Called by writeLayerXML(), used by derived classes to encode provider&#39;s specific data source to proje...
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
void refreshRendererIfNeeded(QgsRasterRenderer *rasterRenderer, const QgsRectangle &extent)
Refresh renderer with new extent, if needed.
~QgsRasterLayer() override
This class provides details of the viewable area that a raster will be rendered into.
QDateTime dataTimestamp() const override
Current time stamp of data source.
virtual bool useSourceNoDataValue(int bandNo) const
Returns the source nodata value usage.
Assigns the color of the higher class for every pixel between two class breaks.
virtual void legendSymbologyItems(QList< QPair< QString, QColor > > &symbolItems) const
Gets symbology items if provided by renderer.
double min() const
Returns the minimum value for the range.
virtual QString generateBandName(int bandNumber) const
helper function to create zero padded band names
void reload() override
Synchronises with changes in the datasource.
void setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
virtual void setUserNoDataValue(int bandNo, const QgsRasterRangeList &noData)
Complex Float64.
Definition: qgis.h:92
QgsLegendColorList legendSymbologyItems() const
Returns a list with classification items (Text and color)
virtual QgsRasterDataProvider::ProviderCapabilities providerCapabilities() const
Returns flags containing the supported capabilities of the data provider.
void readXml(const QDomElement &filterElem) override
Sets base class members from xml. Usually called from create() methods of subclasses.
double stdDevFactor() const
Returns the factor f so that the min/max range is [ mean - f * stddev , mean + f * stddev ]...
virtual int xSize() const
Gets raster size.
static QgsRasterRendererRegistry * rasterRendererRegistry()
Returns the application&#39;s raster renderer registry, used for managing raster layer renderers...
Raster renderer pipe that applies colors to a raster.
QgsRasterRendererCreateFunc rendererCreateFunction
static const QgsContrastEnhancement::ContrastEnhancementAlgorithm MULTIPLE_BAND_SINGLE_BYTE_ENHANCEMENT_ALGORITHM
Default enhancement algorithm for multiple band raster of type Byte.
void setError(const QgsError &error)
Sets error message.
Definition: qgsmaplayer.h:1408
QString authid() const
Returns the authority identifier for the CRS.
Eight bit unsigned integer (quint8)
Definition: qgis.h:82
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
Setting options for loading raster layers.
static const QgsRasterMinMaxOrigin::Limits MULTIPLE_BAND_MULTI_BYTE_MIN_MAX_LIMITS
Default enhancement limits for multiple band raster of type different from Byte.
int band() const
Returns the band used by the renderer.
Color, alpha, red, green, blue, 4 bytes the same as QImage::Format_ARGB32.
Definition: qgis.h:93
void readCommonStyle(const QDomElement &layerElement, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read style data common to all layer types.
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:209
virtual int colorInterpretation(int bandNo) const
Returns data type for the band specified by number.
QgsRectangle mDrawnExtent
Intersection of current map extent and layer extent.
Base class for raster data providers.
QgsPointXY mTopLeftPoint
Coordinate (in output device coordinate system) of top left corner of the part of the raster that is ...
QgsRasterLayer()
Constructor. Provider is not set.