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