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