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