QGIS API Documentation  2.99.0-Master (cd0ba91)
qgsmaplayer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmaplayer.cpp - description
3  -------------------
4  begin : Fri Jun 28 2002
5  copyright : (C) 2002 by Gary E.Sherman
6  email : sherman at mrcc.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 
18 
19 #include <QDir>
20 #include <QDomDocument>
21 #include <QDomElement>
22 #include <QDomImplementation>
23 #include <QDomNode>
24 #include <QFile>
25 #include <QFileInfo>
26 #include <QTextStream>
27 #include <QUrl>
28 
29 #include <sqlite3.h>
30 
31 #include "qgssqliteutils.h"
32 
33 #include "qgssqliteutils.h"
34 #include "qgs3drendererregistry.h"
35 #include "qgsabstract3drenderer.h"
36 #include "qgsapplication.h"
38 #include "qgsdatasourceuri.h"
39 #include "qgslogger.h"
40 #include "qgsauthmanager.h"
41 #include "qgsmaplayer.h"
42 #include "qgsmaplayerlegend.h"
44 #include "qgspathresolver.h"
46 #include "qgsproject.h"
47 #include "qgsproviderregistry.h"
48 #include "qgsrasterlayer.h"
49 #include "qgsreadwritecontext.h"
50 #include "qgsrectangle.h"
51 #include "qgsvectorlayer.h"
52 #include "qgsvectordataprovider.h"
53 #include "qgsxmlutils.h"
54 #include "qgssettings.h" // TODO: get rid of it [MD]
55 #include "qgsstringutils.h"
56 
58  const QString &lyrname,
59  const QString &source )
60  : mDataSource( source )
61  , mLayerType( type )
62  , mStyleManager( new QgsMapLayerStyleManager( this ) )
63 {
64  // Set the display name = internal name
65  mLayerName = lyrname;
66 
67  //mShortName.replace( QRegExp( "[\\W]" ), "_" );
68 
69  // Generate the unique ID of this layer
70  QString uuid = QUuid::createUuid().toString();
71  // trim { } from uuid
72  mID = lyrname + '_' + uuid.mid( 1, uuid.length() - 2 );
73 
74  // Tidy the ID up to avoid characters that may cause problems
75  // elsewhere (e.g in some parts of XML). Replaces every non-word
76  // character (word characters are the alphabet, numbers and
77  // underscore) with an underscore.
78  // Note that the first backslashe in the regular expression is
79  // there for the compiler, so the pattern is actually \W
80  mID.replace( QRegExp( "[\\W]" ), QStringLiteral( "_" ) );
81 
82  //set some generous defaults for scale based visibility
83  mMinScale = 0;
84  mMaxScale = 100000000;
85  mScaleBasedVisibility = false;
86 
88  connect( &mRefreshTimer, &QTimer::timeout, this, [ = ] { triggerRepaint( true ); } );
89 }
90 
92 {
93  delete m3DRenderer;
94  delete mLegend;
95  delete mStyleManager;
96 }
97 
98 void QgsMapLayer::clone( QgsMapLayer *layer ) const
99 {
100  layer->setBlendMode( blendMode() );
101 
102  Q_FOREACH ( const QString &s, styleManager()->styles() )
103  {
104  layer->styleManager()->addStyle( s, styleManager()->style( s ) );
105  }
106 
107  layer->setName( name() );
108  layer->setShortName( shortName() );
109  layer->setExtent( extent() );
110  layer->setMaximumScale( maximumScale() );
111  layer->setMinimumScale( minimumScale() );
113  layer->setTitle( title() );
114  layer->setAbstract( abstract() );
115  layer->setKeywordList( keywordList() );
116  layer->setDataUrl( dataUrl() );
117  layer->setDataUrlFormat( dataUrlFormat() );
118  layer->setAttribution( attribution() );
119  layer->setAttributionUrl( attributionUrl() );
120  layer->setMetadataUrl( metadataUrl() );
123  layer->setLegendUrl( legendUrl() );
125  layer->setDependencies( dependencies() );
126  layer->setCrs( crs() );
127  layer->setCustomProperties( mCustomProperties );
128 }
129 
131 {
132  return mLayerType;
133 }
134 
135 QString QgsMapLayer::id() const
136 {
137  return mID;
138 }
139 
140 void QgsMapLayer::setName( const QString &name )
141 {
142  if ( name == mLayerName )
143  return;
144 
145  mLayerName = name;
146 
147  emit nameChanged();
148 }
149 
150 QString QgsMapLayer::name() const
151 {
152  QgsDebugMsgLevel( "returning name '" + mLayerName + '\'', 4 );
153  return mLayerName;
154 }
155 
157 {
158  return nullptr;
159 }
160 
162 {
163  return nullptr;
164 }
165 
167 {
168  // Redo this every time we're asked for it, as we don't know if
169  // dataSource has changed.
170  QString safeName = QgsDataSourceUri::removePassword( mDataSource );
171  return safeName;
172 }
173 
174 QString QgsMapLayer::source() const
175 {
176  return mDataSource;
177 }
178 
180 {
181  return mExtent;
182 }
183 
184 void QgsMapLayer::setBlendMode( QPainter::CompositionMode blendMode )
185 {
186  mBlendMode = blendMode;
187  emit blendModeChanged( blendMode );
188  emit styleChanged();
189 }
190 
191 QPainter::CompositionMode QgsMapLayer::blendMode() const
192 {
193  return mBlendMode;
194 }
195 
196 
197 bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, const QgsReadWriteContext &context )
198 {
199  bool layerError;
200 
201  QDomNode mnl;
202  QDomElement mne;
203 
204  // read provider
205  QString provider;
206  mnl = layerElement.namedItem( QStringLiteral( "provider" ) );
207  mne = mnl.toElement();
208  provider = mne.text();
209 
210  // set data source
211  mnl = layerElement.namedItem( QStringLiteral( "datasource" ) );
212  mne = mnl.toElement();
213  mDataSource = mne.text();
214 
215  // if the layer needs authentication, ensure the master password is set
216  QRegExp rx( "authcfg=([a-z]|[A-Z]|[0-9]){7}" );
217  if ( ( rx.indexIn( mDataSource ) != -1 )
219  {
220  return false;
221  }
222 
223  // TODO: this should go to providers
224  if ( provider == QLatin1String( "spatialite" ) )
225  {
227  uri.setDatabase( context.pathResolver().readPath( uri.database() ) );
228  mDataSource = uri.uri();
229  }
230  else if ( provider == QLatin1String( "ogr" ) )
231  {
232  QStringList theURIParts = mDataSource.split( '|' );
233  theURIParts[0] = context.pathResolver().readPath( theURIParts[0] );
234  mDataSource = theURIParts.join( QStringLiteral( "|" ) );
235  }
236  else if ( provider == QLatin1String( "gpx" ) )
237  {
238  QStringList theURIParts = mDataSource.split( '?' );
239  theURIParts[0] = context.pathResolver().readPath( theURIParts[0] );
240  mDataSource = theURIParts.join( QStringLiteral( "?" ) );
241  }
242  else if ( provider == QLatin1String( "delimitedtext" ) )
243  {
244  QUrl urlSource = QUrl::fromEncoded( mDataSource.toLatin1() );
245 
246  if ( !mDataSource.startsWith( QLatin1String( "file:" ) ) )
247  {
248  QUrl file = QUrl::fromLocalFile( mDataSource.left( mDataSource.indexOf( '?' ) ) );
249  urlSource.setScheme( QStringLiteral( "file" ) );
250  urlSource.setPath( file.path() );
251  }
252 
253  QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().readPath( urlSource.toLocalFile() ) );
254  urlDest.setQueryItems( urlSource.queryItems() );
255  mDataSource = QString::fromLatin1( urlDest.toEncoded() );
256  }
257  else if ( provider == QLatin1String( "wms" ) )
258  {
259  // >>> BACKWARD COMPATIBILITY < 1.9
260  // For project file backward compatibility we must support old format:
261  // 1. mode: <url>
262  // example: http://example.org/wms?
263  // 2. mode: tiled=<width>;<height>;<resolution>;<resolution>...,ignoreUrl=GetMap;GetFeatureInfo,featureCount=<count>,username=<name>,password=<password>,url=<url>
264  // example: tiled=256;256;0.703;0.351,url=http://example.org/tilecache?
265  // example: featureCount=10,http://example.org/wms?
266  // example: ignoreUrl=GetMap;GetFeatureInfo,username=cimrman,password=jara,url=http://example.org/wms?
267  // This is modified version of old QgsWmsProvider::parseUri
268  // The new format has always params crs,format,layers,styles and that params
269  // should not appear in old format url -> use them to identify version
270  // XYZ tile layers do not need to contain crs,format params, but they have type=xyz
271  if ( !mDataSource.contains( QLatin1String( "type=" ) ) &&
272  !mDataSource.contains( QLatin1String( "crs=" ) ) && !mDataSource.contains( QLatin1String( "format=" ) ) )
273  {
274  QgsDebugMsg( "Old WMS URI format detected -> converting to new format" );
275  QgsDataSourceUri uri;
276  if ( !mDataSource.startsWith( QLatin1String( "http:" ) ) )
277  {
278  QStringList parts = mDataSource.split( ',' );
279  QStringListIterator iter( parts );
280  while ( iter.hasNext() )
281  {
282  QString item = iter.next();
283  if ( item.startsWith( QLatin1String( "username=" ) ) )
284  {
285  uri.setParam( QStringLiteral( "username" ), item.mid( 9 ) );
286  }
287  else if ( item.startsWith( QLatin1String( "password=" ) ) )
288  {
289  uri.setParam( QStringLiteral( "password" ), item.mid( 9 ) );
290  }
291  else if ( item.startsWith( QLatin1String( "tiled=" ) ) )
292  {
293  // in < 1.9 tiled= may apper in to variants:
294  // tiled=width;height - non tiled mode, specifies max width and max height
295  // tiled=width;height;resolutions-1;resolution2;... - tile mode
296 
297  QStringList params = item.mid( 6 ).split( ';' );
298 
299  if ( params.size() == 2 ) // non tiled mode
300  {
301  uri.setParam( QStringLiteral( "maxWidth" ), params.takeFirst() );
302  uri.setParam( QStringLiteral( "maxHeight" ), params.takeFirst() );
303  }
304  else if ( params.size() > 2 ) // tiled mode
305  {
306  // resolutions are no more needed and size limit is not used for tiles
307  // we have to tell to the provider however that it is tiled
308  uri.setParam( QStringLiteral( "tileMatrixSet" ), QLatin1String( "" ) );
309  }
310  }
311  else if ( item.startsWith( QLatin1String( "featureCount=" ) ) )
312  {
313  uri.setParam( QStringLiteral( "featureCount" ), item.mid( 13 ) );
314  }
315  else if ( item.startsWith( QLatin1String( "url=" ) ) )
316  {
317  uri.setParam( QStringLiteral( "url" ), item.mid( 4 ) );
318  }
319  else if ( item.startsWith( QLatin1String( "ignoreUrl=" ) ) )
320  {
321  uri.setParam( QStringLiteral( "ignoreUrl" ), item.mid( 10 ).split( ';' ) );
322  }
323  }
324  }
325  else
326  {
327  uri.setParam( QStringLiteral( "url" ), mDataSource );
328  }
329  mDataSource = uri.encodedUri();
330  // At this point, the URI is obviously incomplete, we add additional params
331  // in QgsRasterLayer::readXml
332  }
333  // <<< BACKWARD COMPATIBILITY < 1.9
334  }
335  else
336  {
337  bool handled = false;
338 
339  if ( provider == QLatin1String( "gdal" ) )
340  {
341  if ( mDataSource.startsWith( QLatin1String( "NETCDF:" ) ) )
342  {
343  // NETCDF:filename:variable
344  // filename can be quoted with " as it can contain colons
345  QRegExp r( "NETCDF:(.+):([^:]+)" );
346  if ( r.exactMatch( mDataSource ) )
347  {
348  QString filename = r.cap( 1 );
349  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
350  filename = filename.mid( 1, filename.length() - 2 );
351  mDataSource = "NETCDF:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
352  handled = true;
353  }
354  }
355  else if ( mDataSource.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
356  {
357  // HDF4_SDS:subdataset_type:file_name:subdataset_index
358  // filename can be quoted with " as it can contain colons
359  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
360  if ( r.exactMatch( mDataSource ) )
361  {
362  QString filename = r.cap( 2 );
363  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
364  filename = filename.mid( 1, filename.length() - 2 );
365  mDataSource = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 3 );
366  handled = true;
367  }
368  }
369  else if ( mDataSource.startsWith( QLatin1String( "HDF5:" ) ) )
370  {
371  // HDF5:file_name:subdataset
372  // filename can be quoted with " as it can contain colons
373  QRegExp r( "HDF5:(.+):([^:]+)" );
374  if ( r.exactMatch( mDataSource ) )
375  {
376  QString filename = r.cap( 1 );
377  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
378  filename = filename.mid( 1, filename.length() - 2 );
379  mDataSource = "HDF5:\"" + context.pathResolver().readPath( filename ) + "\":" + r.cap( 2 );
380  handled = true;
381  }
382  }
383  else if ( mDataSource.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
384  {
385  // NITF_IM:0:filename
386  // RADARSAT_2_CALIB:?:filename
387  QRegExp r( "([^:]+):([^:]+):(.+)" );
388  if ( r.exactMatch( mDataSource ) )
389  {
390  mDataSource = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().readPath( r.cap( 3 ) );
391  handled = true;
392  }
393  }
394  }
395 
396  if ( !handled )
398  }
399 
400  // Set the CRS from project file, asking the user if necessary.
401  // Make it the saved CRS to have WMS layer projected correctly.
402  // We will still overwrite whatever GDAL etc picks up anyway
403  // further down this function.
404  mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
405  mne = mnl.toElement();
406 
408  CUSTOM_CRS_VALIDATION savedValidation;
409 
410  QDomNode srsNode = layerElement.namedItem( QStringLiteral( "srs" ) );
411  mCRS.readXml( srsNode );
412  mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
413  mCRS.validate();
414  savedCRS = mCRS;
415 
416  // Do not validate any projections in children, they will be overwritten anyway.
417  // No need to ask the user for a projections when it is overwritten, is there?
420 
421  // read custom properties before passing reading further to a subclass, so that
422  // the subclass can also read custom properties
423  readCustomProperties( layerElement );
424 
425  // now let the children grab what they need from the Dom node.
426  layerError = !readXml( layerElement, context );
427 
428  // overwrite CRS with what we read from project file before the raster/vector
429  // file reading functions changed it. They will if projections is specified in the file.
430  // FIXME: is this necessary?
432  mCRS = savedCRS;
433 
434  // Abort if any error in layer, such as not found.
435  if ( layerError )
436  {
437  return false;
438  }
439 
440  // the internal name is just the data source basename
441  //QFileInfo dataSourceFileInfo( mDataSource );
442  //internalName = dataSourceFileInfo.baseName();
443 
444  // set ID
445  mnl = layerElement.namedItem( QStringLiteral( "id" ) );
446  if ( ! mnl.isNull() )
447  {
448  mne = mnl.toElement();
449  if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
450  {
451  mID = mne.text();
452  }
453  }
454 
455  // use scale dependent visibility flag
456  setScaleBasedVisibility( layerElement.attribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ) ).toInt() == 1 );
457  if ( layerElement.hasAttribute( QStringLiteral( "minimumScale" ) ) )
458  {
459  // older element, when scales were reversed
460  setMaximumScale( layerElement.attribute( QStringLiteral( "minimumScale" ) ).toDouble() );
461  setMinimumScale( layerElement.attribute( QStringLiteral( "maximumScale" ) ).toDouble() );
462  }
463  else
464  {
465  setMaximumScale( layerElement.attribute( QStringLiteral( "maxScale" ) ).toDouble() );
466  setMinimumScale( layerElement.attribute( QStringLiteral( "minScale" ) ).toDouble() );
467  }
468 
469  setAutoRefreshInterval( layerElement.attribute( QStringLiteral( "autoRefreshTime" ), 0 ).toInt() );
470  setAutoRefreshEnabled( layerElement.attribute( QStringLiteral( "autoRefreshEnabled" ), QStringLiteral( "0" ) ).toInt() );
471  setRefreshOnNofifyMessage( layerElement.attribute( QStringLiteral( "refreshOnNotifyMessage" ), QString() ) );
472  setRefreshOnNotifyEnabled( layerElement.attribute( QStringLiteral( "refreshOnNotifyEnabled" ), QStringLiteral( "0" ) ).toInt() );
473 
474 
475  // set name
476  mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
477  mne = mnl.toElement();
478  setName( mne.text() );
479 
480  //short name
481  QDomElement shortNameElem = layerElement.firstChildElement( QStringLiteral( "shortname" ) );
482  if ( !shortNameElem.isNull() )
483  {
484  mShortName = shortNameElem.text();
485  }
486 
487  //title
488  QDomElement titleElem = layerElement.firstChildElement( QStringLiteral( "title" ) );
489  if ( !titleElem.isNull() )
490  {
491  mTitle = titleElem.text();
492  }
493 
494  //abstract
495  QDomElement abstractElem = layerElement.firstChildElement( QStringLiteral( "abstract" ) );
496  if ( !abstractElem.isNull() )
497  {
498  mAbstract = abstractElem.text();
499  }
500 
501  //keywordList
502  QDomElement keywordListElem = layerElement.firstChildElement( QStringLiteral( "keywordList" ) );
503  if ( !keywordListElem.isNull() )
504  {
505  QStringList kwdList;
506  for ( QDomNode n = keywordListElem.firstChild(); !n.isNull(); n = n.nextSibling() )
507  {
508  kwdList << n.toElement().text();
509  }
510  mKeywordList = kwdList.join( QStringLiteral( ", " ) );
511  }
512 
513  //metadataUrl
514  QDomElement dataUrlElem = layerElement.firstChildElement( QStringLiteral( "dataUrl" ) );
515  if ( !dataUrlElem.isNull() )
516  {
517  mDataUrl = dataUrlElem.text();
518  mDataUrlFormat = dataUrlElem.attribute( QStringLiteral( "format" ), QLatin1String( "" ) );
519  }
520 
521  //legendUrl
522  QDomElement legendUrlElem = layerElement.firstChildElement( QStringLiteral( "legendUrl" ) );
523  if ( !legendUrlElem.isNull() )
524  {
525  mLegendUrl = legendUrlElem.text();
526  mLegendUrlFormat = legendUrlElem.attribute( QStringLiteral( "format" ), QLatin1String( "" ) );
527  }
528 
529  //attribution
530  QDomElement attribElem = layerElement.firstChildElement( QStringLiteral( "attribution" ) );
531  if ( !attribElem.isNull() )
532  {
533  mAttribution = attribElem.text();
534  mAttributionUrl = attribElem.attribute( QStringLiteral( "href" ), QLatin1String( "" ) );
535  }
536 
537  //metadataUrl
538  QDomElement metaUrlElem = layerElement.firstChildElement( QStringLiteral( "metadataUrl" ) );
539  if ( !metaUrlElem.isNull() )
540  {
541  mMetadataUrl = metaUrlElem.text();
542  mMetadataUrlType = metaUrlElem.attribute( QStringLiteral( "type" ), QLatin1String( "" ) );
543  mMetadataUrlFormat = metaUrlElem.attribute( QStringLiteral( "format" ), QLatin1String( "" ) );
544  }
545 
546  // mMetadata.readFromLayer( this );
547  QDomElement metadataElem = layerElement.firstChildElement( QStringLiteral( "resourceMetadata" ) );
548  mMetadata.readMetadataXml( metadataElem );
549 
550  QgsAbstract3DRenderer *r3D = nullptr;
551  QDomElement renderer3DElem = layerElement.firstChildElement( QStringLiteral( "renderer-3d" ) );
552  if ( !renderer3DElem.isNull() )
553  {
554  QString type3D = renderer3DElem.attribute( QStringLiteral( "type" ) );
556  if ( meta3D )
557  {
558  r3D = meta3D->createRenderer( renderer3DElem, context );
559  }
560  }
561  setRenderer3D( r3D );
562 
563  return true;
564 } // bool QgsMapLayer::readLayerXML
565 
566 
567 bool QgsMapLayer::readXml( const QDomNode &layer_node, const QgsReadWriteContext &context )
568 {
569  Q_UNUSED( layer_node );
570  Q_UNUSED( context );
571  // NOP by default; children will over-ride with behavior specific to them
572 
573  return true;
574 } // void QgsMapLayer::readXml
575 
576 
577 
578 bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context ) const
579 {
580  // use scale dependent visibility flag
581  layerElement.setAttribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ), hasScaleBasedVisibility() ? 1 : 0 );
582  layerElement.setAttribute( QStringLiteral( "maxScale" ), QString::number( maximumScale() ) );
583  layerElement.setAttribute( QStringLiteral( "minScale" ), QString::number( minimumScale() ) );
584 
585  if ( !extent().isNull() )
586  {
587  layerElement.appendChild( QgsXmlUtils::writeRectangle( mExtent, document ) );
588  }
589 
590  layerElement.setAttribute( QStringLiteral( "autoRefreshTime" ), QString::number( mRefreshTimer.interval() ) );
591  layerElement.setAttribute( QStringLiteral( "autoRefreshEnabled" ), mRefreshTimer.isActive() ? 1 : 0 );
592  layerElement.setAttribute( QStringLiteral( "refreshOnNotifyEnabled" ), mIsRefreshOnNofifyEnabled ? 1 : 0 );
593  layerElement.setAttribute( QStringLiteral( "refreshOnNotifyMessage" ), mRefreshOnNofifyMessage );
594 
595 
596  // ID
597  QDomElement layerId = document.createElement( QStringLiteral( "id" ) );
598  QDomText layerIdText = document.createTextNode( id() );
599  layerId.appendChild( layerIdText );
600 
601  layerElement.appendChild( layerId );
602 
603  // data source
604  QDomElement dataSource = document.createElement( QStringLiteral( "datasource" ) );
605 
606  QString src = source();
607 
608  const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
609  // TODO: what about postgres, mysql and others, they should not go through writePath()
610  if ( vlayer && vlayer->providerType() == QLatin1String( "spatialite" ) )
611  {
612  QgsDataSourceUri uri( src );
613  QString database = context.pathResolver().writePath( uri.database() );
614  uri.setConnection( uri.host(), uri.port(), database, uri.username(), uri.password() );
615  src = uri.uri();
616  }
617  else if ( vlayer && vlayer->providerType() == QLatin1String( "ogr" ) )
618  {
619  QStringList theURIParts = src.split( '|' );
620  theURIParts[0] = context.pathResolver().writePath( theURIParts[0] );
621  src = theURIParts.join( QStringLiteral( "|" ) );
622  }
623  else if ( vlayer && vlayer->providerType() == QLatin1String( "gpx" ) )
624  {
625  QStringList theURIParts = src.split( '?' );
626  theURIParts[0] = context.pathResolver().writePath( theURIParts[0] );
627  src = theURIParts.join( QStringLiteral( "?" ) );
628  }
629  else if ( vlayer && vlayer->providerType() == QLatin1String( "delimitedtext" ) )
630  {
631  QUrl urlSource = QUrl::fromEncoded( src.toLatin1() );
632  QUrl urlDest = QUrl::fromLocalFile( context.pathResolver().writePath( urlSource.toLocalFile() ) );
633  urlDest.setQueryItems( urlSource.queryItems() );
634  src = QString::fromLatin1( urlDest.toEncoded() );
635  }
636  else if ( vlayer && vlayer->providerType() == QLatin1String( "memory" ) )
637  {
638  // Refetch the source from the provider, because adding fields actually changes the source for this provider.
639  src = vlayer->dataProvider()->dataSourceUri();
640  }
641  else
642  {
643  bool handled = false;
644 
645  if ( !vlayer )
646  {
647  const QgsRasterLayer *rlayer = qobject_cast<const QgsRasterLayer *>( this );
648  // Update path for subdataset
649  if ( rlayer && rlayer->providerType() == QLatin1String( "gdal" ) )
650  {
651  if ( src.startsWith( QLatin1String( "NETCDF:" ) ) )
652  {
653  // NETCDF:filename:variable
654  // filename can be quoted with " as it can contain colons
655  QRegExp r( "NETCDF:(.+):([^:]+)" );
656  if ( r.exactMatch( src ) )
657  {
658  QString filename = r.cap( 1 );
659  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
660  filename = filename.mid( 1, filename.length() - 2 );
661  src = "NETCDF:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
662  handled = true;
663  }
664  }
665  else if ( src.startsWith( QLatin1String( "HDF4_SDS:" ) ) )
666  {
667  // HDF4_SDS:subdataset_type:file_name:subdataset_index
668  // filename can be quoted with " as it can contain colons
669  QRegExp r( "HDF4_SDS:([^:]+):(.+):([^:]+)" );
670  if ( r.exactMatch( src ) )
671  {
672  QString filename = r.cap( 2 );
673  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
674  filename = filename.mid( 1, filename.length() - 2 );
675  src = "HDF4_SDS:" + r.cap( 1 ) + ":\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 3 );
676  handled = true;
677  }
678  }
679  else if ( src.startsWith( QLatin1String( "HDF5:" ) ) )
680  {
681  // HDF5:file_name:subdataset
682  // filename can be quoted with " as it can contain colons
683  QRegExp r( "HDF5:(.+):([^:]+)" );
684  if ( r.exactMatch( src ) )
685  {
686  QString filename = r.cap( 1 );
687  if ( filename.startsWith( '"' ) && filename.endsWith( '"' ) )
688  filename = filename.mid( 1, filename.length() - 2 );
689  src = "HDF5:\"" + context.pathResolver().writePath( filename ) + "\":" + r.cap( 2 );
690  handled = true;
691  }
692  }
693  else if ( src.contains( QRegExp( "^(NITF_IM|RADARSAT_2_CALIB):" ) ) )
694  {
695  // NITF_IM:0:filename
696  // RADARSAT_2_CALIB:?:filename
697  QRegExp r( "([^:]+):([^:]+):(.+)" );
698  if ( r.exactMatch( src ) )
699  {
700  src = r.cap( 1 ) + ':' + r.cap( 2 ) + ':' + context.pathResolver().writePath( r.cap( 3 ) );
701  handled = true;
702  }
703  }
704  }
705  }
706 
707  if ( !handled )
708  src = context.pathResolver().writePath( src );
709  }
710 
711  QDomText dataSourceText = document.createTextNode( src );
712  dataSource.appendChild( dataSourceText );
713 
714  layerElement.appendChild( dataSource );
715 
716 
717  // layer name
718  QDomElement layerName = document.createElement( QStringLiteral( "layername" ) );
719  QDomText layerNameText = document.createTextNode( name() );
720  layerName.appendChild( layerNameText );
721  layerElement.appendChild( layerName );
722 
723  // layer short name
724  if ( !mShortName.isEmpty() )
725  {
726  QDomElement layerShortName = document.createElement( QStringLiteral( "shortname" ) );
727  QDomText layerShortNameText = document.createTextNode( mShortName );
728  layerShortName.appendChild( layerShortNameText );
729  layerElement.appendChild( layerShortName );
730  }
731 
732  // layer title
733  if ( !mTitle.isEmpty() )
734  {
735  QDomElement layerTitle = document.createElement( QStringLiteral( "title" ) );
736  QDomText layerTitleText = document.createTextNode( mTitle );
737  layerTitle.appendChild( layerTitleText );
738  layerElement.appendChild( layerTitle );
739  }
740 
741  // layer abstract
742  if ( !mAbstract.isEmpty() )
743  {
744  QDomElement layerAbstract = document.createElement( QStringLiteral( "abstract" ) );
745  QDomText layerAbstractText = document.createTextNode( mAbstract );
746  layerAbstract.appendChild( layerAbstractText );
747  layerElement.appendChild( layerAbstract );
748  }
749 
750  // layer keyword list
751  QStringList keywordStringList = keywordList().split( ',' );
752  if ( !keywordStringList.isEmpty() )
753  {
754  QDomElement layerKeywordList = document.createElement( QStringLiteral( "keywordList" ) );
755  for ( int i = 0; i < keywordStringList.size(); ++i )
756  {
757  QDomElement layerKeywordValue = document.createElement( QStringLiteral( "value" ) );
758  QDomText layerKeywordText = document.createTextNode( keywordStringList.at( i ).trimmed() );
759  layerKeywordValue.appendChild( layerKeywordText );
760  layerKeywordList.appendChild( layerKeywordValue );
761  }
762  layerElement.appendChild( layerKeywordList );
763  }
764 
765  // layer metadataUrl
766  QString aDataUrl = dataUrl();
767  if ( !aDataUrl.isEmpty() )
768  {
769  QDomElement layerDataUrl = document.createElement( QStringLiteral( "dataUrl" ) );
770  QDomText layerDataUrlText = document.createTextNode( aDataUrl );
771  layerDataUrl.appendChild( layerDataUrlText );
772  layerDataUrl.setAttribute( QStringLiteral( "format" ), dataUrlFormat() );
773  layerElement.appendChild( layerDataUrl );
774  }
775 
776  // layer legendUrl
777  QString aLegendUrl = legendUrl();
778  if ( !aLegendUrl.isEmpty() )
779  {
780  QDomElement layerLegendUrl = document.createElement( QStringLiteral( "legendUrl" ) );
781  QDomText layerLegendUrlText = document.createTextNode( aLegendUrl );
782  layerLegendUrl.appendChild( layerLegendUrlText );
783  layerLegendUrl.setAttribute( QStringLiteral( "format" ), legendUrlFormat() );
784  layerElement.appendChild( layerLegendUrl );
785  }
786 
787  // layer attribution
788  QString aAttribution = attribution();
789  if ( !aAttribution.isEmpty() )
790  {
791  QDomElement layerAttribution = document.createElement( QStringLiteral( "attribution" ) );
792  QDomText layerAttributionText = document.createTextNode( aAttribution );
793  layerAttribution.appendChild( layerAttributionText );
794  layerAttribution.setAttribute( QStringLiteral( "href" ), attributionUrl() );
795  layerElement.appendChild( layerAttribution );
796  }
797 
798  // layer metadataUrl
799  QString aMetadataUrl = metadataUrl();
800  if ( !aMetadataUrl.isEmpty() )
801  {
802  QDomElement layerMetadataUrl = document.createElement( QStringLiteral( "metadataUrl" ) );
803  QDomText layerMetadataUrlText = document.createTextNode( aMetadataUrl );
804  layerMetadataUrl.appendChild( layerMetadataUrlText );
805  layerMetadataUrl.setAttribute( QStringLiteral( "type" ), metadataUrlType() );
806  layerMetadataUrl.setAttribute( QStringLiteral( "format" ), metadataUrlFormat() );
807  layerElement.appendChild( layerMetadataUrl );
808  }
809 
810  // timestamp if supported
811  if ( timestamp() > QDateTime() )
812  {
813  QDomElement stamp = document.createElement( QStringLiteral( "timestamp" ) );
814  QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
815  stamp.appendChild( stampText );
816  layerElement.appendChild( stamp );
817  }
818 
819  layerElement.appendChild( layerName );
820 
821  // zorder
822  // This is no longer stored in the project file. It is superfluous since the layers
823  // are written and read in the proper order.
824 
825  // spatial reference system id
826  QDomElement mySrsElement = document.createElement( QStringLiteral( "srs" ) );
827  mCRS.writeXml( mySrsElement, document );
828  layerElement.appendChild( mySrsElement );
829 
830  // layer metadata
831  QDomElement myMetadataElem = document.createElement( QStringLiteral( "resourceMetadata" ) );
832  mMetadata.writeMetadataXml( myMetadataElem, document );
833  layerElement.appendChild( myMetadataElem );
834 
835  if ( m3DRenderer )
836  {
837  QDomElement renderer3DElem = document.createElement( QStringLiteral( "renderer-3d" ) );
838  renderer3DElem.setAttribute( QStringLiteral( "type" ), m3DRenderer->type() );
839  m3DRenderer->writeXml( renderer3DElem, context );
840  layerElement.appendChild( renderer3DElem );
841  }
842 
843  // now append layer node to map layer node
844 
845  writeCustomProperties( layerElement, document );
846 
847  return writeXml( layerElement, document, context );
848 
849 }
850 
851 
852 bool QgsMapLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
853 {
854  Q_UNUSED( layer_node );
855  Q_UNUSED( document );
856  Q_UNUSED( context );
857  // NOP by default; children will over-ride with behavior specific to them
858 
859  return true;
860 } // void QgsMapLayer::writeXml
861 
863 {
864  if ( m3DRenderer )
865  m3DRenderer->resolveReferences( *project );
866 }
867 
868 
869 void QgsMapLayer::readCustomProperties( const QDomNode &layerNode, const QString &keyStartsWith )
870 {
871  mCustomProperties.readXml( layerNode, keyStartsWith );
872 }
873 
874 void QgsMapLayer::writeCustomProperties( QDomNode &layerNode, QDomDocument &doc ) const
875 {
876  mCustomProperties.writeXml( layerNode, doc );
877 }
878 
879 void QgsMapLayer::readStyleManager( const QDomNode &layerNode )
880 {
881  QDomElement styleMgrElem = layerNode.firstChildElement( QStringLiteral( "map-layer-style-manager" ) );
882  if ( !styleMgrElem.isNull() )
883  mStyleManager->readXml( styleMgrElem );
884  else
885  mStyleManager->reset();
886 }
887 
888 void QgsMapLayer::writeStyleManager( QDomNode &layerNode, QDomDocument &doc ) const
889 {
890  if ( mStyleManager )
891  {
892  QDomElement styleMgrElem = doc.createElement( QStringLiteral( "map-layer-style-manager" ) );
893  mStyleManager->writeXml( styleMgrElem );
894  layerNode.appendChild( styleMgrElem );
895  }
896 }
897 
899 {
900  return mValid;
901 }
902 
903 #if 0
904 void QgsMapLayer::connectNotify( const char *signal )
905 {
906  Q_UNUSED( signal );
907  QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
908 } // QgsMapLayer::connectNotify
909 #endif
910 
911 bool QgsMapLayer::isInScaleRange( double scale ) const
912 {
913  return !mScaleBasedVisibility ||
914  ( ( mMinScale == 0 || mMinScale * Qgis::SCALE_PRECISION < scale )
915  && ( mMaxScale == 0 || scale < mMaxScale ) );
916 }
917 
919 {
920  return mScaleBasedVisibility;
921 }
922 
924 {
925  return mRefreshTimer.isActive();
926 }
927 
929 {
930  return mRefreshTimer.interval();
931 }
932 
934 {
935  if ( interval <= 0 )
936  {
937  mRefreshTimer.stop();
938  mRefreshTimer.setInterval( 0 );
939  }
940  else
941  {
942  mRefreshTimer.setInterval( interval );
943  }
944  emit autoRefreshIntervalChanged( mRefreshTimer.isActive() ? mRefreshTimer.interval() : 0 );
945 }
946 
948 {
949  if ( !enabled )
950  mRefreshTimer.stop();
951  else if ( mRefreshTimer.interval() > 0 )
952  mRefreshTimer.start();
953 
954  emit autoRefreshIntervalChanged( mRefreshTimer.isActive() ? mRefreshTimer.interval() : 0 );
955 }
956 
958 {
959  return mMetadata;
960 }
961 
962 void QgsMapLayer::setMaximumScale( double scale )
963 {
964  mMinScale = scale;
965 }
966 
968 {
969  return mMinScale;
970 }
971 
972 
973 void QgsMapLayer::setMinimumScale( double scale )
974 {
975  mMaxScale = scale;
976 }
977 
978 void QgsMapLayer::setScaleBasedVisibility( const bool enabled )
979 {
980  mScaleBasedVisibility = enabled;
981 }
982 
984 {
985  return mMaxScale;
986 }
987 
988 QStringList QgsMapLayer::subLayers() const
989 {
990  return QStringList(); // Empty
991 }
992 
993 void QgsMapLayer::setLayerOrder( const QStringList &layers )
994 {
995  Q_UNUSED( layers );
996  // NOOP
997 }
998 
999 void QgsMapLayer::setSubLayerVisibility( const QString &name, bool vis )
1000 {
1001  Q_UNUSED( name );
1002  Q_UNUSED( vis );
1003  // NOOP
1004 }
1005 
1007 {
1008  return mCRS;
1009 }
1010 
1011 void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem &srs, bool emitSignal )
1012 {
1013  mCRS = srs;
1014 
1015  if ( !mCRS.isValid() )
1016  {
1017  mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( name() ) );
1018  mCRS.validate();
1019  }
1020 
1021  if ( emitSignal )
1022  emit crsChanged();
1023 }
1024 
1025 QString QgsMapLayer::formatLayerName( const QString &name )
1026 {
1027  QString layerName( name );
1028  layerName.replace( '_', ' ' );
1030  return layerName;
1031 }
1032 
1033 QString QgsMapLayer::styleURI() const
1034 {
1035  QString myURI = publicSource();
1036 
1037  // if file is using the VSIFILE mechanism, remove the prefix
1038  if ( myURI.startsWith( QLatin1String( "/vsigzip/" ), Qt::CaseInsensitive ) )
1039  {
1040  myURI.remove( 0, 9 );
1041  }
1042  else if ( myURI.startsWith( QLatin1String( "/vsizip/" ), Qt::CaseInsensitive ) &&
1043  myURI.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) )
1044  {
1045  // ideally we should look for .qml file inside zip file
1046  myURI.remove( 0, 8 );
1047  }
1048  else if ( myURI.startsWith( QLatin1String( "/vsitar/" ), Qt::CaseInsensitive ) &&
1049  ( myURI.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) ||
1050  myURI.endsWith( QLatin1String( ".tar.gz" ), Qt::CaseInsensitive ) ||
1051  myURI.endsWith( QLatin1String( ".tgz" ), Qt::CaseInsensitive ) ) )
1052  {
1053  // ideally we should look for .qml file inside tar file
1054  myURI.remove( 0, 8 );
1055  }
1056 
1057  QFileInfo myFileInfo( myURI );
1058  QString key;
1059 
1060  if ( myFileInfo.exists() )
1061  {
1062  // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name
1063  if ( myURI.endsWith( QLatin1String( ".gz" ), Qt::CaseInsensitive ) )
1064  myURI.chop( 3 );
1065  else if ( myURI.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) )
1066  myURI.chop( 4 );
1067  else if ( myURI.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
1068  myURI.chop( 4 );
1069  else if ( myURI.endsWith( QLatin1String( ".tar.gz" ), Qt::CaseInsensitive ) )
1070  myURI.chop( 7 );
1071  else if ( myURI.endsWith( QLatin1String( ".tgz" ), Qt::CaseInsensitive ) )
1072  myURI.chop( 4 );
1073  myFileInfo.setFile( myURI );
1074  // get the file name for our .qml style file
1075  key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
1076  }
1077  else
1078  {
1079  key = publicSource();
1080  }
1081 
1082  return key;
1083 }
1084 
1085 QString QgsMapLayer::loadDefaultStyle( bool &resultFlag )
1086 {
1087  return loadNamedStyle( styleURI(), resultFlag );
1088 }
1089 
1090 bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &uri, QString &qml )
1091 {
1092  QgsDebugMsgLevel( QString( "db = %1 uri = %2" ).arg( db, uri ), 4 );
1093 
1094  bool resultFlag = false;
1095 
1096  // read from database
1097  sqlite3_database_unique_ptr database;
1098  sqlite3_statement_unique_ptr statement;
1099 
1100  int myResult;
1101 
1102  QgsDebugMsgLevel( QString( "Trying to load style for \"%1\" from \"%2\"" ).arg( uri, db ), 4 );
1103 
1104  if ( db.isEmpty() || !QFile( db ).exists() )
1105  return false;
1106 
1107  myResult = database.open_v2( db, SQLITE_OPEN_READONLY, nullptr );
1108  if ( myResult != SQLITE_OK )
1109  {
1110  return false;
1111  }
1112 
1113  QString mySql = QStringLiteral( "select qml from tbl_styles where style=?" );
1114  statement = database.prepare( mySql, myResult );
1115  if ( myResult == SQLITE_OK )
1116  {
1117  QByteArray param = uri.toUtf8();
1118 
1119  if ( sqlite3_bind_text( statement.get(), 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
1120  sqlite3_step( statement.get() ) == SQLITE_ROW )
1121  {
1122  qml = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( statement.get(), 0 ) ) );
1123  resultFlag = true;
1124  }
1125  }
1126  return resultFlag;
1127 }
1128 
1129 
1130 QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag )
1131 {
1132  QgsDebugMsgLevel( QString( "uri = %1 myURI = %2" ).arg( uri, publicSource() ), 4 );
1133 
1134  resultFlag = false;
1135 
1136  QDomDocument myDocument( QStringLiteral( "qgis" ) );
1137 
1138  // location of problem associated with errorMsg
1139  int line, column;
1140  QString myErrorMessage;
1141 
1142  QFile myFile( uri );
1143  if ( myFile.open( QFile::ReadOnly ) )
1144  {
1145  // read file
1146  resultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
1147  if ( !resultFlag )
1148  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1149  myFile.close();
1150  }
1151  else
1152  {
1153  QFileInfo project( QgsProject::instance()->fileName() );
1154  QgsDebugMsgLevel( QString( "project fileName: %1" ).arg( project.absoluteFilePath() ), 4 );
1155 
1156  QString qml;
1157  if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, qml ) ||
1158  ( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, qml ) ) ||
1159  loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, qml ) )
1160  {
1161  resultFlag = myDocument.setContent( qml, &myErrorMessage, &line, &column );
1162  if ( !resultFlag )
1163  {
1164  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1165  }
1166  }
1167  else
1168  {
1169  myErrorMessage = tr( "Style not found in database" );
1170  }
1171  }
1172 
1173  if ( !resultFlag )
1174  {
1175  return myErrorMessage;
1176  }
1177 
1178  resultFlag = importNamedStyle( myDocument, myErrorMessage );
1179  if ( !resultFlag )
1180  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1181 
1182  return myErrorMessage;
1183 }
1184 
1185 
1186 bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMessage )
1187 {
1188  QDomElement myRoot = myDocument.firstChildElement( QStringLiteral( "qgis" ) );
1189  if ( myRoot.isNull() )
1190  {
1191  myErrorMessage = tr( "Root <qgis> element could not be found" );
1192  return false;
1193  }
1194 
1195  // get style file version string, if any
1196  QgsProjectVersion fileVersion( myRoot.attribute( QStringLiteral( "version" ) ) );
1197  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
1198 
1199  if ( thisVersion > fileVersion )
1200  {
1201  QgsProjectFileTransform styleFile( myDocument, fileVersion );
1202  styleFile.updateRevision( thisVersion );
1203  }
1204 
1205  //Test for matching geometry type on vector layers when applying, if geometry type is given in the style
1206  if ( type() == QgsMapLayer::VectorLayer && !myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).isNull() )
1207  {
1208  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( this );
1209  QgsWkbTypes::GeometryType importLayerGeometryType = static_cast<QgsWkbTypes::GeometryType>( myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).text().toInt() );
1210  if ( vl->geometryType() != importLayerGeometryType )
1211  {
1212  myErrorMessage = tr( "Cannot apply style to layer with a different geometry type" );
1213  return false;
1214  }
1215  }
1216 
1217  // use scale dependent visibility flag
1218  setScaleBasedVisibility( myRoot.attribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ) ).toInt() == 1 );
1219  if ( myRoot.hasAttribute( QStringLiteral( "minimumScale" ) ) )
1220  {
1221  //older scale element, when min/max were reversed
1222  setMaximumScale( myRoot.attribute( QStringLiteral( "minimumScale" ) ).toDouble() );
1223  setMinimumScale( myRoot.attribute( QStringLiteral( "maximumScale" ) ).toDouble() );
1224  }
1225  else
1226  {
1227  setMaximumScale( myRoot.attribute( QStringLiteral( "maxScale" ) ).toDouble() );
1228  setMinimumScale( myRoot.attribute( QStringLiteral( "minScale" ) ).toDouble() );
1229  }
1230 
1231  return readSymbology( myRoot, myErrorMessage, QgsReadWriteContext() ); // TODO: support relative paths in QML?
1232 }
1233 
1234 void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg ) const
1235 {
1236  QDomImplementation DomImplementation;
1237  QDomDocumentType documentType = DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1238  QDomDocument myDocument( documentType );
1239 
1240  QDomElement myRootNode = myDocument.createElement( QStringLiteral( "qgis" ) );
1241  myRootNode.setAttribute( QStringLiteral( "version" ), Qgis::QGIS_VERSION );
1242  myDocument.appendChild( myRootNode );
1243 
1244  myRootNode.setAttribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ), hasScaleBasedVisibility() ? 1 : 0 );
1245  myRootNode.setAttribute( QStringLiteral( "maxScale" ), QString::number( maximumScale() ) );
1246  myRootNode.setAttribute( QStringLiteral( "mincale" ), QString::number( minimumScale() ) );
1247 
1248  if ( !writeSymbology( myRootNode, myDocument, errorMsg, QgsReadWriteContext() ) ) // TODO: support relative paths in QML?
1249  {
1250  errorMsg = QObject::tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1251  return;
1252  }
1253 
1254  /*
1255  * Check to see if the layer is vector - in which case we should also export its geometryType
1256  * to avoid eventually pasting to a layer with a different geometry
1257  */
1258  if ( type() == QgsMapLayer::VectorLayer )
1259  {
1260  //Getting the selectionLayer geometry
1261  const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( this );
1262  QString geoType = QString::number( vl->geometryType() );
1263 
1264  //Adding geometryinformation
1265  QDomElement layerGeometryType = myDocument.createElement( QStringLiteral( "layerGeometryType" ) );
1266  QDomText type = myDocument.createTextNode( geoType );
1267 
1268  layerGeometryType.appendChild( type );
1269  myRootNode.appendChild( layerGeometryType );
1270  }
1271 
1272  doc = myDocument;
1273 }
1274 
1275 QString QgsMapLayer::saveDefaultStyle( bool &resultFlag )
1276 {
1277  return saveNamedStyle( styleURI(), resultFlag );
1278 }
1279 
1280 QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag )
1281 {
1282  QString myErrorMessage;
1283  QDomDocument myDocument;
1284  exportNamedStyle( myDocument, myErrorMessage );
1285 
1286  // check if the uri is a file or ends with .qml,
1287  // which indicates that it should become one
1288  // everything else goes to the database
1289  QString filename;
1290 
1291  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1292  if ( vlayer && vlayer->providerType() == QLatin1String( "ogr" ) )
1293  {
1294  QStringList theURIParts = uri.split( '|' );
1295  filename = theURIParts[0];
1296  }
1297  else if ( vlayer && vlayer->providerType() == QLatin1String( "gpx" ) )
1298  {
1299  QStringList theURIParts = uri.split( '?' );
1300  filename = theURIParts[0];
1301  }
1302  else if ( vlayer && vlayer->providerType() == QLatin1String( "delimitedtext" ) )
1303  {
1304  filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
1305  // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
1306  if ( filename.isEmpty() )
1307  filename = uri;
1308  }
1309  else
1310  {
1311  filename = uri;
1312  }
1313 
1314  QFileInfo myFileInfo( filename );
1315  if ( myFileInfo.exists() || filename.endsWith( QLatin1String( ".qml" ), Qt::CaseInsensitive ) )
1316  {
1317  QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1318  if ( !myDirInfo.isWritable() )
1319  {
1320  return tr( "The directory containing your dataset needs to be writable!" );
1321  }
1322 
1323  // now construct the file name for our .qml style file
1324  QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".qml";
1325 
1326  QFile myFile( myFileName );
1327  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1328  {
1329  QTextStream myFileStream( &myFile );
1330  // save as utf-8 with 2 spaces for indents
1331  myDocument.save( myFileStream, 2 );
1332  myFile.close();
1333  resultFlag = true;
1334  return tr( "Created default style file as %1" ).arg( myFileName );
1335  }
1336  else
1337  {
1338  resultFlag = false;
1339  return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
1340  }
1341  }
1342  else
1343  {
1344  QString qml = myDocument.toString();
1345 
1346  // read from database
1347  sqlite3_database_unique_ptr database;
1348  sqlite3_statement_unique_ptr statement;
1349 
1350  int myResult = database.open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ) );
1351  if ( myResult != SQLITE_OK )
1352  {
1353  return tr( "User database could not be opened." );
1354  }
1355 
1356  QByteArray param0 = uri.toUtf8();
1357  QByteArray param1 = qml.toUtf8();
1358 
1359  QString mySql = QStringLiteral( "create table if not exists tbl_styles(style varchar primary key,qml varchar)" );
1360  statement = database.prepare( mySql, myResult );
1361  if ( myResult == SQLITE_OK )
1362  {
1363  if ( sqlite3_step( statement.get() ) != SQLITE_DONE )
1364  {
1365  resultFlag = false;
1366  return tr( "The style table could not be created." );
1367  }
1368  }
1369 
1370  mySql = QStringLiteral( "insert into tbl_styles(style,qml) values (?,?)" );
1371  statement = database.prepare( mySql, myResult );
1372  if ( myResult == SQLITE_OK )
1373  {
1374  if ( sqlite3_bind_text( statement.get(), 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1375  sqlite3_bind_text( statement.get(), 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1376  sqlite3_step( statement.get() ) == SQLITE_DONE )
1377  {
1378  resultFlag = true;
1379  myErrorMessage = tr( "The style %1 was saved to database" ).arg( uri );
1380  }
1381  }
1382 
1383  if ( !resultFlag )
1384  {
1385  QString mySql = QStringLiteral( "update tbl_styles set qml=? where style=?" );
1386  statement = database.prepare( mySql, myResult );
1387 
1388  if ( myResult == SQLITE_OK )
1389  {
1390  if ( sqlite3_bind_text( statement.get(), 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1391  sqlite3_bind_text( statement.get(), 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1392  sqlite3_step( statement.get() ) == SQLITE_DONE )
1393  {
1394  resultFlag = true;
1395  myErrorMessage = tr( "The style %1 was updated in the database." ).arg( uri );
1396  }
1397  else
1398  {
1399  resultFlag = false;
1400  myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( uri );
1401  }
1402  }
1403  else
1404  {
1405  resultFlag = false;
1406  myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( uri );
1407  }
1408  }
1409  }
1410 
1411  return myErrorMessage;
1412 }
1413 
1414 void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
1415 {
1416  QDomDocument myDocument = QDomDocument();
1417 
1418  QDomNode header = myDocument.createProcessingInstruction( QStringLiteral( "xml" ), QStringLiteral( "version=\"1.0\" encoding=\"UTF-8\"" ) );
1419  myDocument.appendChild( header );
1420 
1421  // Create the root element
1422  QDomElement root = myDocument.createElementNS( QStringLiteral( "http://www.opengis.net/sld" ), QStringLiteral( "StyledLayerDescriptor" ) );
1423  root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.1.0" ) );
1424  root.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" ) );
1425  root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
1426  root.setAttribute( QStringLiteral( "xmlns:se" ), QStringLiteral( "http://www.opengis.net/se" ) );
1427  root.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1428  root.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
1429  myDocument.appendChild( root );
1430 
1431  // Create the NamedLayer element
1432  QDomElement namedLayerNode = myDocument.createElement( QStringLiteral( "NamedLayer" ) );
1433  root.appendChild( namedLayerNode );
1434 
1435  const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
1436  if ( !vlayer )
1437  {
1438  errorMsg = tr( "Could not save symbology because:\n%1" )
1439  .arg( QStringLiteral( "Non-vector layers not supported yet" ) );
1440  return;
1441  }
1442 
1443  QgsStringMap props;
1444  if ( hasScaleBasedVisibility() )
1445  {
1446  props[ QStringLiteral( "scaleMinDenom" )] = QString::number( mMinScale );
1447  props[ QStringLiteral( "scaleMaxDenom" )] = QString::number( mMaxScale );
1448  }
1449  if ( !vlayer->writeSld( namedLayerNode, myDocument, errorMsg, props ) )
1450  {
1451  errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1452  return;
1453  }
1454 
1455  doc = myDocument;
1456 }
1457 
1458 QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const
1459 {
1460  QString errorMsg;
1461  QDomDocument myDocument;
1462  exportSldStyle( myDocument, errorMsg );
1463  if ( !errorMsg.isNull() )
1464  {
1465  resultFlag = false;
1466  return errorMsg;
1467  }
1468  const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
1469 
1470  // check if the uri is a file or ends with .sld,
1471  // which indicates that it should become one
1472  QString filename;
1473  if ( vlayer->providerType() == QLatin1String( "ogr" ) )
1474  {
1475  QStringList theURIParts = uri.split( '|' );
1476  filename = theURIParts[0];
1477  }
1478  else if ( vlayer->providerType() == QLatin1String( "gpx" ) )
1479  {
1480  QStringList theURIParts = uri.split( '?' );
1481  filename = theURIParts[0];
1482  }
1483  else if ( vlayer->providerType() == QLatin1String( "delimitedtext" ) )
1484  {
1485  filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
1486  // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
1487  if ( filename.isEmpty() )
1488  filename = uri;
1489  }
1490  else
1491  {
1492  filename = uri;
1493  }
1494 
1495  QFileInfo myFileInfo( filename );
1496  if ( myFileInfo.exists() || filename.endsWith( QLatin1String( ".sld" ), Qt::CaseInsensitive ) )
1497  {
1498  QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1499  if ( !myDirInfo.isWritable() )
1500  {
1501  return tr( "The directory containing your dataset needs to be writable!" );
1502  }
1503 
1504  // now construct the file name for our .sld style file
1505  QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";
1506 
1507  QFile myFile( myFileName );
1508  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1509  {
1510  QTextStream myFileStream( &myFile );
1511  // save as utf-8 with 2 spaces for indents
1512  myDocument.save( myFileStream, 2 );
1513  myFile.close();
1514  resultFlag = true;
1515  return tr( "Created default style file as %1" ).arg( myFileName );
1516  }
1517  }
1518 
1519  resultFlag = false;
1520  return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
1521 }
1522 
1523 QString QgsMapLayer::loadSldStyle( const QString &uri, bool &resultFlag )
1524 {
1525  resultFlag = false;
1526 
1527  QDomDocument myDocument;
1528 
1529  // location of problem associated with errorMsg
1530  int line, column;
1531  QString myErrorMessage;
1532 
1533  QFile myFile( uri );
1534  if ( myFile.open( QFile::ReadOnly ) )
1535  {
1536  // read file
1537  resultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
1538  if ( !resultFlag )
1539  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1540  myFile.close();
1541  }
1542  else
1543  {
1544  myErrorMessage = tr( "Unable to open file %1" ).arg( uri );
1545  }
1546 
1547  if ( !resultFlag )
1548  {
1549  return myErrorMessage;
1550  }
1551 
1552  // check for root SLD element
1553  QDomElement myRoot = myDocument.firstChildElement( QStringLiteral( "StyledLayerDescriptor" ) );
1554  if ( myRoot.isNull() )
1555  {
1556  myErrorMessage = QStringLiteral( "Error: StyledLayerDescriptor element not found in %1" ).arg( uri );
1557  resultFlag = false;
1558  return myErrorMessage;
1559  }
1560 
1561  // now get the style node out and pass it over to the layer
1562  // to deserialise...
1563  QDomElement namedLayerElem = myRoot.firstChildElement( QStringLiteral( "NamedLayer" ) );
1564  if ( namedLayerElem.isNull() )
1565  {
1566  myErrorMessage = QStringLiteral( "Info: NamedLayer element not found." );
1567  resultFlag = false;
1568  return myErrorMessage;
1569  }
1570 
1571  QString errorMsg;
1572  resultFlag = readSld( namedLayerElem, errorMsg );
1573  if ( !resultFlag )
1574  {
1575  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, errorMsg );
1576  return myErrorMessage;
1577  }
1578 
1579  return QLatin1String( "" );
1580 }
1581 
1582 bool QgsMapLayer::readStyle( const QDomNode &node, QString &errorMessage, const QgsReadWriteContext &context )
1583 {
1584  Q_UNUSED( node );
1585  Q_UNUSED( errorMessage );
1586  Q_UNUSED( context );
1587  return false;
1588 }
1589 
1590 bool QgsMapLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const
1591 {
1592  Q_UNUSED( node );
1593  Q_UNUSED( doc );
1594  Q_UNUSED( errorMessage );
1595  Q_UNUSED( context );
1596  return false;
1597 }
1598 
1599 
1601 {
1602  return &mUndoStack;
1603 }
1604 
1606 {
1607  return &mUndoStackStyles;
1608 }
1609 
1610 
1612 {
1613  return mCustomProperties.keys();
1614 }
1615 
1616 void QgsMapLayer::setCustomProperty( const QString &key, const QVariant &value )
1617 {
1618  mCustomProperties.setValue( key, value );
1619 }
1620 
1622 {
1623  mCustomProperties = properties;
1624 }
1625 
1626 QVariant QgsMapLayer::customProperty( const QString &value, const QVariant &defaultValue ) const
1627 {
1628  return mCustomProperties.value( value, defaultValue );
1629 }
1630 
1631 void QgsMapLayer::removeCustomProperty( const QString &key )
1632 {
1633  mCustomProperties.remove( key );
1634 }
1635 
1637 {
1638  return mError;
1639 }
1640 
1641 
1642 
1644 {
1645  return false;
1646 }
1647 
1649 {
1650  return true;
1651 }
1652 
1653 void QgsMapLayer::setValid( bool valid )
1654 {
1655  mValid = valid;
1656 }
1657 
1659 {
1660  if ( legend == mLegend )
1661  return;
1662 
1663  delete mLegend;
1664  mLegend = legend;
1665 
1666  if ( mLegend )
1667  connect( mLegend, &QgsMapLayerLegend::itemsChanged, this, &QgsMapLayer::legendChanged );
1668 
1669  emit legendChanged();
1670 }
1671 
1673 {
1674  return mLegend;
1675 }
1676 
1678 {
1679  return mStyleManager;
1680 }
1681 
1683 {
1684  if ( renderer == m3DRenderer )
1685  return;
1686 
1687  delete m3DRenderer;
1688  m3DRenderer = renderer;
1689  emit renderer3DChanged();
1690 }
1691 
1693 {
1694  return m3DRenderer;
1695 }
1696 
1697 void QgsMapLayer::triggerRepaint( bool deferredUpdate )
1698 {
1699  emit repaintRequested( deferredUpdate );
1700 }
1701 
1703 {
1704  mMetadata = metadata;
1705 // mMetadata.saveToLayer( this );
1706  emit metadataChanged();
1707 }
1708 
1710 {
1711  return QString();
1712 }
1713 
1714 QDateTime QgsMapLayer::timestamp() const
1715 {
1716  return QDateTime();
1717 }
1718 
1720 {
1721  emit styleChanged();
1722 }
1723 
1725 {
1726  mExtent = r;
1727 }
1728 
1729 static QList<const QgsMapLayer *> _depOutEdges( const QgsMapLayer *vl, const QgsMapLayer *that, const QSet<QgsMapLayerDependency> &layers )
1730 {
1731  QList<const QgsMapLayer *> lst;
1732  if ( vl == that )
1733  {
1734  Q_FOREACH ( const QgsMapLayerDependency &dep, layers )
1735  {
1736  if ( const QgsMapLayer *l = QgsProject::instance()->mapLayer( dep.layerId() ) )
1737  lst << l;
1738  }
1739  }
1740  else
1741  {
1742  Q_FOREACH ( const QgsMapLayerDependency &dep, vl->dependencies() )
1743  {
1744  if ( const QgsMapLayer *l = QgsProject::instance()->mapLayer( dep.layerId() ) )
1745  lst << l;
1746  }
1747  }
1748  return lst;
1749 }
1750 
1751 static bool _depHasCycleDFS( const QgsMapLayer *n, QHash<const QgsMapLayer *, int> &mark, const QgsMapLayer *that, const QSet<QgsMapLayerDependency> &layers )
1752 {
1753  if ( mark.value( n ) == 1 ) // temporary
1754  return true;
1755  if ( mark.value( n ) == 0 ) // not visited
1756  {
1757  mark[n] = 1; // temporary
1758  Q_FOREACH ( const QgsMapLayer *m, _depOutEdges( n, that, layers ) )
1759  {
1760  if ( _depHasCycleDFS( m, mark, that, layers ) )
1761  return true;
1762  }
1763  mark[n] = 2; // permanent
1764  }
1765  return false;
1766 }
1767 
1768 bool QgsMapLayer::hasDependencyCycle( const QSet<QgsMapLayerDependency> &layers ) const
1769 {
1770  QHash<const QgsMapLayer *, int> marks;
1771  return _depHasCycleDFS( this, marks, this, layers );
1772 }
1773 
1774 bool QgsMapLayer::isReadOnly() const
1775 {
1776  return true;
1777 }
1778 
1779 QSet<QgsMapLayerDependency> QgsMapLayer::dependencies() const
1780 {
1781  return mDependencies;
1782 }
1783 
1784 bool QgsMapLayer::setDependencies( const QSet<QgsMapLayerDependency> &oDeps )
1785 {
1786  QSet<QgsMapLayerDependency> deps;
1787  Q_FOREACH ( const QgsMapLayerDependency &dep, oDeps )
1788  {
1789  if ( dep.origin() == QgsMapLayerDependency::FromUser )
1790  deps << dep;
1791  }
1792  if ( hasDependencyCycle( deps ) )
1793  return false;
1794 
1795  mDependencies = deps;
1796  emit dependenciesChanged();
1797  return true;
1798 }
1799 
1801 {
1802  if ( !dataProvider() )
1803  return;
1804 
1805  if ( enabled && !isRefreshOnNotifyEnabled() )
1806  {
1807  dataProvider()->setListening( enabled );
1808  connect( dataProvider(), &QgsVectorDataProvider::notify, this, &QgsMapLayer::onNotifiedTriggerRepaint );
1809  }
1810  else if ( !enabled && isRefreshOnNotifyEnabled() )
1811  {
1812  // we don't want to disable provider listening because someone else could need it (e.g. actions)
1813  disconnect( dataProvider(), &QgsVectorDataProvider::notify, this, &QgsMapLayer::onNotifiedTriggerRepaint );
1814  }
1815  mIsRefreshOnNofifyEnabled = enabled;
1816 }
1817 
1818 void QgsMapLayer::onNotifiedTriggerRepaint( const QString &message )
1819 {
1820  if ( refreshOnNotifyMessage().isEmpty() || refreshOnNotifyMessage() == message )
1821  triggerRepaint();
1822 }
1823 
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:292
Origin origin() const
Return the dependency origin.
QString mShortName
Definition: qgsmaplayer.h:1122
virtual QStringList subLayers() const
Returns the sublayers of this layer.
void setMetadataUrl(const QString &metaUrl)
Sets the metadata URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:303
The class is used as a container of context for various read/write operations on other objects...
QString dataUrlFormat() const
Returns the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:254
A rectangle specified with double values.
Definition: qgsrectangle.h:39
Base class for all map layer types.
Definition: qgsmaplayer.h:56
static QString formatLayerName(const QString &name)
A convenience function to capitalize and format a layer name.
static Qgs3DRendererRegistry * renderer3DRegistry()
Returns registry of available 3D renderers.
static const QString QGIS_VERSION
Version string.
Definition: qgis.h:63
void currentStyleChanged(const QString &currentName)
Emitted when the current style has been changed.
void setShortName(const QString &shortName)
Sets the short name of the layer used by QGIS Server to identify the layer.
Definition: qgsmaplayer.h:162
virtual void resolveReferences(const QgsProject &project)
Resolves references to other objects - second phase of loading - after readXml()
virtual void exportSldStyle(QDomDocument &doc, QString &errorMsg) const
Export the properties of this layer as SLD style in a QDomDocument.
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
void dependenciesChanged()
Emitted when dependencies are changed.
virtual QString loadSldStyle(const QString &uri, bool &resultFlag)
Attempts to style the layer using the formatting from an SLD type file.
Base class for all renderers that may to participate in 3D view.
QString mAttributionUrl
Definition: qgsmaplayer.h:1135
QString mKeywordList
Definition: qgsmaplayer.h:1127
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user&#39;s home dir.
virtual QSet< QgsMapLayerDependency > dependencies() const
Gets the list of dependencies.
virtual QgsDataProvider * dataProvider()
Returns the layer&#39;s data provider.
void readXml(const QDomNode &parentNode, const QString &keyStartsWith=QString())
Read store contents from XML.
void reset()
Reset the style manager to a basic state - with one default style which is set as current...
QStringList customPropertyKeys() const
Returns list of all keys within custom properties.
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
Definition: qgsmaplayer.h:169
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
QgsMapLayerLegend * legend() const
Can be null.
virtual ~QgsMapLayer()
Definition: qgsmaplayer.cpp:91
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
void setCustomProperties(const QgsObjectCustomProperties &properties)
Set custom properties for layer.
void metadataChanged()
Emitted when the layer&#39;s metadata is changed.
QString mDataUrlFormat
Definition: qgsmaplayer.h:1131
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
virtual QgsError error() const
Get current status error.
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
void validate()
Perform some validation on this CRS.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:236
bool updateRevision(const QgsProjectVersion &version)
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
virtual QString loadNamedStyle(const QString &uri, bool &resultFlag)
Retrieve a named style for this layer if one exists (either as a .qml file on disk or as a record in ...
QString mLegendUrlFormat
Definition: qgsmaplayer.h:1144
void setRefreshOnNotifyEnabled(bool enabled)
Set whether provider notification is connected to triggerRepaint.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
void readCustomProperties(const QDomNode &layerNode, const QString &keyStartsWith=QString())
Read custom properties from project file.
virtual bool loadNamedStyleFromDatabase(const QString &db, const QString &uri, QString &qml)
Retrieve a named style for this layer from a sqlite database.
virtual void resolveReferences(QgsProject *project)
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects...
virtual bool readXml(const QDomNode &layer_node, const QgsReadWriteContext &context)
Called by readLayerXML(), used by children to read state specific to them from project files...
virtual bool writeXml(QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context) const
Called by writeLayerXML(), used by children to write state specific to them to project files...
virtual QString saveSldStyle(const QString &uri, bool &resultFlag) const
Saves the properties of this layer to an SLD format file.
static QDomElement writeRectangle(const QgsRectangle &rect, QDomDocument &doc)
Definition: qgsxmlutils.cpp:78
void crsChanged()
Emit a signal that layer&#39;s CRS has been reset.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
virtual const QgsLayerMetadata & metadata() const
Returns a reference to the layer&#39;s metadata store.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Return value for the given key. If the key is not stored, default value will be used.
void setMetadataUrlType(const QString &metaUrlType)
Set the metadata type of the layer used by QGIS Server in GetCapabilities request MetadataUrlType ind...
Definition: qgsmaplayer.h:321
bool writeMetadataXml(QDomElement &metadataElement, QDomDocument &document) const
Stores state in Dom node.
Abstract base class for spatial data provider implementations.
virtual QString styleURI() const
Retrieve the style URI for this layer (either as a .qml file on disk or as a record in the users styl...
void writeXml(QDomNode &parentNode, QDomDocument &doc) const
Write store contents to XML.
bool isValid() const
Return the status of the layer.
void notify(const QString &msg)
Emitted when datasource issues a notification.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
void setConnection(const QString &aHost, const QString &aPort, const QString &aDatabase, const QString &aUsername, const QString &aPassword, SslMode sslmode=SslPrefer, const QString &authConfigId=QString())
Set all connection related members at once.
bool mIsRefreshOnNofifyEnabled
Definition: qgsmaplayer.h:1155
QgsMapLayer::LayerType type() const
Returns the type of the layer.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:443
void remove(const QString &key)
Remove a key (entry) from the store.
QString mLayerName
Name of the layer - used for display.
Definition: qgsmaplayer.h:1120
QString username() const
Returns the username.
bool isRefreshOnNotifyEnabled() const
Returns true if the refresh on provider nofification is enabled.
Definition: qgsmaplayer.h:883
void styleChanged()
Signal emitted whenever a change affects the layer&#39;s style.
virtual bool readSymbology(const QDomNode &node, QString &errorMessage, const QgsReadWriteContext &context)=0
Read the symbology for the current layer from the Dom node supplied.
virtual QString htmlMetadata() const
Obtain a formatted HTML string containing assorted metadata for this layer.
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsMapLayer(QgsMapLayer::LayerType type=VectorLayer, const QString &name=QString(), const QString &source=QString())
Constructor for QgsMapLayer.
Definition: qgsmaplayer.cpp:57
void writeCustomProperties(QDomNode &layerNode, QDomDocument &doc) const
Write custom properties to project file.
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
QgsMapLayerStyleManager * styleManager() const
Get access to the layer&#39;s style manager.
QgsError mError
Error.
Definition: qgsmaplayer.h:1147
virtual bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context) const =0
Write the symbology for the layer into the docment provided.
QgsRectangle mExtent
Extent of the layer.
Definition: qgsmaplayer.h:1111
virtual void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const =0
Writes renderer&#39;s properties to given XML element.
QString mMetadataUrl
MetadataUrl of the layer.
Definition: qgsmaplayer.h:1138
void setLegendUrlFormat(const QString &legendUrlFormat)
Sets the format for a URL based layer legend.
Definition: qgsmaplayer.h:716
QString host() const
Returns the host.
QString metadataUrlFormat() const
Returns the metadata format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:348
void setValidationHint(const QString &html)
Set user hint for validation.
const QgsPathResolver & pathResolver() const
Returns path resolver for conversion between relative and absolute paths.
void setKeywordList(const QString &keywords)
Sets the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:208
void setValue(const QString &key, const QVariant &value)
Add an entry to the store. If the entry with the keys exists already, it will be overwritten.
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
static QString capitalize(const QString &string, Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
void setTitle(const QString &title)
Sets the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:176
void readStyleManager(const QDomNode &layerNode)
Read style manager&#39;s configuration (if any). To be called by subclasses.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted...
int autoRefreshInterval() const
Returns the auto refresh interval (in milliseconds).
virtual void setSubLayerVisibility(const QString &name, bool visible)
Set the visibility of the given sublayer name.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:38
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
LayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:94
virtual void setMetadata(const QgsLayerMetadata &metadata)
Sets the layer&#39;s metadata store.
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer&#39;s spatial reference system.
QSet< QgsMapLayerDependency > mDependencies
List of layers that may modify this layer on modification.
Definition: qgsmaplayer.h:1150
virtual void setListening(bool isListening)
Set whether the provider will listen to datasource notifications If set, the provider will issue noti...
void nameChanged()
Emitted when the name has been changed.
A class to describe the version of a project.
QString providerType() const
[ data provider interface ] Which provider is being used for this Raster Layer?
virtual void setExtent(const QgsRectangle &rect)
Set the extent.
void readXml(const QDomElement &mgrElement)
Read configuration (for project loading)
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:216
bool writeSld(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsStringMap &props=QgsStringMap()) const
Writes the symbology of the layer into the document provided in SLD 1.1 format.
virtual QString dataSourceUri(bool expandAuthConfig=false) const
Get the data source specification.
QString mDataUrl
DataUrl of the layer.
Definition: qgsmaplayer.h:1130
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer...
virtual bool isEditable() const
Returns true if the layer can be edited.
QString legendUrl() const
Returns the URL for the layer&#39;s legend.
Definition: qgsmaplayer.h:711
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
Reads and writes project states.
Definition: qgsproject.h:81
QString publicSource() const
Gets a version of the internal layer definition that has sensitive bits removed (for example...
void setParam(const QString &key, const QString &value)
Set generic param (generic mode)
void setRenderer3D(QgsAbstract3DRenderer *renderer)
Sets 3D renderer for the layer.
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg)
Import the properties of this layer from a QDomDocument.
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
double minimumScale() const
Returns the minimum map scale (i.e.
double maximumScale() const
Returns the maximum map scale (i.e.
bool mValid
Indicates if the layer is valid and can be drawn.
Definition: qgsmaplayer.h:1114
QString port() const
Returns the port.
void setName(const QString &name)
Set the display name of the layer.
void setDataUrlFormat(const QString &dataUrlFormat)
Sets the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:245
int open(const QString &path)
Opens the database at the specified file path.
QStringList keys() const
Return list of stored keys.
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
QString mTitle
Definition: qgsmaplayer.h:1123
virtual bool readSld(const QDomNode &node, QString &errorMessage)
Definition: qgsmaplayer.h:647
QString legendUrlFormat() const
Returns the format for a URL based layer legend.
Definition: qgsmaplayer.h:721
Convert just the first letter of each word to uppercase, leave the rest untouched.
QString mMetadataUrlFormat
Definition: qgsmaplayer.h:1140
void setLegendUrl(const QString &legendUrl)
Sets the URL for the layer&#39;s legend.
Definition: qgsmaplayer.h:706
bool hasDependencyCycle(const QSet< QgsMapLayerDependency > &layers) const
Checks whether a new set of dependencies will introduce a cycle.
void setMetadataUrlFormat(const QString &metaUrlFormat)
Sets the metadata format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:339
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:184
static QString pkgDataPath()
Returns the common root path of all application data directories.
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
QString uri(bool expandAuthConfig=true) const
return complete uri
bool readLayerXml(const QDomElement &layerElement, const QgsReadWriteContext &context)
Sets state from Dom document.
QString mAttribution
Attribution of the layer.
Definition: qgsmaplayer.h:1134
QString mAbstract
Description of the layer.
Definition: qgsmaplayer.h:1126
void writeStyleManager(QDomNode &layerNode, QDomDocument &doc) const
Write style manager&#39;s configuration (if exists). To be called by subclasses.
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
bool readMetadataXml(const QDomElement &metadataElement)
Sets state from Dom document.
void blendModeChanged(QPainter::CompositionMode blendMode)
Signal emitted when the blend mode is changed, through QgsMapLayer::setBlendMode() ...
A structured metadata store for a map layer.
GeometryType
The geometry types are used to group QgsWkbTypes::Type in a coarse way.
Definition: qgswkbtypes.h:137
QString mRefreshOnNofifyMessage
Definition: qgsmaplayer.h:1156
static QgsAuthManager * authManager()
Returns the application&#39;s authentication manager instance.
virtual bool readStyle(const QDomNode &node, QString &errorMessage, const QgsReadWriteContext &context)
Read the style for the current layer from the Dom node supplied.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
virtual QString loadDefaultStyle(bool &resultFlag)
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
virtual QString saveDefaultStyle(bool &resultFlag)
Save the properties of this layer as the default style (either as a .qml file on disk or as a record ...
void itemsChanged()
Emitted when existing items/nodes got invalid and should be replaced by new ones. ...
QString name() const
Returns the display name of the layer.
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:1117
QString source() const
Returns the source for the layer.
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
QgsError is container for error messages (report).
Definition: qgserror.h:83
bool setMasterPassword(bool verify=false)
Main call to initially set or continually check master password is set.
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
QByteArray encodedUri() const
return complete encoded uri (generic mode)
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:383
Qgs3DRendererAbstractMetadata * rendererMetadata(const QString &type) const
Returns metadata for a 3D renderer type (may be used to create a new instance of the type) ...
QString mLegendUrl
WMS legend.
Definition: qgsmaplayer.h:1143
This class represents a coordinate reference system (CRS).
QString layerId() const
Return the ID of the layer this dependency depends on.
QgsVectorDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
void legendChanged()
Signal emitted when legend of the layer has changed.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
Simple key-value store (keys = strings, values = variants) that supports loading/saving to/from XML i...
void setAutoRefreshInterval(int interval)
Sets the auto refresh interval (in milliseconds) for the layer.
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
void setAttributionUrl(const QString &attribUrl)
Sets the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:283
virtual QString saveNamedStyle(const QString &uri, bool &resultFlag)
Save the properties of this layer as a named style (either as a .qml file on disk or as a record in t...
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
virtual bool writeStyle(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context) const
Write just the style information for the layer into the document.
QString metadataUrl() const
Returns the metadata URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:312
void setAutoRefreshEnabled(bool enabled)
Sets whether auto refresh is enabled for the layer.
QUndoStack * undoStackStyles()
Return pointer to layer&#39;s style undo stack.
QString password() const
Returns the password.
bool addStyle(const QString &name, const QgsMapLayerStyle &style)
Add a style with given name and data.
QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QUndoStack * undoStack()
Return pointer to layer&#39;s undo stack.
void setAttribution(const QString &attrib)
Sets the attribution of the layer used by QGIS Server in GetCapabilities request. ...
Definition: qgsmaplayer.h:265
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
QString providerType() const
Return the provider type for this layer.
void setRefreshOnNofifyMessage(const QString &message)
Set the notification message that triggers repaine If refresh on notification is enabled, the notification will triggerRepaint only if the notification message is equal to.
Definition: qgsmaplayer.h:958
bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
Base metadata class for 3D renderers.
void setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
This class models dependencies with or between map layers.
Management of styles for use with one map layer.
QString refreshOnNotifyMessage() const
Returns the message that should be notified by the provider to triggerRepaint.
Definition: qgsmaplayer.h:876
Represents a vector layer which manages a vector based data sets.
virtual QDateTime timestamp() const
Time stamp of data source in the moment when data/metadata were loaded by provider.
virtual void setLayerOrder(const QStringList &layers)
Reorders the previously selected sublayers of this layer from bottom to top.
void writeXml(QDomElement &mgrElement) const
Write configuration (for project saving)
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in Dom node.
void setDataUrl(const QString &dataUrl)
Sets the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:227
void setValid(bool valid)
Set whether layer is valid or not - should be used in constructor.
QString mMetadataUrlType
Definition: qgsmaplayer.h:1139
QString database() const
Returns the database.
virtual QgsAbstract3DRenderer * createRenderer(QDomElement &elem, const QgsReadWriteContext &context)=0
Returns new instance of the renderer given the DOM element.
void setDatabase(const QString &database)
Set database.
void setAbstract(const QString &abstract)
Sets the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:192
static const double SCALE_PRECISION
Fudge factor used to compare two scales.
Definition: qgis.h:125
QString attribution() const
Returns the attribution of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:274
QString metadataUrlType() const
Returns the metadata type of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:330
void setScaleBasedVisibility(const bool enabled)
Sets whether scale based visibility is enabled for the layer.
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
static QString removePassword(const QString &aUri)
Removes password element from uris.
virtual bool setDependencies(const QSet< QgsMapLayerDependency > &layers)
Sets the list of dependencies.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg) const
Export the properties of this layer as named style in a QDomDocument.