QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
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 "qgssqliteutils.h"
20 #include "qgs3drendererregistry.h"
21 #include "qgsabstract3drenderer.h"
22 #include "qgsapplication.h"
25 #include "qgsdatasourceuri.h"
26 #include "qgsfileutils.h"
27 #include "qgslogger.h"
28 #include "qgsauthmanager.h"
29 #include "qgsmaplayer.h"
30 #include "qgsmaplayerlegend.h"
32 #include "qgspathresolver.h"
34 #include "qgsproject.h"
35 #include "qgsproviderregistry.h"
36 #include "qgsrasterlayer.h"
37 #include "qgsreadwritecontext.h"
38 #include "qgsrectangle.h"
39 #include "qgssldexportcontext.h"
40 #include "qgsvectorlayer.h"
41 #include "qgsxmlutils.h"
42 #include "qgsstringutils.h"
43 #include "qgsmessagelog.h"
46 #include "qgsprovidermetadata.h"
47 #include "qgslayernotesutils.h"
48 #include "qgsdatums.h"
49 #include "qgsprojoperation.h"
50 #include "qgsthreadingutils.h"
51 #include "qgsunittypes.h"
52 
53 #include <QDir>
54 #include <QDomDocument>
55 #include <QDomElement>
56 #include <QDomImplementation>
57 #include <QDomNode>
58 #include <QFile>
59 #include <QFileInfo>
60 #include <QLocale>
61 #include <QTextStream>
62 #include <QUrl>
63 #include <QTimer>
64 #include <QStandardPaths>
65 #include <QUuid>
66 #include <QRegularExpression>
67 
68 #include <sqlite3.h>
69 
71 {
72  switch ( type )
73  {
74  case Metadata:
75  return QStringLiteral( ".qmd" );
76 
77  case Style:
78  return QStringLiteral( ".qml" );
79  }
80  return QString();
81 }
82 
84  const QString &lyrname,
85  const QString &source )
86  : mDataSource( source )
87  , mLayerName( lyrname )
88  , mLayerType( type )
89  , mServerProperties( std::make_unique<QgsMapLayerServerProperties>( this ) )
90  , mUndoStack( new QUndoStack( this ) )
91  , mUndoStackStyles( new QUndoStack( this ) )
92  , mStyleManager( new QgsMapLayerStyleManager( this ) )
93  , mRefreshTimer( new QTimer( this ) )
94 {
95  mID = generateId( lyrname );
96  connect( this, &QgsMapLayer::crsChanged, this, &QgsMapLayer::configChanged );
97  connect( this, &QgsMapLayer::nameChanged, this, &QgsMapLayer::configChanged );
98  connect( mRefreshTimer, &QTimer::timeout, this, [this]
99  {
100 
101  switch ( mAutoRefreshMode )
102  {
104  break;
106  triggerRepaint( true );
107  break;
109  reload();
110  break;
111  }
112  } );
113 }
114 
116 {
117  if ( project() && project()->pathResolver().writePath( mDataSource ).startsWith( "attachment:" ) )
118  {
120  }
121 
122  delete m3DRenderer;
123  delete mLegend;
124  delete mStyleManager;
125 }
126 
127 void QgsMapLayer::clone( QgsMapLayer *layer ) const
128 {
130 
131  QgsDebugMsgLevel( QStringLiteral( "Cloning layer '%1'" ).arg( name() ), 3 );
132  layer->setBlendMode( blendMode() );
133 
134  const auto constStyles = styleManager()->styles();
135  for ( const QString &s : constStyles )
136  {
137  layer->styleManager()->addStyle( s, styleManager()->style( s ) );
138  }
139 
140  layer->setName( name() );
141  layer->setShortName( shortName() );
142 
143  if ( layer->dataProvider() && layer->dataProvider()->elevationProperties() )
144  {
146  layer->mExtent3D = mExtent3D;
147  else
148  layer->mExtent2D = mExtent2D;
149  }
150 
151  layer->setMaximumScale( maximumScale() );
152  layer->setMinimumScale( minimumScale() );
154  layer->setTitle( title() );
155  layer->setAbstract( abstract() );
156  layer->setKeywordList( keywordList() );
157  layer->setDataUrl( dataUrl() );
158  layer->setDataUrlFormat( dataUrlFormat() );
159  layer->setAttribution( attribution() );
160  layer->setAttributionUrl( attributionUrl() );
161  layer->setLegendUrl( legendUrl() );
163  layer->setDependencies( dependencies() );
165  layer->setCrs( crs() );
166  layer->setCustomProperties( mCustomProperties );
167  layer->setOpacity( mLayerOpacity );
168  layer->setMetadata( mMetadata );
169  layer->serverProperties()->copyTo( mServerProperties.get() );
170 }
171 
173 {
174  // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
176 
177  return mLayerType;
178 }
179 
181 {
183 
184  return mFlags;
185 }
186 
188 {
190 
191  if ( flags == mFlags )
192  return;
193 
194  mFlags = flags;
195  emit flagsChanged();
196 }
197 
199 {
201 
202  return Qgis::MapLayerProperties();
203 }
204 
205 QString QgsMapLayer::id() const
206 {
207  // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
209 
210  return mID;
211 }
212 
213 void QgsMapLayer::setName( const QString &name )
214 {
216 
217  if ( name == mLayerName )
218  return;
219 
220  mLayerName = name;
221 
222  emit nameChanged();
223 }
224 
225 QString QgsMapLayer::name() const
226 {
227  // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
229 
230  QgsDebugMsgLevel( "returning name '" + mLayerName + '\'', 4 );
231  return mLayerName;
232 }
233 
235 {
237 
238  return nullptr;
239 }
240 
242 {
244 
245  return nullptr;
246 }
247 
248 QString QgsMapLayer::shortName() const
249 {
251 
252  return mShortName;
253 }
254 
255 void QgsMapLayer::setMetadataUrl( const QString &metaUrl )
256 {
258 
259  QList<QgsMapLayerServerProperties::MetadataUrl> urls = serverProperties()->metadataUrls();
260  if ( urls.isEmpty() )
261  {
262  const QgsMapLayerServerProperties::MetadataUrl newItem = QgsMapLayerServerProperties::MetadataUrl( metaUrl, QLatin1String(), QLatin1String() );
263  urls.prepend( newItem );
264  }
265  else
266  {
267  const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst();
268  const QgsMapLayerServerProperties::MetadataUrl newItem( metaUrl, old.type, old.format );
269  urls.prepend( newItem );
270  }
272 }
273 
275 {
277 
278  if ( mServerProperties->metadataUrls().isEmpty() )
279  {
280  return QLatin1String();
281  }
282  else
283  {
284  return mServerProperties->metadataUrls().first().url;
285  }
286 }
287 
288 void QgsMapLayer::setMetadataUrlType( const QString &metaUrlType )
289 {
291 
292  QList<QgsMapLayerServerProperties::MetadataUrl> urls = mServerProperties->metadataUrls();
293  if ( urls.isEmpty() )
294  {
295  const QgsMapLayerServerProperties::MetadataUrl newItem( QLatin1String(), metaUrlType, QLatin1String() );
296  urls.prepend( newItem );
297  }
298  else
299  {
300  const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst();
301  const QgsMapLayerServerProperties::MetadataUrl newItem( old.url, metaUrlType, old.format );
302  urls.prepend( newItem );
303  }
304  mServerProperties->setMetadataUrls( urls );
305 }
306 
308 {
310 
311  if ( mServerProperties->metadataUrls().isEmpty() )
312  {
313  return QLatin1String();
314  }
315  else
316  {
317  return mServerProperties->metadataUrls().first().type;
318  }
319 }
320 
321 void QgsMapLayer::setMetadataUrlFormat( const QString &metaUrlFormat )
322 {
324 
325  QList<QgsMapLayerServerProperties::MetadataUrl> urls = mServerProperties->metadataUrls();
326  if ( urls.isEmpty() )
327  {
328  const QgsMapLayerServerProperties::MetadataUrl newItem( QLatin1String(), QLatin1String(), metaUrlFormat );
329  urls.prepend( newItem );
330  }
331  else
332  {
333  const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst( );
334  const QgsMapLayerServerProperties::MetadataUrl newItem( old.url, old.type, metaUrlFormat );
335  urls.prepend( newItem );
336  }
337  mServerProperties->setMetadataUrls( urls );
338 }
339 
341 {
343 
344  if ( mServerProperties->metadataUrls().isEmpty() )
345  {
346  return QString();
347  }
348  else
349  {
350  return mServerProperties->metadataUrls().first().format;
351  }
352 }
353 
354 QString QgsMapLayer::publicSource( bool hidePassword ) const
355 {
357 
358  // Redo this every time we're asked for it, as we don't know if
359  // dataSource has changed.
360  QString safeName = QgsDataSourceUri::removePassword( mDataSource, hidePassword );
361  return safeName;
362 }
363 
364 QString QgsMapLayer::source() const
365 {
367 
368  return mDataSource;
369 }
370 
372 {
374 
375  return mExtent2D.isNull() ? mExtent3D.toRectangle() : mExtent2D;
376 }
377 
379 {
381 
382  return mExtent3D;
383 }
384 
385 void QgsMapLayer::setBlendMode( const QPainter::CompositionMode blendMode )
386 {
388 
389  if ( mBlendMode == blendMode )
390  return;
391 
392  mBlendMode = blendMode;
393  emit blendModeChanged( blendMode );
395 }
396 
397 QPainter::CompositionMode QgsMapLayer::blendMode() const
398 {
399  // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
401 
402  return mBlendMode;
403 }
404 
405 void QgsMapLayer::setOpacity( double opacity )
406 {
408 
410  return;
412  emit opacityChanged( opacity );
414 }
415 
416 double QgsMapLayer::opacity() const
417 {
418  // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
420 
421  return mLayerOpacity;
422 }
423 
424 bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags, QgsDataProvider *preloadedProvider )
425 {
427 
428  mPreloadedProvider.reset( preloadedProvider );
429 
430  bool layerError;
431  mReadFlags = flags;
432 
433  QDomNode mnl;
434  QDomElement mne;
435 
436  // read provider
437  QString provider;
438  mnl = layerElement.namedItem( QStringLiteral( "provider" ) );
439  mne = mnl.toElement();
440  provider = mne.text();
441 
442  // set data source
443  mnl = layerElement.namedItem( QStringLiteral( "datasource" ) );
444  mne = mnl.toElement();
445  const QString dataSourceRaw = mne.text();
446  mDataSource = provider.isEmpty() ? dataSourceRaw : QgsProviderRegistry::instance()->relativeToAbsoluteUri( provider, dataSourceRaw, context );
447 
448  // if the layer needs authentication, ensure the master password is set
449  const thread_local QRegularExpression rx( "authcfg=([a-z]|[A-Z]|[0-9]){7}" );
450  if ( rx.match( mDataSource ).hasMatch()
452  {
453  return false;
454  }
455 
456  mDataSource = decodedSource( mDataSource, provider, context );
457 
458  // Set the CRS from project file, asking the user if necessary.
459  // Make it the saved CRS to have WMS layer projected correctly.
460  // We will still overwrite whatever GDAL etc picks up anyway
461  // further down this function.
462  mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
463  mne = mnl.toElement();
464 
466  CUSTOM_CRS_VALIDATION savedValidation;
467 
468  const QDomNode srsNode = layerElement.namedItem( QStringLiteral( "srs" ) );
469  mCRS.readXml( srsNode );
470  mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
472  mCRS.validate();
473  savedCRS = mCRS;
474 
475  // Do not validate any projections in children, they will be overwritten anyway.
476  // No need to ask the user for a projections when it is overwritten, is there?
479 
480  const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Layer" ), mne.text() );
481 
482  // the internal name is just the data source basename
483  //QFileInfo dataSourceFileInfo( mDataSource );
484  //internalName = dataSourceFileInfo.baseName();
485 
486  // set ID
487  mnl = layerElement.namedItem( QStringLiteral( "id" ) );
488  if ( ! mnl.isNull() )
489  {
490  mne = mnl.toElement();
491  if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
492  {
493  mID = mne.text();
494  }
495  }
496 
497  // set name
498  mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
499  mne = mnl.toElement();
500 
501  //name can be translated
502  setName( context.projectTranslator()->translate( QStringLiteral( "project:layers:%1" ).arg( layerElement.namedItem( QStringLiteral( "id" ) ).toElement().text() ), mne.text() ) );
503 
504  // now let the children grab what they need from the Dom node.
505  layerError = !readXml( layerElement, context );
506 
507  // overwrite CRS with what we read from project file before the raster/vector
508  // file reading functions changed it. They will if projections is specified in the file.
509  // FIXME: is this necessary? Yes, it is (autumn 2019)
511  mCRS = savedCRS;
512 
513  //short name
514  const QDomElement shortNameElem = layerElement.firstChildElement( QStringLiteral( "shortname" ) );
515  if ( !shortNameElem.isNull() )
516  {
517  mShortName = shortNameElem.text();
518  }
519 
520  //title
521  const QDomElement titleElem = layerElement.firstChildElement( QStringLiteral( "title" ) );
522  if ( !titleElem.isNull() )
523  {
524  mTitle = titleElem.text();
525  }
526 
527  //abstract
528  const QDomElement abstractElem = layerElement.firstChildElement( QStringLiteral( "abstract" ) );
529  if ( !abstractElem.isNull() )
530  {
531  mAbstract = abstractElem.text();
532  }
533 
534  //keywordList
535  const QDomElement keywordListElem = layerElement.firstChildElement( QStringLiteral( "keywordList" ) );
536  if ( !keywordListElem.isNull() )
537  {
538  QStringList kwdList;
539  for ( QDomNode n = keywordListElem.firstChild(); !n.isNull(); n = n.nextSibling() )
540  {
541  kwdList << n.toElement().text();
542  }
543  mKeywordList = kwdList.join( QLatin1String( ", " ) );
544  }
545 
546  //dataUrl
547  const QDomElement dataUrlElem = layerElement.firstChildElement( QStringLiteral( "dataUrl" ) );
548  if ( !dataUrlElem.isNull() )
549  {
550  mDataUrl = dataUrlElem.text();
551  mDataUrlFormat = dataUrlElem.attribute( QStringLiteral( "format" ), QString() );
552  }
553 
554  //legendUrl
555  const QDomElement legendUrlElem = layerElement.firstChildElement( QStringLiteral( "legendUrl" ) );
556  if ( !legendUrlElem.isNull() )
557  {
558  mLegendUrl = legendUrlElem.text();
559  mLegendUrlFormat = legendUrlElem.attribute( QStringLiteral( "format" ), QString() );
560  }
561 
562  //attribution
563  const QDomElement attribElem = layerElement.firstChildElement( QStringLiteral( "attribution" ) );
564  if ( !attribElem.isNull() )
565  {
566  mAttribution = attribElem.text();
567  mAttributionUrl = attribElem.attribute( QStringLiteral( "href" ), QString() );
568  }
569 
570  serverProperties()->readXml( layerElement );
571 
572  if ( serverProperties()->metadataUrls().isEmpty() )
573  {
574  // metadataUrl is still empty, maybe it's a QGIS Project < 3.22
575  // keep for legacy
576  const QDomElement metaUrlElem = layerElement.firstChildElement( QStringLiteral( "metadataUrl" ) );
577  if ( !metaUrlElem.isNull() )
578  {
579  const QString url = metaUrlElem.text();
580  const QString type = metaUrlElem.attribute( QStringLiteral( "type" ), QString() );
581  const QString format = metaUrlElem.attribute( QStringLiteral( "format" ), QString() );
582  const QgsMapLayerServerProperties::MetadataUrl newItem( url, type, format );
583  mServerProperties->setMetadataUrls( QList<QgsMapLayerServerProperties::MetadataUrl>() << newItem );
584  }
585  }
586 
587  // mMetadata.readFromLayer( this );
588  const QDomElement metadataElem = layerElement.firstChildElement( QStringLiteral( "resourceMetadata" ) );
589  mMetadata.readMetadataXml( metadataElem );
590 
591  setAutoRefreshInterval( layerElement.attribute( QStringLiteral( "autoRefreshTime" ), QStringLiteral( "0" ) ).toInt() );
592  if ( layerElement.hasAttribute( QStringLiteral( "autoRefreshMode" ) ) )
593  {
594  setAutoRefreshMode( qgsEnumKeyToValue( layerElement.attribute( QStringLiteral( "autoRefreshMode" ) ), Qgis::AutoRefreshMode::Disabled ) );
595  }
596  else
597  {
598  setAutoRefreshMode( layerElement.attribute( QStringLiteral( "autoRefreshEnabled" ), QStringLiteral( "0" ) ).toInt() ? Qgis::AutoRefreshMode::RedrawOnly : Qgis::AutoRefreshMode::Disabled );
599  }
600  setRefreshOnNofifyMessage( layerElement.attribute( QStringLiteral( "refreshOnNotifyMessage" ), QString() ) );
601  setRefreshOnNotifyEnabled( layerElement.attribute( QStringLiteral( "refreshOnNotifyEnabled" ), QStringLiteral( "0" ) ).toInt() );
602 
603  // geographic extent is read only if necessary
604  if ( mReadFlags & QgsMapLayer::ReadFlag::FlagTrustLayerMetadata )
605  {
606  const QDomNode wgs84ExtentNode = layerElement.namedItem( QStringLiteral( "wgs84extent" ) );
607  if ( !wgs84ExtentNode.isNull() )
608  mWgs84Extent = QgsXmlUtils::readRectangle( wgs84ExtentNode.toElement() );
609  }
610 
611  mLegendPlaceholderImage = layerElement.attribute( QStringLiteral( "legendPlaceholderImage" ) );
612 
613  return ! layerError;
614 } // bool QgsMapLayer::readLayerXML
615 
616 
617 bool QgsMapLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
618 {
620 
621  Q_UNUSED( layer_node )
622  Q_UNUSED( context )
623  // NOP by default; children will over-ride with behavior specific to them
624 
625  // read Extent
627  {
628  const QDomNode extent3DNode = layer_node.namedItem( QStringLiteral( "extent3D" ) );
629  if ( extent3DNode.isNull() )
630  {
631  const QDomNode extentNode = layer_node.namedItem( QStringLiteral( "extent" ) );
632  if ( !extentNode.isNull() )
633  {
634  mExtent2D = QgsXmlUtils::readRectangle( extentNode.toElement() );
635  }
636  }
637  else
638  {
639  mExtent3D = QgsXmlUtils::readBox3D( extent3DNode.toElement() );
640  }
641  }
642 
643  return true;
644 } // void QgsMapLayer::readXml
645 
646 
647 bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context ) const
648 {
650 
651  if ( !mExtent3D.isNull() && dataProvider() && dataProvider()->elevationProperties() && dataProvider()->elevationProperties()->containsElevationData() )
652  layerElement.appendChild( QgsXmlUtils::writeBox3D( mExtent3D, document ) );
653  else if ( !mExtent2D.isNull() )
654  layerElement.appendChild( QgsXmlUtils::writeRectangle( mExtent2D, document ) );
655 
656  if ( const QgsRectangle lWgs84Extent = wgs84Extent( true ); !lWgs84Extent.isNull() )
657  {
658  layerElement.appendChild( QgsXmlUtils::writeRectangle( lWgs84Extent, document, QStringLiteral( "wgs84extent" ) ) );
659  }
660 
661  layerElement.setAttribute( QStringLiteral( "autoRefreshTime" ), QString::number( mRefreshTimer->interval() ) );
662  layerElement.setAttribute( QStringLiteral( "autoRefreshMode" ), qgsEnumValueToKey( mAutoRefreshMode ) );
663  layerElement.setAttribute( QStringLiteral( "refreshOnNotifyEnabled" ), mIsRefreshOnNofifyEnabled ? 1 : 0 );
664  layerElement.setAttribute( QStringLiteral( "refreshOnNotifyMessage" ), mRefreshOnNofifyMessage );
665 
666  // ID
667  QDomElement layerId = document.createElement( QStringLiteral( "id" ) );
668  const QDomText layerIdText = document.createTextNode( id() );
669  layerId.appendChild( layerIdText );
670 
671  layerElement.appendChild( layerId );
672 
673  // data source
674  QDomElement dataSource = document.createElement( QStringLiteral( "datasource" ) );
675  const QgsDataProvider *provider = dataProvider();
676  const QString providerKey = provider ? provider->name() : QString();
677  const QString srcRaw = encodedSource( source(), context );
678  const QString src = providerKey.isEmpty() ? srcRaw : QgsProviderRegistry::instance()->absoluteToRelativeUri( providerKey, srcRaw, context );
679  const QDomText dataSourceText = document.createTextNode( src );
680  dataSource.appendChild( dataSourceText );
681  layerElement.appendChild( dataSource );
682 
683  // layer name
684  QDomElement layerName = document.createElement( QStringLiteral( "layername" ) );
685  const QDomText layerNameText = document.createTextNode( name() );
686  layerName.appendChild( layerNameText );
687  layerElement.appendChild( layerName );
688 
689  // layer short name
690  if ( !mShortName.isEmpty() )
691  {
692  QDomElement layerShortName = document.createElement( QStringLiteral( "shortname" ) );
693  const QDomText layerShortNameText = document.createTextNode( mShortName );
694  layerShortName.appendChild( layerShortNameText );
695  layerElement.appendChild( layerShortName );
696  }
697 
698  // layer title
699  if ( !mTitle.isEmpty() )
700  {
701  QDomElement layerTitle = document.createElement( QStringLiteral( "title" ) );
702  const QDomText layerTitleText = document.createTextNode( mTitle );
703  layerTitle.appendChild( layerTitleText );
704  layerElement.appendChild( layerTitle );
705  }
706 
707  // layer abstract
708  if ( !mAbstract.isEmpty() )
709  {
710  QDomElement layerAbstract = document.createElement( QStringLiteral( "abstract" ) );
711  const QDomText layerAbstractText = document.createTextNode( mAbstract );
712  layerAbstract.appendChild( layerAbstractText );
713  layerElement.appendChild( layerAbstract );
714  }
715 
716  // layer keyword list
717  const QStringList keywordStringList = keywordList().split( ',' );
718  if ( !keywordStringList.isEmpty() )
719  {
720  QDomElement layerKeywordList = document.createElement( QStringLiteral( "keywordList" ) );
721  for ( int i = 0; i < keywordStringList.size(); ++i )
722  {
723  QDomElement layerKeywordValue = document.createElement( QStringLiteral( "value" ) );
724  const QDomText layerKeywordText = document.createTextNode( keywordStringList.at( i ).trimmed() );
725  layerKeywordValue.appendChild( layerKeywordText );
726  layerKeywordList.appendChild( layerKeywordValue );
727  }
728  layerElement.appendChild( layerKeywordList );
729  }
730 
731  // layer dataUrl
732  const QString aDataUrl = dataUrl();
733  if ( !aDataUrl.isEmpty() )
734  {
735  QDomElement layerDataUrl = document.createElement( QStringLiteral( "dataUrl" ) );
736  const QDomText layerDataUrlText = document.createTextNode( aDataUrl );
737  layerDataUrl.appendChild( layerDataUrlText );
738  layerDataUrl.setAttribute( QStringLiteral( "format" ), dataUrlFormat() );
739  layerElement.appendChild( layerDataUrl );
740  }
741 
742  // layer legendUrl
743  const QString aLegendUrl = legendUrl();
744  if ( !aLegendUrl.isEmpty() )
745  {
746  QDomElement layerLegendUrl = document.createElement( QStringLiteral( "legendUrl" ) );
747  const QDomText layerLegendUrlText = document.createTextNode( aLegendUrl );
748  layerLegendUrl.appendChild( layerLegendUrlText );
749  layerLegendUrl.setAttribute( QStringLiteral( "format" ), legendUrlFormat() );
750  layerElement.appendChild( layerLegendUrl );
751  }
752 
753  // layer attribution
754  const QString aAttribution = attribution();
755  if ( !aAttribution.isEmpty() )
756  {
757  QDomElement layerAttribution = document.createElement( QStringLiteral( "attribution" ) );
758  const QDomText layerAttributionText = document.createTextNode( aAttribution );
759  layerAttribution.appendChild( layerAttributionText );
760  layerAttribution.setAttribute( QStringLiteral( "href" ), attributionUrl() );
761  layerElement.appendChild( layerAttribution );
762  }
763 
764  // timestamp if supported
765  if ( timestamp() > QDateTime() )
766  {
767  QDomElement stamp = document.createElement( QStringLiteral( "timestamp" ) );
768  const QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
769  stamp.appendChild( stampText );
770  layerElement.appendChild( stamp );
771  }
772 
773  layerElement.appendChild( layerName );
774 
775  // zorder
776  // This is no longer stored in the project file. It is superfluous since the layers
777  // are written and read in the proper order.
778 
779  // spatial reference system id
780  QDomElement mySrsElement = document.createElement( QStringLiteral( "srs" ) );
781  mCRS.writeXml( mySrsElement, document );
782  layerElement.appendChild( mySrsElement );
783 
784  // layer metadata
785  QDomElement myMetadataElem = document.createElement( QStringLiteral( "resourceMetadata" ) );
786  mMetadata.writeMetadataXml( myMetadataElem, document );
787  layerElement.appendChild( myMetadataElem );
788 
789  layerElement.setAttribute( QStringLiteral( "legendPlaceholderImage" ), mLegendPlaceholderImage );
790 
791  // now append layer node to map layer node
792  return writeXml( layerElement, document, context );
793 }
794 
795 void QgsMapLayer::writeCommonStyle( QDomElement &layerElement, QDomDocument &document,
796  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
797 {
799 
800  // save categories
801  const QMetaEnum metaEnum = QMetaEnum::fromType<QgsMapLayer::StyleCategories>();
802  const QString categoriesKeys( metaEnum.valueToKeys( static_cast<int>( categories ) ) );
803  layerElement.setAttribute( QStringLiteral( "styleCategories" ), categoriesKeys );
804 
805  if ( categories.testFlag( Rendering ) )
806  {
807  // use scale dependent visibility flag
808  layerElement.setAttribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ), hasScaleBasedVisibility() ? 1 : 0 );
809  layerElement.setAttribute( QStringLiteral( "maxScale" ), QString::number( maximumScale() ) );
810  layerElement.setAttribute( QStringLiteral( "minScale" ), QString::number( minimumScale() ) );
811  }
812 
813  if ( categories.testFlag( Symbology3D ) )
814  {
815  if ( m3DRenderer )
816  {
817  QDomElement renderer3DElem = document.createElement( QStringLiteral( "renderer-3d" ) );
818  renderer3DElem.setAttribute( QStringLiteral( "type" ), m3DRenderer->type() );
819  m3DRenderer->writeXml( renderer3DElem, context );
820  layerElement.appendChild( renderer3DElem );
821  }
822  }
823 
824  if ( categories.testFlag( LayerConfiguration ) )
825  {
826  // flags
827  // this code is saving automatically all the flags entries
828  QDomElement layerFlagsElem = document.createElement( QStringLiteral( "flags" ) );
829  const auto enumMap = qgsEnumMap<QgsMapLayer::LayerFlag>();
830  for ( auto it = enumMap.constBegin(); it != enumMap.constEnd(); ++it )
831  {
832  const bool flagValue = mFlags.testFlag( it.key() );
833  QDomElement flagElem = document.createElement( it.value() );
834  flagElem.appendChild( document.createTextNode( QString::number( flagValue ) ) );
835  layerFlagsElem.appendChild( flagElem );
836  }
837  layerElement.appendChild( layerFlagsElem );
838  }
839 
840  if ( categories.testFlag( Temporal ) )
841  {
842  if ( QgsMapLayerTemporalProperties *properties = const_cast< QgsMapLayer * >( this )->temporalProperties() )
843  properties->writeXml( layerElement, document, context );
844  }
845 
846  if ( categories.testFlag( Elevation ) )
847  {
848  if ( QgsMapLayerElevationProperties *properties = const_cast< QgsMapLayer * >( this )->elevationProperties() )
849  properties->writeXml( layerElement, document, context );
850  }
851 
852  if ( categories.testFlag( Notes ) && QgsLayerNotesUtils::layerHasNotes( this ) )
853  {
854  QDomElement notesElem = document.createElement( QStringLiteral( "userNotes" ) );
855  notesElem.setAttribute( QStringLiteral( "value" ), QgsLayerNotesUtils::layerNotes( this ) );
856  layerElement.appendChild( notesElem );
857  }
858 
859  // custom properties
860  if ( categories.testFlag( CustomProperties ) )
861  {
862  writeCustomProperties( layerElement, document );
863  }
864 }
865 
866 
867 bool QgsMapLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
868 {
870 
871  Q_UNUSED( layer_node )
872  Q_UNUSED( document )
873  Q_UNUSED( context )
874  // NOP by default; children will over-ride with behavior specific to them
875 
876  return true;
877 }
878 
879 QString QgsMapLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
880 {
882 
883  Q_UNUSED( context )
884  return source;
885 }
886 
887 QString QgsMapLayer::decodedSource( const QString &source, const QString &dataProvider, const QgsReadWriteContext &context ) const
888 {
890 
891  Q_UNUSED( context )
892  Q_UNUSED( dataProvider )
893  return source;
894 }
895 
897 {
899 
901  if ( m3DRenderer )
902  m3DRenderer->resolveReferences( *project );
903 }
904 
905 
906 void QgsMapLayer::readCustomProperties( const QDomNode &layerNode, const QString &keyStartsWith )
907 {
909 
910  const QgsObjectCustomProperties oldKeys = mCustomProperties;
911 
912  mCustomProperties.readXml( layerNode, keyStartsWith );
913 
914  for ( const QString &key : mCustomProperties.keys() )
915  {
916  if ( !oldKeys.contains( key ) || mCustomProperties.value( key ) != oldKeys.value( key ) )
917  {
918  emit customPropertyChanged( key );
919  }
920  }
921 }
922 
923 void QgsMapLayer::writeCustomProperties( QDomNode &layerNode, QDomDocument &doc ) const
924 {
926 
927  mCustomProperties.writeXml( layerNode, doc );
928 }
929 
930 void QgsMapLayer::readStyleManager( const QDomNode &layerNode )
931 {
933 
934  const QDomElement styleMgrElem = layerNode.firstChildElement( QStringLiteral( "map-layer-style-manager" ) );
935  if ( !styleMgrElem.isNull() )
936  mStyleManager->readXml( styleMgrElem );
937  else
938  mStyleManager->reset();
939 }
940 
941 void QgsMapLayer::writeStyleManager( QDomNode &layerNode, QDomDocument &doc ) const
942 {
944 
945  if ( mStyleManager )
946  {
947  QDomElement styleMgrElem = doc.createElement( QStringLiteral( "map-layer-style-manager" ) );
948  mStyleManager->writeXml( styleMgrElem );
949  layerNode.appendChild( styleMgrElem );
950  }
951 }
952 
954 {
956 
957  return mMapTipTemplate;
958 }
959 
960 void QgsMapLayer::setMapTipTemplate( const QString &mapTip )
961 {
963 
964  if ( mMapTipTemplate == mapTip )
965  return;
966 
967  mMapTipTemplate = mapTip;
968  emit mapTipTemplateChanged();
969 }
970 
971 void QgsMapLayer::setMapTipsEnabled( bool enabled )
972 {
974 
975  if ( mMapTipsEnabled == enabled )
976  return;
977 
978  mMapTipsEnabled = enabled;
979  emit mapTipsEnabledChanged();
980 }
981 
983 {
985 
986  return mMapTipsEnabled;
987 }
988 
990 {
992  if ( layerReadFlags & QgsMapLayer::FlagTrustLayerMetadata )
993  {
995  }
996  if ( layerReadFlags & QgsMapLayer::FlagForceReadOnly )
997  {
999  }
1000 
1001  if ( layerReadFlags & QgsMapLayer::FlagReadExtentFromXml )
1002  {
1003  const QDomNode extent3DNode = layerNode.namedItem( QStringLiteral( "extent3D" ) );
1004  if ( extent3DNode.isNull() )
1005  {
1006  const QDomNode extentNode = layerNode.namedItem( QStringLiteral( "extent" ) );
1007  if ( !extentNode.isNull() )
1008  {
1010  }
1011  }
1012  else
1013  {
1015  }
1016  }
1017 
1018  return flags;
1019 }
1020 
1022 {
1023  // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
1025 
1026  return mValid;
1027 }
1028 
1029 #if 0
1030 void QgsMapLayer::connectNotify( const char *signal )
1031 {
1032  Q_UNUSED( signal )
1033  QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
1034 } // QgsMapLayer::connectNotify
1035 #endif
1036 
1037 bool QgsMapLayer::isInScaleRange( double scale ) const
1038 {
1039  // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1041 
1042  return !mScaleBasedVisibility ||
1043  ( ( mMinScale == 0 || mMinScale * Qgis::SCALE_PRECISION < scale )
1044  && ( mMaxScale == 0 || scale < mMaxScale ) );
1045 }
1046 
1048 {
1049  // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1051 
1052  return mScaleBasedVisibility;
1053 }
1054 
1056 {
1058 
1059  return mAutoRefreshMode != Qgis::AutoRefreshMode::Disabled;;
1060 }
1061 
1063 {
1065 
1066  return mAutoRefreshMode;
1067 }
1068 
1070 {
1072 
1073  return mRefreshTimer->interval();
1074 }
1075 
1077 {
1079 
1080  if ( interval <= 0 )
1081  {
1082  mRefreshTimer->stop();
1083  mRefreshTimer->setInterval( 0 );
1085  }
1086  else
1087  {
1088  mRefreshTimer->setInterval( interval );
1089  }
1090  emit autoRefreshIntervalChanged( mRefreshTimer->isActive() ? mRefreshTimer->interval() : 0 );
1091 }
1092 
1094 {
1096 
1098 }
1099 
1101 {
1103 
1104  if ( mode == mAutoRefreshMode )
1105  return;
1106 
1107  mAutoRefreshMode = mode;
1108  switch ( mAutoRefreshMode )
1109  {
1111  mRefreshTimer->stop();
1112  break;
1113 
1116  if ( mRefreshTimer->interval() > 0 )
1117  mRefreshTimer->start();
1118  break;
1119  }
1120 
1121  emit autoRefreshIntervalChanged( mRefreshTimer->isActive() ? mRefreshTimer->interval() : 0 );
1122 }
1123 
1125 {
1127 
1128  return mMetadata;
1129 }
1130 
1131 void QgsMapLayer::setMaximumScale( double scale )
1132 {
1134 
1135  mMinScale = scale;
1136 }
1137 
1139 {
1141 
1142  return mMinScale;
1143 }
1144 
1145 void QgsMapLayer::setMinimumScale( double scale )
1146 {
1148 
1149  mMaxScale = scale;
1150 }
1151 
1152 void QgsMapLayer::setScaleBasedVisibility( const bool enabled )
1153 {
1155 
1156  mScaleBasedVisibility = enabled;
1157 }
1158 
1160 {
1162 
1163  return mMaxScale;
1164 }
1165 
1166 QStringList QgsMapLayer::subLayers() const
1167 {
1169 
1170  return QStringList();
1171 }
1172 
1173 void QgsMapLayer::setLayerOrder( const QStringList &layers )
1174 {
1176 
1177  Q_UNUSED( layers )
1178 }
1179 
1180 void QgsMapLayer::setSubLayerVisibility( const QString &name, bool vis )
1181 {
1183 
1184  Q_UNUSED( name )
1185  Q_UNUSED( vis )
1186 }
1187 
1189 {
1191 
1192  return false;
1193 }
1194 
1196 {
1197  // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1199 
1200  return mCRS;
1201 }
1202 
1203 void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem &srs, bool emitSignal )
1204 {
1206 
1207  mCRS = srs;
1208 
1209  if ( mShouldValidateCrs && isSpatial() && !mCRS.isValid() && type() != Qgis::LayerType::Annotation )
1210  {
1211  mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( name() ) );
1212  mCRS.validate();
1213  }
1214 
1215  if ( emitSignal )
1216  emit crsChanged();
1217 }
1218 
1220 {
1222 
1223  const QgsDataProvider *lDataProvider = dataProvider();
1224  return lDataProvider ? lDataProvider->transformContext() : QgsCoordinateTransformContext();
1225 }
1226 
1227 QString QgsMapLayer::formatLayerName( const QString &name )
1228 {
1229  QString layerName( name );
1230  layerName.replace( '_', ' ' );
1232  return layerName;
1233 }
1234 
1235 QString QgsMapLayer::baseURI( PropertyType type ) const
1236 {
1238 
1239  QString myURI = publicSource();
1240 
1241  // first get base path for delimited text, spatialite and OGR layers,
1242  // as in these cases URI may contain layer name and/or additional
1243  // information. This also strips prefix in case if VSIFILE mechanism
1244  // is used
1245  if ( providerType() == QLatin1String( "ogr" ) || providerType() == QLatin1String( "delimitedtext" )
1246  || providerType() == QLatin1String( "gdal" ) || providerType() == QLatin1String( "spatialite" ) )
1247  {
1248  QVariantMap components = QgsProviderRegistry::instance()->decodeUri( providerType(), myURI );
1249  myURI = components["path"].toString();
1250  }
1251 
1252  QFileInfo myFileInfo( myURI );
1253  QString key;
1254 
1255  if ( myFileInfo.exists() )
1256  {
1257  // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name
1258  if ( myURI.endsWith( QLatin1String( ".gz" ), Qt::CaseInsensitive ) )
1259  myURI.chop( 3 );
1260  else if ( myURI.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) )
1261  myURI.chop( 4 );
1262  else if ( myURI.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
1263  myURI.chop( 4 );
1264  else if ( myURI.endsWith( QLatin1String( ".tar.gz" ), Qt::CaseInsensitive ) )
1265  myURI.chop( 7 );
1266  else if ( myURI.endsWith( QLatin1String( ".tgz" ), Qt::CaseInsensitive ) )
1267  myURI.chop( 4 );
1268  myFileInfo.setFile( myURI );
1269  // get the file name for our .qml style file
1270  key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
1271  }
1272  else
1273  {
1274  key = publicSource();
1275  }
1276 
1277  return key;
1278 }
1279 
1281 {
1283 
1284  return baseURI( PropertyType::Metadata );
1285 }
1286 
1287 QString QgsMapLayer::saveDefaultMetadata( bool &resultFlag )
1288 {
1290 
1291  if ( const QgsProviderMetadata *metadata = QgsProviderRegistry::instance()->providerMetadata( providerType() ) )
1292  {
1293  if ( metadata->providerCapabilities() & QgsProviderMetadata::SaveLayerMetadata )
1294  {
1295  try
1296  {
1297  QString errorMessage;
1298  resultFlag = QgsProviderRegistry::instance()->saveLayerMetadata( providerType(), mDataSource, mMetadata, errorMessage );
1299  if ( resultFlag )
1300  return tr( "Successfully saved default layer metadata" );
1301  else
1302  return errorMessage;
1303  }
1304  catch ( QgsNotSupportedException &e )
1305  {
1306  resultFlag = false;
1307  return e.what();
1308  }
1309  }
1310  }
1311 
1312  // fallback default metadata saving method, for providers which don't support (or implement) saveLayerMetadata
1313  return saveNamedMetadata( metadataUri(), resultFlag );
1314 }
1315 
1316 QString QgsMapLayer::loadDefaultMetadata( bool &resultFlag )
1317 {
1319 
1320  return loadNamedMetadata( metadataUri(), resultFlag );
1321 }
1322 
1323 QString QgsMapLayer::styleURI() const
1324 {
1326 
1327  return baseURI( PropertyType::Style );
1328 }
1329 
1330 QString QgsMapLayer::loadDefaultStyle( bool &resultFlag )
1331 {
1333 
1334  return loadNamedStyle( styleURI(), resultFlag );
1335 }
1336 
1337 bool QgsMapLayer::loadNamedMetadataFromDatabase( const QString &db, const QString &uri, QString &qmd )
1338 {
1340 
1341  return loadNamedPropertyFromDatabase( db, uri, qmd, PropertyType::Metadata );
1342 }
1343 
1344 bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &uri, QString &qml )
1345 {
1347 
1348  return loadNamedPropertyFromDatabase( db, uri, qml, PropertyType::Style );
1349 }
1350 
1351 bool QgsMapLayer::loadNamedPropertyFromDatabase( const QString &db, const QString &uri, QString &xml, QgsMapLayer::PropertyType type )
1352 {
1354 
1355  QgsDebugMsgLevel( QStringLiteral( "db = %1 uri = %2" ).arg( db, uri ), 4 );
1356 
1357  bool resultFlag = false;
1358 
1359  // read from database
1360  sqlite3_database_unique_ptr database;
1361  sqlite3_statement_unique_ptr statement;
1362 
1363  int myResult;
1364 
1365  QgsDebugMsgLevel( QStringLiteral( "Trying to load style or metadata for \"%1\" from \"%2\"" ).arg( uri, db ), 4 );
1366 
1367  if ( db.isEmpty() || !QFile( db ).exists() )
1368  return false;
1369 
1370  myResult = database.open_v2( db, SQLITE_OPEN_READONLY, nullptr );
1371  if ( myResult != SQLITE_OK )
1372  {
1373  return false;
1374  }
1375 
1376  QString mySql;
1377  switch ( type )
1378  {
1379  case Metadata:
1380  mySql = QStringLiteral( "select qmd from tbl_metadata where metadata=?" );
1381  break;
1382 
1383  case Style:
1384  mySql = QStringLiteral( "select qml from tbl_styles where style=?" );
1385  break;
1386  }
1387 
1388  statement = database.prepare( mySql, myResult );
1389  if ( myResult == SQLITE_OK )
1390  {
1391  QByteArray param = uri.toUtf8();
1392 
1393  if ( sqlite3_bind_text( statement.get(), 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
1394  sqlite3_step( statement.get() ) == SQLITE_ROW )
1395  {
1396  xml = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( statement.get(), 0 ) ) );
1397  resultFlag = true;
1398  }
1399  }
1400  return resultFlag;
1401 }
1402 
1403 
1404 QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories )
1405 {
1407 
1408  return loadNamedStyle( uri, resultFlag, false, categories );
1409 }
1410 
1411 QString QgsMapLayer::loadNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag, StyleCategories categories )
1412 {
1414 
1415  QgsDebugMsgLevel( QStringLiteral( "uri = %1 myURI = %2" ).arg( uri, publicSource() ), 4 );
1416 
1417  resultFlag = false;
1418  if ( uri.isEmpty() )
1419  return QString();
1420 
1421  QDomDocument myDocument( QStringLiteral( "qgis" ) );
1422 
1423  // location of problem associated with errorMsg
1424  int line, column;
1425  QString myErrorMessage;
1426 
1427  QFile myFile( uri );
1428  if ( myFile.open( QFile::ReadOnly ) )
1429  {
1430  QgsDebugMsgLevel( QStringLiteral( "file found %1" ).arg( uri ), 2 );
1431  // read file
1432  resultFlag = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
1433  if ( !resultFlag )
1434  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1435  myFile.close();
1436  }
1437  else
1438  {
1439  const QFileInfo project( QgsProject::instance()->fileName() );
1440  QgsDebugMsgLevel( QStringLiteral( "project fileName: %1" ).arg( project.absoluteFilePath() ), 4 );
1441 
1442  QString xml;
1443  switch ( type )
1444  {
1445  case QgsMapLayer::Style:
1446  {
1447  if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, xml ) ||
1448  ( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) ) ||
1449  loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, xml ) )
1450  {
1451  resultFlag = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1452  if ( !resultFlag )
1453  {
1454  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1455  }
1456  }
1457  else
1458  {
1459  myErrorMessage = tr( "Style not found in database" );
1460  resultFlag = false;
1461  }
1462  break;
1463  }
1464  case QgsMapLayer::Metadata:
1465  {
1466  if ( loadNamedMetadataFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, xml ) ||
1467  ( project.exists() && loadNamedMetadataFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) ) ||
1468  loadNamedMetadataFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, xml ) )
1469  {
1470  resultFlag = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1471  if ( !resultFlag )
1472  {
1473  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1474  }
1475  }
1476  else
1477  {
1478  myErrorMessage = tr( "Metadata not found in database" );
1479  resultFlag = false;
1480  }
1481  break;
1482  }
1483  }
1484  }
1485 
1486  if ( !resultFlag )
1487  {
1488  return myErrorMessage;
1489  }
1490 
1491  switch ( type )
1492  {
1493  case QgsMapLayer::Style:
1494  resultFlag = importNamedStyle( myDocument, myErrorMessage, categories );
1495  if ( !resultFlag )
1496  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1497  break;
1498  case QgsMapLayer::Metadata:
1499  resultFlag = importNamedMetadata( myDocument, myErrorMessage );
1500  if ( !resultFlag )
1501  myErrorMessage = tr( "Loading metadata file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1502  break;
1503  }
1504  return myErrorMessage;
1505 }
1506 
1507 bool QgsMapLayer::importNamedMetadata( QDomDocument &document, QString &errorMessage )
1508 {
1510 
1511  const QDomElement myRoot = document.firstChildElement( QStringLiteral( "qgis" ) );
1512  if ( myRoot.isNull() )
1513  {
1514  errorMessage = tr( "Root <qgis> element could not be found" );
1515  return false;
1516  }
1517 
1518  return mMetadata.readMetadataXml( myRoot );
1519 }
1520 
1521 bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMessage, QgsMapLayer::StyleCategories categories )
1522 {
1524 
1525  const QDomElement myRoot = myDocument.firstChildElement( QStringLiteral( "qgis" ) );
1526  if ( myRoot.isNull() )
1527  {
1528  myErrorMessage = tr( "Root <qgis> element could not be found" );
1529  return false;
1530  }
1531 
1532  // get style file version string, if any
1533  const QgsProjectVersion fileVersion( myRoot.attribute( QStringLiteral( "version" ) ) );
1534  const QgsProjectVersion thisVersion( Qgis::version() );
1535 
1536  if ( thisVersion > fileVersion )
1537  {
1538  QgsProjectFileTransform styleFile( myDocument, fileVersion );
1539  styleFile.updateRevision( thisVersion );
1540  }
1541 
1542  // Get source categories
1543  const QgsMapLayer::StyleCategories sourceCategories = QgsXmlUtils::readFlagAttribute( myRoot, QStringLiteral( "styleCategories" ), QgsMapLayer::AllStyleCategories );
1544 
1545  //Test for matching geometry type on vector layers when applying, if geometry type is given in the style
1546  if ( ( sourceCategories.testFlag( QgsMapLayer::Symbology ) || sourceCategories.testFlag( QgsMapLayer::Symbology3D ) ) &&
1547  ( categories.testFlag( QgsMapLayer::Symbology ) || categories.testFlag( QgsMapLayer::Symbology3D ) ) )
1548  {
1549  if ( type() == Qgis::LayerType::Vector && !myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).isNull() )
1550  {
1551  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( this );
1552  const Qgis::GeometryType importLayerGeometryType = static_cast<Qgis::GeometryType>( myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).text().toInt() );
1553  if ( importLayerGeometryType != Qgis::GeometryType::Unknown && vl->geometryType() != importLayerGeometryType )
1554  {
1555  myErrorMessage = tr( "Cannot apply style with symbology to layer with a different geometry type" );
1556  return false;
1557  }
1558  }
1559  }
1560 
1562  return readSymbology( myRoot, myErrorMessage, context, categories ); // TODO: support relative paths in QML?
1563 }
1564 
1565 void QgsMapLayer::exportNamedMetadata( QDomDocument &doc, QString &errorMsg ) const
1566 {
1568 
1569  QDomImplementation DomImplementation;
1570  const QDomDocumentType documentType = DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1571  QDomDocument myDocument( documentType );
1572 
1573  QDomElement myRootNode = myDocument.createElement( QStringLiteral( "qgis" ) );
1574  myRootNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
1575  myDocument.appendChild( myRootNode );
1576 
1577  if ( !mMetadata.writeMetadataXml( myRootNode, myDocument ) )
1578  {
1579  errorMsg = QObject::tr( "Could not save metadata" );
1580  return;
1581  }
1582 
1583  doc = myDocument;
1584 }
1585 
1586 void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1587 {
1589 
1590  QDomImplementation DomImplementation;
1591  const QDomDocumentType documentType = DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1592  QDomDocument myDocument( documentType );
1593 
1594  QDomElement myRootNode = myDocument.createElement( QStringLiteral( "qgis" ) );
1595  myRootNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
1596  myDocument.appendChild( myRootNode );
1597 
1598  if ( !writeSymbology( myRootNode, myDocument, errorMsg, context, categories ) ) // TODO: support relative paths in QML?
1599  {
1600  errorMsg = QObject::tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1601  return;
1602  }
1603 
1604  /*
1605  * Check to see if the layer is vector - in which case we should also export its geometryType
1606  * to avoid eventually pasting to a layer with a different geometry
1607  */
1608  if ( type() == Qgis::LayerType::Vector )
1609  {
1610  //Getting the selectionLayer geometry
1611  const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( this );
1612  const QString geoType = QString::number( static_cast<int>( vl->geometryType() ) );
1613 
1614  //Adding geometryinformation
1615  QDomElement layerGeometryType = myDocument.createElement( QStringLiteral( "layerGeometryType" ) );
1616  const QDomText type = myDocument.createTextNode( geoType );
1617 
1618  layerGeometryType.appendChild( type );
1619  myRootNode.appendChild( layerGeometryType );
1620  }
1621 
1622  doc = myDocument;
1623 }
1624 
1625 QString QgsMapLayer::saveDefaultStyle( bool &resultFlag )
1626 {
1628 
1629  return saveDefaultStyle( resultFlag, AllStyleCategories );
1630 }
1631 
1632 QString QgsMapLayer::saveDefaultStyle( bool &resultFlag, StyleCategories categories )
1633 {
1635 
1636  return saveNamedStyle( styleURI(), resultFlag, categories );
1637 }
1638 
1639 QString QgsMapLayer::saveNamedMetadata( const QString &uri, bool &resultFlag )
1640 {
1642 
1643  return saveNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
1644 }
1645 
1646 QString QgsMapLayer::loadNamedMetadata( const QString &uri, bool &resultFlag )
1647 {
1649 
1650  return loadNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
1651 }
1652 
1653 QString QgsMapLayer::saveNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag, StyleCategories categories )
1654 {
1656 
1657  // check if the uri is a file or ends with .qml/.qmd,
1658  // which indicates that it should become one
1659  // everything else goes to the database
1660  QString filename;
1661 
1662  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1663  if ( vlayer && vlayer->providerType() == QLatin1String( "ogr" ) )
1664  {
1665  QStringList theURIParts = uri.split( '|' );
1666  filename = theURIParts[0];
1667  }
1668  else if ( vlayer && vlayer->providerType() == QLatin1String( "gpx" ) )
1669  {
1670  QStringList theURIParts = uri.split( '?' );
1671  filename = theURIParts[0];
1672  }
1673  else if ( vlayer && vlayer->providerType() == QLatin1String( "delimitedtext" ) )
1674  {
1675  filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
1676  // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
1677  if ( filename.isEmpty() )
1678  filename = uri;
1679  }
1680  else
1681  {
1682  filename = uri;
1683  }
1684 
1685  QString myErrorMessage;
1686  QDomDocument myDocument;
1687  switch ( type )
1688  {
1689  case Metadata:
1690  exportNamedMetadata( myDocument, myErrorMessage );
1691  break;
1692 
1693  case Style:
1694  const QgsReadWriteContext context;
1695  exportNamedStyle( myDocument, myErrorMessage, context, categories );
1696  break;
1697  }
1698 
1699  const QFileInfo myFileInfo( filename );
1700  if ( myFileInfo.exists() || filename.endsWith( QgsMapLayer::extensionPropertyType( type ), Qt::CaseInsensitive ) )
1701  {
1702  const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1703  if ( !myDirInfo.isWritable() )
1704  {
1705  resultFlag = false;
1706  return tr( "The directory containing your dataset needs to be writable!" );
1707  }
1708 
1709  // now construct the file name for our .qml or .qmd file
1710  const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
1711 
1712  QFile myFile( myFileName );
1713  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1714  {
1715  QTextStream myFileStream( &myFile );
1716  // save as utf-8 with 2 spaces for indents
1717  myDocument.save( myFileStream, 2 );
1718  myFile.close();
1719  resultFlag = true;
1720  switch ( type )
1721  {
1722  case Metadata:
1723  return tr( "Created default metadata file as %1" ).arg( myFileName );
1724 
1725  case Style:
1726  return tr( "Created default style file as %1" ).arg( myFileName );
1727  }
1728 
1729  }
1730  else
1731  {
1732  resultFlag = false;
1733  switch ( type )
1734  {
1735  case Metadata:
1736  return tr( "ERROR: Failed to created default metadata file as %1. Check file permissions and retry." ).arg( myFileName );
1737 
1738  case Style:
1739  return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
1740  }
1741  }
1742  }
1743  else
1744  {
1745  const QString qml = myDocument.toString();
1746 
1747  // read from database
1748  sqlite3_database_unique_ptr database;
1749  sqlite3_statement_unique_ptr statement;
1750 
1751  int myResult = database.open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ) );
1752  if ( myResult != SQLITE_OK )
1753  {
1754  return tr( "User database could not be opened." );
1755  }
1756 
1757  QByteArray param0 = uri.toUtf8();
1758  QByteArray param1 = qml.toUtf8();
1759 
1760  QString mySql;
1761  switch ( type )
1762  {
1763  case Metadata:
1764  mySql = QStringLiteral( "create table if not exists tbl_metadata(metadata varchar primary key,qmd varchar)" );
1765  break;
1766 
1767  case Style:
1768  mySql = QStringLiteral( "create table if not exists tbl_styles(style varchar primary key,qml varchar)" );
1769  break;
1770  }
1771 
1772  statement = database.prepare( mySql, myResult );
1773  if ( myResult == SQLITE_OK )
1774  {
1775  if ( sqlite3_step( statement.get() ) != SQLITE_DONE )
1776  {
1777  resultFlag = false;
1778  switch ( type )
1779  {
1780  case Metadata:
1781  return tr( "The metadata table could not be created." );
1782 
1783  case Style:
1784  return tr( "The style table could not be created." );
1785  }
1786  }
1787  }
1788 
1789  switch ( type )
1790  {
1791  case Metadata:
1792  mySql = QStringLiteral( "insert into tbl_metadata(metadata,qmd) values (?,?)" );
1793  break;
1794 
1795  case Style:
1796  mySql = QStringLiteral( "insert into tbl_styles(style,qml) values (?,?)" );
1797  break;
1798  }
1799  statement = database.prepare( mySql, myResult );
1800  if ( myResult == SQLITE_OK )
1801  {
1802  if ( sqlite3_bind_text( statement.get(), 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1803  sqlite3_bind_text( statement.get(), 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1804  sqlite3_step( statement.get() ) == SQLITE_DONE )
1805  {
1806  resultFlag = true;
1807  switch ( type )
1808  {
1809  case Metadata:
1810  myErrorMessage = tr( "The metadata %1 was saved to database" ).arg( uri );
1811  break;
1812 
1813  case Style:
1814  myErrorMessage = tr( "The style %1 was saved to database" ).arg( uri );
1815  break;
1816  }
1817  }
1818  }
1819 
1820  if ( !resultFlag )
1821  {
1822  QString mySql;
1823  switch ( type )
1824  {
1825  case Metadata:
1826  mySql = QStringLiteral( "update tbl_metadata set qmd=? where metadata=?" );
1827  break;
1828 
1829  case Style:
1830  mySql = QStringLiteral( "update tbl_styles set qml=? where style=?" );
1831  break;
1832  }
1833  statement = database.prepare( mySql, myResult );
1834  if ( myResult == SQLITE_OK )
1835  {
1836  if ( sqlite3_bind_text( statement.get(), 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
1837  sqlite3_bind_text( statement.get(), 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
1838  sqlite3_step( statement.get() ) == SQLITE_DONE )
1839  {
1840  resultFlag = true;
1841  switch ( type )
1842  {
1843  case Metadata:
1844  myErrorMessage = tr( "The metadata %1 was updated in the database." ).arg( uri );
1845  break;
1846 
1847  case Style:
1848  myErrorMessage = tr( "The style %1 was updated in the database." ).arg( uri );
1849  break;
1850  }
1851  }
1852  else
1853  {
1854  resultFlag = false;
1855  switch ( type )
1856  {
1857  case Metadata:
1858  myErrorMessage = tr( "The metadata %1 could not be updated in the database." ).arg( uri );
1859  break;
1860 
1861  case Style:
1862  myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( uri );
1863  break;
1864  }
1865  }
1866  }
1867  else
1868  {
1869  resultFlag = false;
1870  switch ( type )
1871  {
1872  case Metadata:
1873  myErrorMessage = tr( "The metadata %1 could not be inserted into database." ).arg( uri );
1874  break;
1875 
1876  case Style:
1877  myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( uri );
1878  break;
1879  }
1880  }
1881  }
1882  }
1883 
1884  return myErrorMessage;
1885 }
1886 
1887 QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag, StyleCategories categories )
1888 {
1890 
1891  return saveNamedProperty( uri, QgsMapLayer::Style, resultFlag, categories );
1892 }
1893 
1894 void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
1895 {
1896 
1897  return exportSldStyleV2( doc, errorMsg, QgsSldExportContext() );
1898 }
1899 
1900 void QgsMapLayer::exportSldStyleV2( QDomDocument &doc, QString &errorMsg, const QgsSldExportContext &exportContext ) const
1901 {
1903 
1904  QDomDocument myDocument = QDomDocument();
1905 
1906  const QDomNode header = myDocument.createProcessingInstruction( QStringLiteral( "xml" ), QStringLiteral( "version=\"1.0\" encoding=\"UTF-8\"" ) );
1907  myDocument.appendChild( header );
1908 
1909  const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
1910  const QgsRasterLayer *rlayer = qobject_cast<const QgsRasterLayer *>( this );
1911  if ( !vlayer && !rlayer )
1912  {
1913  errorMsg = tr( "Could not save symbology because:\n%1" )
1914  .arg( tr( "Only vector and raster layers are supported" ) );
1915  return;
1916  }
1917 
1918  // Create the root element
1919  QDomElement root = myDocument.createElementNS( QStringLiteral( "http://www.opengis.net/sld" ), QStringLiteral( "StyledLayerDescriptor" ) );
1920  QDomElement layerNode;
1921  if ( vlayer )
1922  {
1923  root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.1.0" ) );
1924  root.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" ) );
1925  root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
1926  root.setAttribute( QStringLiteral( "xmlns:se" ), QStringLiteral( "http://www.opengis.net/se" ) );
1927  root.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
1928  root.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
1929  myDocument.appendChild( root );
1930 
1931  // Create the NamedLayer element
1932  layerNode = myDocument.createElement( QStringLiteral( "NamedLayer" ) );
1933  root.appendChild( layerNode );
1934  }
1935 
1936  // note: Only SLD 1.0 version is generated because seems none is using SE1.1.0 at least for rasters
1937  if ( rlayer )
1938  {
1939  // Create the root element
1940  root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0.0" ) );
1941  root.setAttribute( QStringLiteral( "xmlns:gml" ), QStringLiteral( "http://www.opengis.net/gml" ) );
1942  root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
1943  root.setAttribute( QStringLiteral( "xmlns:sld" ), QStringLiteral( "http://www.opengis.net/sld" ) );
1944  myDocument.appendChild( root );
1945 
1946  // Create the NamedLayer element
1947  layerNode = myDocument.createElement( QStringLiteral( "UserLayer" ) );
1948  root.appendChild( layerNode );
1949  }
1950 
1951  QVariantMap props;
1952 
1953  QVariant context;
1954  context.setValue( exportContext );
1955 
1956  props[ QStringLiteral( "SldExportContext" ) ] = context;
1957 
1958  if ( hasScaleBasedVisibility() )
1959  {
1960  props[ QStringLiteral( "scaleMinDenom" ) ] = QString::number( mMinScale );
1961  props[ QStringLiteral( "scaleMaxDenom" ) ] = QString::number( mMaxScale );
1962  }
1963 
1964  if ( vlayer )
1965  {
1966  if ( !vlayer->writeSld( layerNode, myDocument, errorMsg, props ) )
1967  {
1968  errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1969  return;
1970  }
1971  }
1972 
1973  if ( rlayer )
1974  {
1975  if ( !rlayer->writeSld( layerNode, myDocument, errorMsg, props ) )
1976  {
1977  errorMsg = tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1978  return;
1979  }
1980  }
1981 
1982  doc = myDocument;
1983 }
1984 
1985 QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const
1986 {
1987  QgsSldExportContext context;
1988  context.setExportFilePath( uri );
1989  return saveSldStyleV2( resultFlag, context );
1990 }
1991 
1992 QString QgsMapLayer::saveSldStyleV2( bool &resultFlag, const QgsSldExportContext &exportContext ) const
1993 {
1995 
1996  const QgsMapLayer *mlayer = qobject_cast<const QgsMapLayer *>( this );
1997 
1998  const QString uri { exportContext.exportFilePath() };
1999 
2000  // check if the uri is a file or ends with .sld,
2001  // which indicates that it should become one
2002  QString filename;
2003  if ( mlayer->providerType() == QLatin1String( "ogr" ) )
2004  {
2005  QStringList theURIParts = uri.split( '|' );
2006  filename = theURIParts[0];
2007  }
2008  else if ( mlayer->providerType() == QLatin1String( "gpx" ) )
2009  {
2010  QStringList theURIParts = uri.split( '?' );
2011  filename = theURIParts[0];
2012  }
2013  else if ( mlayer->providerType() == QLatin1String( "delimitedtext" ) )
2014  {
2015  filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
2016  // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
2017  if ( filename.isEmpty() )
2018  filename = uri;
2019  }
2020  else
2021  {
2022  filename = uri;
2023  }
2024 
2025  const QFileInfo myFileInfo( filename );
2026  if ( myFileInfo.exists() || filename.endsWith( QLatin1String( ".sld" ), Qt::CaseInsensitive ) )
2027  {
2028  const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
2029  if ( !myDirInfo.isWritable() )
2030  {
2031  resultFlag = false;
2032  return tr( "The directory containing your dataset needs to be writable!" );
2033  }
2034 
2035  // now construct the file name for our .sld style file
2036  const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";
2037 
2038  QString errorMsg;
2039  QDomDocument myDocument;
2040 
2041  QgsSldExportContext context { exportContext };
2042  context.setExportFilePath( myFileName );
2043 
2044  mlayer->exportSldStyleV2( myDocument, errorMsg, context );
2045 
2046  if ( !errorMsg.isNull() )
2047  {
2048  resultFlag = false;
2049  return errorMsg;
2050  }
2051 
2052  QFile myFile( myFileName );
2053  if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
2054  {
2055  QTextStream myFileStream( &myFile );
2056  // save as utf-8 with 2 spaces for indents
2057  myDocument.save( myFileStream, 2 );
2058  myFile.close();
2059  resultFlag = true;
2060  return tr( "Created default style file as %1" ).arg( myFileName );
2061  }
2062  }
2063 
2064  resultFlag = false;
2065  return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
2066 
2067 }
2068 
2069 QString QgsMapLayer::loadSldStyle( const QString &uri, bool &resultFlag )
2070 {
2072 
2073  resultFlag = false;
2074 
2075  QDomDocument myDocument;
2076 
2077  // location of problem associated with errorMsg
2078  int line, column;
2079  QString myErrorMessage;
2080 
2081  QFile myFile( uri );
2082  if ( myFile.open( QFile::ReadOnly ) )
2083  {
2084  // read file
2085  resultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
2086  if ( !resultFlag )
2087  myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
2088  myFile.close();
2089  }
2090  else
2091  {
2092  myErrorMessage = tr( "Unable to open file %1" ).arg( uri );
2093  }
2094 
2095  if ( !resultFlag )
2096  {
2097  return myErrorMessage;
2098  }
2099 
2100  // check for root SLD element
2101  const QDomElement myRoot = myDocument.firstChildElement( QStringLiteral( "StyledLayerDescriptor" ) );
2102  if ( myRoot.isNull() )
2103  {
2104  myErrorMessage = QStringLiteral( "Error: StyledLayerDescriptor element not found in %1" ).arg( uri );
2105  resultFlag = false;
2106  return myErrorMessage;
2107  }
2108 
2109  // now get the style node out and pass it over to the layer
2110  // to deserialise...
2111  const QDomElement namedLayerElem = myRoot.firstChildElement( QStringLiteral( "NamedLayer" ) );
2112  if ( namedLayerElem.isNull() )
2113  {
2114  myErrorMessage = QStringLiteral( "Info: NamedLayer element not found." );
2115  resultFlag = false;
2116  return myErrorMessage;
2117  }
2118 
2119  QString errorMsg;
2120  resultFlag = readSld( namedLayerElem, errorMsg );
2121  if ( !resultFlag )
2122  {
2123  myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, errorMsg );
2124  return myErrorMessage;
2125  }
2126 
2127  return QString();
2128 }
2129 
2130 bool QgsMapLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
2131 {
2133 
2134  Q_UNUSED( node )
2135  Q_UNUSED( errorMessage )
2136  Q_UNUSED( context )
2137  Q_UNUSED( categories )
2138  return false;
2139 }
2140 
2141 bool QgsMapLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage,
2142  const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2143 {
2145 
2146  Q_UNUSED( node )
2147  Q_UNUSED( doc )
2148  Q_UNUSED( errorMessage )
2149  Q_UNUSED( context )
2150  Q_UNUSED( categories )
2151  return false;
2152 }
2153 
2154 
2155 void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider,
2156  bool loadDefaultStyleFlag )
2157 {
2159 
2160  const QgsDataProvider::ProviderOptions options;
2161 
2163  if ( loadDefaultStyleFlag )
2164  {
2166  }
2167 
2169  {
2171  }
2172  setDataSource( dataSource, baseName, provider, options, flags );
2173 }
2174 
2175 void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider,
2176  const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag )
2177 {
2179 
2181  if ( loadDefaultStyleFlag )
2182  {
2184  }
2185 
2187  {
2189  }
2190  setDataSource( dataSource, baseName, provider, options, flags );
2191 }
2192 
2193 void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider,
2195 {
2197 
2200  {
2202  }
2203  setDataSourcePrivate( dataSource, baseName, provider, options, flags );
2204  emit dataSourceChanged();
2205  emit dataChanged();
2206  triggerRepaint();
2207 }
2208 
2209 
2210 void QgsMapLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider,
2212 {
2214 
2215  Q_UNUSED( dataSource )
2216  Q_UNUSED( baseName )
2217  Q_UNUSED( provider )
2218  Q_UNUSED( options )
2219  Q_UNUSED( flags )
2220 }
2221 
2222 
2224 {
2226 
2227  return mProviderKey;
2228 }
2229 
2230 void QgsMapLayer::readCommonStyle( const QDomElement &layerElement, const QgsReadWriteContext &context,
2231  QgsMapLayer::StyleCategories categories )
2232 {
2234 
2235  if ( categories.testFlag( Symbology3D ) )
2236  {
2237  const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "3D Symbology" ) );
2238 
2239  QgsAbstract3DRenderer *r3D = nullptr;
2240  QDomElement renderer3DElem = layerElement.firstChildElement( QStringLiteral( "renderer-3d" ) );
2241  if ( !renderer3DElem.isNull() )
2242  {
2243  const QString type3D = renderer3DElem.attribute( QStringLiteral( "type" ) );
2245  if ( meta3D )
2246  {
2247  r3D = meta3D->createRenderer( renderer3DElem, context );
2248  }
2249  }
2250  setRenderer3D( r3D );
2251  }
2252 
2253  if ( categories.testFlag( CustomProperties ) )
2254  {
2255  // read custom properties before passing reading further to a subclass, so that
2256  // the subclass can also read custom properties
2257  readCustomProperties( layerElement );
2258  }
2259 
2260  // use scale dependent visibility flag
2261  if ( categories.testFlag( Rendering ) )
2262  {
2263  setScaleBasedVisibility( layerElement.attribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ) ).toInt() == 1 );
2264  if ( layerElement.hasAttribute( QStringLiteral( "minimumScale" ) ) )
2265  {
2266  // older element, when scales were reversed
2267  setMaximumScale( layerElement.attribute( QStringLiteral( "minimumScale" ) ).toDouble() );
2268  setMinimumScale( layerElement.attribute( QStringLiteral( "maximumScale" ) ).toDouble() );
2269  }
2270  else
2271  {
2272  setMaximumScale( layerElement.attribute( QStringLiteral( "maxScale" ) ).toDouble() );
2273  setMinimumScale( layerElement.attribute( QStringLiteral( "minScale" ) ).toDouble() );
2274  }
2275  }
2276 
2277  if ( categories.testFlag( LayerConfiguration ) )
2278  {
2279  // flags
2280  const QDomElement flagsElem = layerElement.firstChildElement( QStringLiteral( "flags" ) );
2281  LayerFlags flags = mFlags;
2282  const auto enumMap = qgsEnumMap<QgsMapLayer::LayerFlag>();
2283  for ( auto it = enumMap.constBegin(); it != enumMap.constEnd(); ++it )
2284  {
2285  const QDomNode flagNode = flagsElem.namedItem( it.value() );
2286  if ( flagNode.isNull() )
2287  continue;
2288  const bool flagValue = flagNode.toElement().text() == "1" ? true : false;
2289  if ( flags.testFlag( it.key() ) && !flagValue )
2290  flags &= ~it.key();
2291  else if ( !flags.testFlag( it.key() ) && flagValue )
2292  flags |= it.key();
2293  }
2294  setFlags( flags );
2295  }
2296 
2297  if ( categories.testFlag( Temporal ) )
2298  {
2299  const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Temporal" ) );
2300 
2302  properties->readXml( layerElement.toElement(), context );
2303  }
2304 
2305  if ( categories.testFlag( Elevation ) )
2306  {
2307  const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Elevation" ) );
2308 
2310  properties->readXml( layerElement.toElement(), context );
2311  }
2312 
2313  if ( categories.testFlag( Notes ) )
2314  {
2315  const QDomElement notesElem = layerElement.firstChildElement( QStringLiteral( "userNotes" ) );
2316  if ( !notesElem.isNull() )
2317  {
2318  const QString notes = notesElem.attribute( QStringLiteral( "value" ) );
2319  QgsLayerNotesUtils::setLayerNotes( this, notes );
2320  }
2321  }
2322 }
2323 
2325 {
2327 
2328  return mUndoStack;
2329 }
2330 
2332 {
2334 
2335  return mUndoStackStyles;
2336 }
2337 
2339 {
2341 
2342  return mCustomProperties.keys();
2343 }
2344 
2345 void QgsMapLayer::setCustomProperty( const QString &key, const QVariant &value )
2346 {
2348 
2349  if ( !mCustomProperties.contains( key ) || mCustomProperties.value( key ) != value )
2350  {
2351  mCustomProperties.setValue( key, value );
2352  emit customPropertyChanged( key );
2353  }
2354 }
2355 
2357 {
2359 
2360  mCustomProperties = properties;
2361  for ( const QString &key : mCustomProperties.keys() )
2362  {
2363  emit customPropertyChanged( key );
2364  }
2365 }
2366 
2368 {
2370 
2371  return mCustomProperties;
2372 }
2373 
2374 QVariant QgsMapLayer::customProperty( const QString &value, const QVariant &defaultValue ) const
2375 {
2376  // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
2378 
2379  return mCustomProperties.value( value, defaultValue );
2380 }
2381 
2382 void QgsMapLayer::removeCustomProperty( const QString &key )
2383 {
2385 
2386  if ( mCustomProperties.contains( key ) )
2387  {
2388  mCustomProperties.remove( key );
2389  emit customPropertyChanged( key );
2390  }
2391 }
2392 
2393 int QgsMapLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError )
2394 {
2396 
2397  return QgsProviderRegistry::instance()->listStyles( mProviderKey, mDataSource, ids, names, descriptions, msgError );
2398 }
2399 
2400 QString QgsMapLayer::getStyleFromDatabase( const QString &styleId, QString &msgError )
2401 {
2403 
2404  return QgsProviderRegistry::instance()->getStyleById( mProviderKey, mDataSource, styleId, msgError );
2405 }
2406 
2407 bool QgsMapLayer::deleteStyleFromDatabase( const QString &styleId, QString &msgError )
2408 {
2410 
2411  return QgsProviderRegistry::instance()->deleteStyleById( mProviderKey, mDataSource, styleId, msgError );
2412 }
2413 
2414 void QgsMapLayer::saveStyleToDatabase( const QString &name, const QString &description,
2415  bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories )
2416 {
2418 
2419  QString sldStyle, qmlStyle;
2420  QDomDocument qmlDocument, sldDocument;
2421  QgsReadWriteContext context;
2422  exportNamedStyle( qmlDocument, msgError, context, categories );
2423  if ( !msgError.isNull() )
2424  {
2425  return;
2426  }
2427  qmlStyle = qmlDocument.toString();
2428 
2429  this->exportSldStyle( sldDocument, msgError );
2430  if ( !msgError.isNull() )
2431  {
2432  return;
2433  }
2434  sldStyle = sldDocument.toString();
2435 
2437  mDataSource, qmlStyle, sldStyle, name,
2438  description, uiFileContent, useAsDefault, msgError );
2439 }
2440 
2441 QString QgsMapLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB, QgsMapLayer::StyleCategories categories )
2442 {
2444 
2445  QString returnMessage;
2446  QString qml, errorMsg;
2447  QString styleName;
2448  if ( !loadFromLocalDB && dataProvider() && dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
2449  {
2450  qml = QgsProviderRegistry::instance()->loadStoredStyle( mProviderKey, mDataSource, styleName, errorMsg );
2451  }
2452 
2453  // Style was successfully loaded from provider storage
2454  if ( !qml.isEmpty() )
2455  {
2456  QDomDocument myDocument( QStringLiteral( "qgis" ) );
2457  myDocument.setContent( qml );
2458  resultFlag = importNamedStyle( myDocument, errorMsg );
2459  returnMessage = QObject::tr( "Loaded from Provider" );
2460  }
2461  else
2462  {
2463  returnMessage = loadNamedProperty( theURI, PropertyType::Style, resultFlag, categories );
2464  }
2465 
2466  if ( ! styleName.isEmpty() )
2467  {
2468  styleManager()->renameStyle( styleManager()->currentStyle(), styleName );
2469  }
2470 
2471  if ( resultFlag )
2472  emit styleLoaded( categories );
2473 
2474  return returnMessage;
2475 }
2476 
2478 {
2480 
2481  return mError;
2482 }
2483 
2485 {
2487 
2488  return false;
2489 }
2490 
2492 {
2494 
2495  return false;
2496 }
2497 
2499 {
2501 
2502  return true;
2503 }
2504 
2506 {
2508 
2509  // invalid layers are temporary? -- who knows?!
2510  if ( !isValid() )
2511  return false;
2512 
2513  if ( mProviderKey == QLatin1String( "memory" ) )
2514  return true;
2515 
2516  const QVariantMap sourceParts = QgsProviderRegistry::instance()->decodeUri( mProviderKey, mDataSource );
2517  const QString path = sourceParts.value( QStringLiteral( "path" ) ).toString();
2518  if ( path.isEmpty() )
2519  return false;
2520 
2521  // check if layer path is inside one of the standard temporary file locations for this platform
2522  const QStringList tempPaths = QStandardPaths::standardLocations( QStandardPaths::TempLocation );
2523  for ( const QString &tempPath : tempPaths )
2524  {
2525  if ( path.startsWith( tempPath ) )
2526  return true;
2527  }
2528 
2529  return false;
2530 }
2531 
2532 void QgsMapLayer::setValid( bool valid )
2533 {
2535 
2536  if ( mValid == valid )
2537  return;
2538 
2539  mValid = valid;
2540  emit isValidChanged();
2541 }
2542 
2544 {
2546 
2547  if ( legend == mLegend )
2548  return;
2549 
2550  delete mLegend;
2551  mLegend = legend;
2552 
2553  if ( mLegend )
2554  {
2555  mLegend->setParent( this );
2556  connect( mLegend, &QgsMapLayerLegend::itemsChanged, this, &QgsMapLayer::legendChanged, Qt::UniqueConnection );
2557  }
2558 
2559  emit legendChanged();
2560 }
2561 
2563 {
2565 
2566  return mLegend;
2567 }
2568 
2570 {
2572 
2573  return mStyleManager;
2574 }
2575 
2577 {
2579 
2580  if ( renderer == m3DRenderer )
2581  return;
2582 
2583  delete m3DRenderer;
2584  m3DRenderer = renderer;
2585  emit renderer3DChanged();
2586  emit repaintRequested();
2587  trigger3DUpdate();
2588 }
2589 
2591 {
2593 
2594  return m3DRenderer;
2595 }
2596 
2597 void QgsMapLayer::triggerRepaint( bool deferredUpdate )
2598 {
2600 
2601  if ( mRepaintRequestedFired )
2602  return;
2603  mRepaintRequestedFired = true;
2604  emit repaintRequested( deferredUpdate );
2605  mRepaintRequestedFired = false;
2606 }
2607 
2609 {
2611 
2612  emit request3DUpdate();
2613 }
2614 
2616 {
2618 
2619  mMetadata = metadata;
2620 // mMetadata.saveToLayer( this );
2621  emit metadataChanged();
2622 }
2623 
2625 {
2627 
2628  return QString();
2629 }
2630 
2631 QDateTime QgsMapLayer::timestamp() const
2632 {
2634 
2635  return QDateTime();
2636 }
2637 
2639 {
2641 
2642  if ( !mBlockStyleChangedSignal )
2643  emit styleChanged();
2644 }
2645 
2647 {
2648  updateExtent( extent );
2649 }
2650 
2651 void QgsMapLayer::setExtent3D( const QgsBox3D &extent )
2652 {
2654 
2655  updateExtent( extent );
2656 }
2657 
2658 bool QgsMapLayer::isReadOnly() const
2659 {
2661 
2662  return true;
2663 }
2664 
2666 {
2668 
2669  return mOriginalXmlProperties;
2670 }
2671 
2672 void QgsMapLayer::setOriginalXmlProperties( const QString &originalXmlProperties )
2673 {
2675 
2676  mOriginalXmlProperties = originalXmlProperties;
2677 }
2678 
2679 QString QgsMapLayer::generateId( const QString &layerName )
2680 {
2681  // Generate the unique ID of this layer
2682  const QString uuid = QUuid::createUuid().toString();
2683  // trim { } from uuid
2684  QString id = layerName + '_' + uuid.mid( 1, uuid.length() - 2 );
2685  // Tidy the ID up to avoid characters that may cause problems
2686  // elsewhere (e.g in some parts of XML). Replaces every non-word
2687  // character (word characters are the alphabet, numbers and
2688  // underscore) with an underscore.
2689  // Note that the first backslash in the regular expression is
2690  // there for the compiler, so the pattern is actually \W
2691  const thread_local QRegularExpression idRx( QStringLiteral( "[\\W]" ) );
2692  id.replace( idRx, QStringLiteral( "_" ) );
2693  return id;
2694 }
2695 
2697 {
2699 
2700  return true;
2701 }
2702 
2704 {
2706 
2707  return mapTipsEnabled() && !mMapTipTemplate.isEmpty();
2708 }
2709 
2710 void QgsMapLayer::setProviderType( const QString &providerType )
2711 {
2713 
2715 }
2716 
2717 QSet<QgsMapLayerDependency> QgsMapLayer::dependencies() const
2718 {
2720 
2721  return mDependencies;
2722 }
2723 
2724 bool QgsMapLayer::setDependencies( const QSet<QgsMapLayerDependency> &oDeps )
2725 {
2727 
2728  QSet<QgsMapLayerDependency> deps;
2729  const auto constODeps = oDeps;
2730  for ( const QgsMapLayerDependency &dep : constODeps )
2731  {
2732  if ( dep.origin() == QgsMapLayerDependency::FromUser )
2733  deps << dep;
2734  }
2735 
2736  mDependencies = deps;
2737  emit dependenciesChanged();
2738  return true;
2739 }
2740 
2742 {
2744 
2745  QgsDataProvider *lDataProvider = dataProvider();
2746 
2747  if ( !lDataProvider )
2748  return;
2749 
2750  if ( enabled && !isRefreshOnNotifyEnabled() )
2751  {
2752  lDataProvider->setListening( enabled );
2753  connect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
2754  }
2755  else if ( !enabled && isRefreshOnNotifyEnabled() )
2756  {
2757  // we don't want to disable provider listening because someone else could need it (e.g. actions)
2758  disconnect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
2759  }
2760  mIsRefreshOnNofifyEnabled = enabled;
2761 }
2762 
2764 {
2766 
2767  if ( QgsMapLayerStore *store = qobject_cast<QgsMapLayerStore *>( parent() ) )
2768  {
2769  return qobject_cast<QgsProject *>( store->parent() );
2770  }
2771  return nullptr;
2772 }
2773 
2774 void QgsMapLayer::onNotified( const QString &message )
2775 {
2777 
2778  if ( refreshOnNotifyMessage().isEmpty() || refreshOnNotifyMessage() == message )
2779  {
2780  triggerRepaint();
2781  emit dataChanged();
2782  }
2783 }
2784 
2785 QgsRectangle QgsMapLayer::wgs84Extent( bool forceRecalculate ) const
2786 {
2788 
2790 
2791  if ( ! forceRecalculate && ! mWgs84Extent.isNull() )
2792  {
2793  wgs84Extent = mWgs84Extent;
2794  }
2795  else if ( ! mExtent2D.isNull() || ! mExtent3D.isNull() )
2796  {
2798  transformer.setBallparkTransformsAreAppropriate( true );
2799  try
2800  {
2801  if ( mExtent2D.isNull() )
2802  wgs84Extent = transformer.transformBoundingBox( mExtent3D.toRectangle() );
2803  else
2804  wgs84Extent = transformer.transformBoundingBox( mExtent2D );
2805  }
2806  catch ( const QgsCsException &cse )
2807  {
2808  QgsMessageLog::logMessage( tr( "Error transforming extent: %1" ).arg( cse.what() ) );
2810  }
2811  }
2812  return wgs84Extent;
2813 }
2814 
2815 void QgsMapLayer::updateExtent( const QgsRectangle &extent ) const
2816 {
2818 
2819  if ( extent == mExtent2D )
2820  return;
2821 
2822  mExtent2D = extent;
2823 
2824  // do not update the wgs84 extent if we trust layer metadata
2825  if ( mReadFlags & QgsMapLayer::ReadFlag::FlagTrustLayerMetadata )
2826  return;
2827 
2828  mWgs84Extent = wgs84Extent( true );
2829 }
2830 
2831 void QgsMapLayer::updateExtent( const QgsBox3D &extent ) const
2832 {
2834 
2835  if ( extent == mExtent3D )
2836  return;
2837 
2838  if ( extent.isNull() )
2839  {
2840  if ( !extent.toRectangle().isNull() )
2841  {
2842  // bad 3D extent param but valid in 2d --> update 2D extent
2843  updateExtent( extent.toRectangle() );
2844  }
2845  else
2846  {
2847  QgsDebugMsgLevel( QStringLiteral( "Unable to update extent with empty parameter" ), 1 );
2848  }
2849  }
2850  else
2851  {
2852  mExtent3D = extent;
2853 
2854  // do not update the wgs84 extent if we trust layer metadata
2855  if ( mReadFlags & QgsMapLayer::ReadFlag::FlagTrustLayerMetadata )
2856  return;
2857 
2858  mWgs84Extent = wgs84Extent( true );
2859  }
2860 }
2861 
2863 {
2865 
2866  // do not update the wgs84 extent if we trust layer metadata
2867  if ( mReadFlags & QgsMapLayer::ReadFlag::FlagTrustLayerMetadata )
2868  return;
2869 
2870  mWgs84Extent = QgsRectangle();
2871 }
2872 
2874 {
2876 
2877  QString metadata = QStringLiteral( "<h1>" ) + tr( "General" ) + QStringLiteral( "</h1>\n<hr>\n" ) + QStringLiteral( "<table class=\"list-view\">\n" );
2878 
2879  // name
2880  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
2881 
2882  QString path;
2883  bool isLocalPath = false;
2884  if ( dataProvider() )
2885  {
2886  // local path
2887  QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( dataProvider()->name(), publicSource() );
2888  if ( uriComponents.contains( QStringLiteral( "path" ) ) )
2889  {
2890  path = uriComponents[QStringLiteral( "path" )].toString();
2891  QFileInfo fi( path );
2892  if ( fi.exists() )
2893  {
2894  isLocalPath = true;
2895  metadata += 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" );
2896 
2897  QDateTime lastModified = fi.lastModified();
2898  QString lastModifiedFileName;
2899  QSet<QString> sidecarFiles = QgsFileUtils::sidecarFilesForPath( path );
2900  if ( fi.isFile() )
2901  {
2902  qint64 fileSize = fi.size();
2903  if ( !sidecarFiles.isEmpty() )
2904  {
2905  lastModifiedFileName = fi.fileName();
2906  QStringList sidecarFileNames;
2907  for ( const QString &sidecarFile : sidecarFiles )
2908  {
2909  QFileInfo sidecarFi( sidecarFile );
2910  fileSize += sidecarFi.size();
2911  if ( sidecarFi.lastModified() > lastModified )
2912  {
2913  lastModified = sidecarFi.lastModified();
2914  lastModifiedFileName = sidecarFi.fileName();
2915  }
2916  sidecarFileNames << sidecarFi.fileName();
2917  }
2918  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + ( sidecarFiles.size() > 1 ? tr( "Sidecar files" ) : tr( "Sidecar file" ) ) + QStringLiteral( "</td><td>%1" ).arg( sidecarFileNames.join( QLatin1String( ", " ) ) ) + QStringLiteral( "</td></tr>\n" );
2919  }
2920  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + ( !sidecarFiles.isEmpty() ? tr( "Total size" ) : tr( "Size" ) ) + QStringLiteral( "</td><td>%1" ).arg( QgsFileUtils::representFileSize( fileSize ) ) + QStringLiteral( "</td></tr>\n" );
2921  }
2922  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Last modified" ) + QStringLiteral( "</td><td>%1" ).arg( QLocale().toString( fi.lastModified() ) ) + ( !lastModifiedFileName.isEmpty() ? QStringLiteral( " (%1)" ).arg( lastModifiedFileName ) : QString() ) + QStringLiteral( "</td></tr>\n" );
2923  }
2924  }
2925  if ( uriComponents.contains( QStringLiteral( "url" ) ) )
2926  {
2927  const QString url = uriComponents[QStringLiteral( "url" )].toString();
2928  metadata += 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" );
2929  }
2930  }
2931 
2932  // data source
2933  if ( publicSource() != path || !isLocalPath )
2934  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>%1" ).arg( publicSource() != path ? publicSource() : path ) + QStringLiteral( "</td></tr>\n" );
2935 
2936  // provider
2937  if ( dataProvider() )
2938  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Provider" ) + QStringLiteral( "</td><td>%1" ).arg( dataProvider()->name() ) + QStringLiteral( "</td></tr>\n" );
2939 
2940  metadata += QLatin1String( "</table>\n<br><br>" );
2941 
2942  // custom properties
2943  if ( const auto keys = customPropertyKeys(); !keys.isEmpty() )
2944  {
2945  metadata += QStringLiteral( "<h1>" ) + tr( "Custom Properties" ) + QStringLiteral( "</h1>\n<hr>\n" );
2946  metadata += QLatin1String( "<table class=\"list-view\">\n<tbody>" );
2947  for ( const QString &key : keys )
2948  {
2949  // keys prefaced with _ are considered private/internal details
2950  if ( key.startsWith( '_' ) )
2951  continue;
2952 
2953  const QVariant propValue = customProperty( key );
2954  metadata += QStringLiteral( "<tr><td class=\"highlight\">%1</td><td>%2</td></tr>" ).arg( key.toHtmlEscaped(), propValue.toString().toHtmlEscaped() );
2955  }
2956  metadata += QLatin1String( "</tbody></table>\n" );
2957  metadata += QLatin1String( "<br><br>\n" );
2958  }
2959 
2960  return metadata;
2961 }
2962 
2964 {
2966 
2967  QString metadata = QStringLiteral( "<h1>" ) + tr( "Coordinate Reference System (CRS)" ) + QStringLiteral( "</h1>\n<hr>\n" );
2968  metadata += QLatin1String( "<table class=\"list-view\">\n" );
2969 
2970  // Identifier
2972  if ( !c.isValid() )
2973  metadata += QStringLiteral( "<tr><td colspan=\"2\" class=\"highlight\">" ) + tr( "Unknown" ) + QStringLiteral( "</td></tr>\n" );
2974  else
2975  {
2976  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + c.userFriendlyIdentifier( Qgis::CrsIdentifierType::FullString ) + QStringLiteral( "</td></tr>\n" );
2977 
2978  // map units
2979  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Units" ) + QStringLiteral( "</td><td>" )
2980  + ( c.isGeographic() ? tr( "Geographic (uses latitude and longitude for coordinates)" ) : QgsUnitTypes::toString( c.mapUnits() ) )
2981  + QStringLiteral( "</td></tr>\n" );
2982 
2983  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Type" ) + QStringLiteral( "</td><td>" ) + QgsCoordinateReferenceSystemUtils::crsTypeToString( c.type() ) + QStringLiteral( "</td></tr>\n" );
2984 
2985  // operation
2986  const QgsProjOperation operation = c.operation();
2987  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Method" ) + QStringLiteral( "</td><td>" ) + operation.description() + QStringLiteral( "</td></tr>\n" );
2988 
2989  // celestial body
2990  try
2991  {
2992  const QString celestialBody = c.celestialBodyName();
2993  if ( !celestialBody.isEmpty() )
2994  {
2995  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Celestial Body" ) + QStringLiteral( "</td><td>" ) + celestialBody + QStringLiteral( "</td></tr>\n" );
2996  }
2997  }
2998  catch ( QgsNotSupportedException & )
2999  {
3000 
3001  }
3002 
3003  QString accuracyString;
3004  // dynamic crs with no epoch?
3005  if ( c.isDynamic() && std::isnan( c.coordinateEpoch() ) )
3006  {
3007  accuracyString = tr( "Based on a dynamic CRS, but no coordinate epoch is set. Coordinates are ambiguous and of limited accuracy." );
3008  }
3009 
3010  // based on datum ensemble?
3011  try
3012  {
3013  const QgsDatumEnsemble ensemble = c.datumEnsemble();
3014  if ( ensemble.isValid() )
3015  {
3016  QString id;
3017  if ( !ensemble.code().isEmpty() )
3018  id = QStringLiteral( "<i>%1</i> (%2:%3)" ).arg( ensemble.name(), ensemble.authority(), ensemble.code() );
3019  else
3020  id = QStringLiteral( "<i>%</i>”" ).arg( ensemble.name() );
3021 
3022  if ( ensemble.accuracy() > 0 )
3023  {
3024  accuracyString = tr( "Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg( id ).arg( ensemble.accuracy() );
3025  }
3026  else
3027  {
3028  accuracyString = tr( "Based on %1, which has a limited accuracy." ).arg( id );
3029  }
3030  }
3031  }
3032  catch ( QgsNotSupportedException & )
3033  {
3034 
3035  }
3036 
3037  if ( !accuracyString.isEmpty() )
3038  {
3039  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Accuracy" ) + QStringLiteral( "</td><td>" ) + accuracyString + QStringLiteral( "</td></tr>\n" );
3040  }
3041 
3042  // static/dynamic
3043  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Reference" ) + QStringLiteral( "</td><td>%1</td></tr>\n" ).arg( c.isDynamic() ? tr( "Dynamic (relies on a datum which is not plate-fixed)" ) : tr( "Static (relies on a datum which is plate-fixed)" ) );
3044 
3045  // coordinate epoch
3046  if ( !std::isnan( c.coordinateEpoch() ) )
3047  {
3048  metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Coordinate Epoch" ) + QStringLiteral( "</td><td>%1</td></tr>\n" ).arg( c.coordinateEpoch() );
3049  }
3050  }
3051 
3052  metadata += QLatin1String( "</table>\n<br><br>\n" );
3053  return metadata;
3054 }
static QString version()
Version string.
Definition: qgis.cpp:258
@ FullString
Full definition – possibly a very lengthy string, e.g. with no truncation of custom WKT definitions.
static const double SCALE_PRECISION
Fudge factor used to compare two scales.
Definition: qgis.h:4874
@ ForceFirstLetterToCapital
Convert just the first letter of each word to uppercase, leave the rest untouched.
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:255
@ Unknown
Unknown types.
LayerType
Types of layers that can be added to a map.
Definition: qgis.h:114
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
QFlags< MapLayerProperty > MapLayerProperties
Map layer properties.
Definition: qgis.h:1805
AutoRefreshMode
Map layer automatic refresh modes.
Definition: qgis.h:1815
@ RedrawOnly
Redraw current data only.
@ ReloadData
Reload data (and draw the new data)
@ Disabled
Automatic refreshing is disabled.
Base metadata class for 3D renderers.
virtual QgsAbstract3DRenderer * createRenderer(QDomElement &elem, const QgsReadWriteContext &context)=0
Returns new instance of the renderer given the DOM element.
Qgs3DRendererAbstractMetadata * rendererMetadata(const QString &type) const
Returns metadata for a 3D renderer type (may be used to create a new instance of the type)
Base class for all renderers that may to participate in 3D view.
virtual QString type() const =0
Returns unique identifier of the renderer class (used to identify subclass)
virtual void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const =0
Writes renderer's properties to given XML element.
virtual void resolveReferences(const QgsProject &project)
Resolves references to other objects - second phase of loading - after readXml()
static QString pkgDataPath()
Returns the common root path of all application data directories.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static Qgs3DRendererRegistry * renderer3DRegistry()
Returns registry of available 3D renderers.
bool setMasterPassword(bool verify=false)
Main call to initially set or continually check master password is set.
A 3-dimensional box composed of x, y, z coordinates.
Definition: qgsbox3d.h:43
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition: qgsbox3d.h:338
bool isNull() const
Test if the box is null (holding no spatial information).
Definition: qgsbox3d.cpp:289
static QString crsTypeToString(Qgis::CrsType type)
Returns a translated string representing a CRS type.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
void validate()
Perform some validation on this CRS.
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
void setValidationHint(const QString &html)
Set user hint for validation.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:67
virtual bool containsElevationData() const
Returns true if the data provider definitely contains elevation related data.
Abstract base class for spatial data provider implementations.
@ FlagLoadDefaultStyle
Reset the layer's style to the default for the datasource.
@ FlagTrustDataSource
Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load ti...
@ ForceReadOnly
Open layer in a read-only mode (since QGIS 3.28)
@ SkipGetExtent
Skip the extent from provider.
virtual QString name() const =0
Returns a provider name.
QFlags< ReadFlag > ReadFlags
void notify(const QString &msg)
Emitted when the datasource issues a notification.
virtual QgsDataProviderElevationProperties * elevationProperties()
Returns the provider's elevation properties.
static QString removePassword(const QString &aUri, bool hide=false)
Removes the password element from a URI.
Contains information about a datum ensemble.
Definition: qgsdatums.h:95
QString code() const
Identification code, e.g.
Definition: qgsdatums.h:122
QString authority() const
Authority name, e.g.
Definition: qgsdatums.h:117
bool isValid() const
Returns true if the datum ensemble is a valid object, or false if it is a null/invalid object.
Definition: qgsdatums.h:102
QString name() const
Display name of datum ensemble.
Definition: qgsdatums.h:107
double accuracy() const
Positional accuracy (in meters).
Definition: qgsdatums.h:112
QgsError is container for error messages (report).
Definition: qgserror.h:81
QString what() const
Definition: qgsexception.h:49
static QSet< QString > sidecarFilesForPath(const QString &path)
Returns a list of the sidecar files which exist for the dataset a the specified path.
static QString representFileSize(qint64 bytes)
Returns the human size from bytes.
A structured metadata store for a map layer.
bool readMetadataXml(const QDomElement &metadataElement) override
Sets state from DOM document.
bool writeMetadataXml(QDomElement &metadataElement, QDomDocument &document) const override
Stores state in a DOM node.
static void setLayerNotes(QgsMapLayer *layer, const QString &notes)
Sets the notes for the specified layer, where notes is a HTML formatted string.
static bool layerHasNotes(const QgsMapLayer *layer)
Returns true if the specified layer has notes available.
static QString layerNotes(const QgsMapLayer *layer)
Returns the notes for the specified layer.
This class models dependencies with or between map layers.
Base class for storage of map layer elevation properties.
The QgsMapLayerLegend class is abstract interface for implementations of legends for one map layer.
void itemsChanged()
Emitted when existing items/nodes got invalid and should be replaced by new ones.
Manages QGIS Server properties for a map layer.
void readXml(const QDomNode &layer_node)
Reads server properties from project file.
void copyTo(QgsMapLayerServerProperties *properties) const
Copy properties to another instance.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
Management of styles for use with one map layer.
bool addStyle(const QString &name, const QgsMapLayerStyle &style)
Add a style with given name and data.
QStringList styles() const
Returns list of all defined style names.
void writeXml(QDomElement &mgrElement) const
Write configuration (for project saving)
void reset()
Reset the style manager to a basic state - with one default style which is set as current.
bool renameStyle(const QString &name, const QString &newName)
Rename a stored style to a different name.
void readXml(const QDomElement &mgrElement)
Read configuration (for project loading)
Base class for storage of map layer temporal properties.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
QString mKeywordList
Definition: qgsmaplayer.h:2131
void setShortName(const QString &shortName)
Sets the short name of the layer used by QGIS Server to identify the layer.
Definition: qgsmaplayer.h:290
virtual bool deleteStyleFromDatabase(const QString &styleId, QString &msgError)
Deletes a style from the database.
bool importNamedMetadata(QDomDocument &document, QString &errorMessage)
Import the metadata of this layer from a QDomDocument.
QString name
Definition: qgsmaplayer.h:78
void readStyleManager(const QDomNode &layerNode)
Read style manager's configuration (if any). To be called by subclasses.
virtual bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const =0
Write the style for the layer into the document provided.
QString legendUrlFormat() const
Returns the format for a URL based layer legend.
Definition: qgsmaplayer.h:1412
QgsRectangle wgs84Extent(bool forceRecalculate=false) const
Returns the WGS84 extent (EPSG:4326) of the layer according to ReadFlag::FlagTrustLayerMetadata.
void setRefreshOnNotifyEnabled(bool enabled)
Set whether provider notification is connected to triggerRepaint.
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
virtual bool isTemporary() const
Returns true if the layer is considered a temporary layer.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context=QgsReadWriteContext(), QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const
Export the properties of this layer as named style in a QDomDocument.
virtual bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all symbols associated with the layer.
void dependenciesChanged()
Emitted when dependencies are changed.
virtual bool hasMapTips() const
Returns true if the layer contains map tips.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void legendChanged()
Signal emitted when legend of the layer has changed.
void writeStyleManager(QDomNode &layerNode, QDomDocument &doc) const
Write style manager's configuration (if exists). To be called by subclasses.
QgsMapLayerLegend * legend() const
Can be nullptr.
QFlags< ReadFlag > ReadFlags
Definition: qgsmaplayer.h:645
QFlags< LayerFlag > LayerFlags
Definition: qgsmaplayer.h:157
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Import the properties of this layer from a QDomDocument.
void setAbstract(const QString &abstract)
Sets the abstract of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:319
void metadataChanged()
Emitted when the layer's metadata is changed.
virtual QgsRectangle extent() const
Returns the extent of the layer.
virtual QString saveSldStyle(const QString &uri, bool &resultFlag) const
Saves the properties of this layer to an SLD format file.
QString source() const
Returns the source for the layer.
void setLegendUrl(const QString &legendUrl)
Sets the URL for the layer's legend.
Definition: qgsmaplayer.h:1397
virtual bool setDependencies(const QSet< QgsMapLayerDependency > &layers)
Sets the list of dependencies.
void request3DUpdate()
Signal emitted when a layer requires an update in any 3D maps.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsError mError
Error.
Definition: qgsmaplayer.h:2146
int mBlockStyleChangedSignal
If non-zero, the styleChanged signal should not be emitted.
Definition: qgsmaplayer.h:2188
QString providerType() const
Returns the provider type (provider key) for this layer.
virtual void setExtent3D(const QgsBox3D &box)
Sets the extent.
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
Qgis::AutoRefreshMode autoRefreshMode() const
Returns the layer's automatic refresh mode.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
void configChanged()
Emitted whenever the configuration is changed.
void trigger3DUpdate()
Will advise any 3D maps that this layer requires to be updated in the scene.
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
virtual QSet< QgsMapLayerDependency > dependencies() const
Gets the list of dependencies.
void setCustomProperties(const QgsObjectCustomProperties &properties)
Set custom properties for layer.
virtual QString encodedSource(const QString &source, const QgsReadWriteContext &context) const
Called by writeLayerXML(), used by derived classes to encode provider's specific data source to proje...
QgsMapLayer::LayerFlags flags() const
Returns the flags for this layer.
virtual void setSubLayerVisibility(const QString &name, bool visible)
Set the visibility of the given sublayer name.
void isValidChanged()
Emitted when the validity of this layer changed.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
bool loadNamedMetadataFromDatabase(const QString &db, const QString &uri, QString &qmd)
Retrieve a named metadata for this layer from a sqlite database.
virtual bool readXml(const QDomNode &layer_node, QgsReadWriteContext &context)
Called by readLayerXML(), used by children to read state specific to them from project files.
QString attribution() const
Returns the attribution of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:397
void setOriginalXmlProperties(const QString &originalXmlProperties)
Sets the original XML properties for the layer to originalXmlProperties.
void writeCustomProperties(QDomNode &layerNode, QDomDocument &doc) const
Write custom properties to project file.
QString mRefreshOnNofifyMessage
Definition: qgsmaplayer.h:2159
QString mLegendUrl
WMS legend.
Definition: qgsmaplayer.h:2142
virtual int listStylesInDatabase(QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError)
Lists all the style in db split into related to the layer and not related to.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
virtual QString loadDefaultStyle(bool &resultFlag)
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
QString mLayerName
Name of the layer - used for display.
Definition: qgsmaplayer.h:2124
virtual QString loadNamedMetadata(const QString &uri, bool &resultFlag)
Retrieve a named metadata for this layer if one exists (either as a .qmd file on disk or as a record ...
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.
Q_DECL_DEPRECATED bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
void mapTipTemplateChanged()
Emitted when the map tip template changes.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
QString crsHtmlMetadata() const
Returns a HTML fragment containing the layer's CRS metadata, for use in the htmlMetadata() method.
void setAttributionUrl(const QString &attribUrl)
Sets the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:405
Q_DECL_DEPRECATED void setAutoRefreshEnabled(bool enabled)
Sets whether auto refresh is enabled for the layer.
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
QgsLayerMetadata metadata
Definition: qgsmaplayer.h:80
static QString formatLayerName(const QString &name)
A convenience function to capitalize and format a layer name.
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
QgsMapLayer(Qgis::LayerType type=Qgis::LayerType::Vector, const QString &name=QString(), const QString &source=QString())
Constructor for QgsMapLayer.
Definition: qgsmaplayer.cpp:83
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
Qgis::LayerType type
Definition: qgsmaplayer.h:82
QString dataUrlFormat() const
Returns the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:378
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
Definition: qgsmaplayer.h:422
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
QString mTitle
Definition: qgsmaplayer.h:2127
void setDataUrl(const QString &dataUrl)
Sets the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:352
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
void setKeywordList(const QString &keywords)
Sets the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:334
void setAttribution(const QString &attrib)
Sets the attribution of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:388
void setFlags(QgsMapLayer::LayerFlags flags)
Returns the flags for this layer.
bool isRefreshOnNotifyEnabled() const
Returns true if the refresh on provider nofification is enabled.
Definition: qgsmaplayer.h:1576
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
QSet< QgsMapLayerDependency > mDependencies
List of layers that may modify this layer on modification.
Definition: qgsmaplayer.h:2149
void readCustomProperties(const QDomNode &layerNode, const QString &keyStartsWith=QString())
Read custom properties from project file.
virtual Qgis::MapLayerProperties properties() const
Returns the map layer properties of this layer.
virtual QString loadSldStyle(const QString &uri, bool &resultFlag)
Attempts to style the layer using the formatting from an SLD type file.
virtual void setMetadata(const QgsLayerMetadata &metadata)
Sets the layer's metadata store.
virtual bool readStyle(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read the style for the current layer from the DOM node supplied.
virtual QString saveDefaultMetadata(bool &resultFlag)
Save the current metadata of this layer as the default metadata (either as a .qmd file on disk or as ...
virtual bool supportsEditing() const
Returns whether the layer supports editing or not.
void setDataUrlFormat(const QString &dataUrlFormat)
Sets the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:369
QString mLegendUrlFormat
Definition: qgsmaplayer.h:2143
QFlags< StyleCategory > StyleCategories
Definition: qgsmaplayer.h:188
virtual void saveStyleToDatabase(const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Saves named and sld style of the layer to the style table in the db.
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
QString mProviderKey
Data provider key (name of the data provider)
Definition: qgsmaplayer.h:2162
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
void styleChanged()
Signal emitted whenever a change affects the layer's style.
virtual bool isEditable() const
Returns true if the layer can be edited.
QUndoStack * undoStack()
Returns pointer to layer's undo stack.
std::unique_ptr< QgsDataProvider > mPreloadedProvider
Optionally used when loading a project, it is released when the layer is effectively created.
Definition: qgsmaplayer.h:2225
QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:312
void crsChanged()
Emit a signal that layer's CRS has been reset.
virtual QgsError error() const
Gets current status error.
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
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...
QString mAttributionUrl
Definition: qgsmaplayer.h:2139
void setScaleBasedVisibility(bool enabled)
Sets whether scale based visibility is enabled for the layer.
void dataSourceChanged()
Emitted whenever the layer's data source has been changed.
QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:361
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
Q_DECL_DEPRECATED QString metadataUrlFormat() const
Returns the metadata format of the layer used by QGIS Server in GetCapabilities request.
void setRefreshOnNofifyMessage(const QString &message)
Set the notification message that triggers repaint If refresh on notification is enabled,...
Definition: qgsmaplayer.h:1775
static QString generateId(const QString &layerName)
Generates an unique identifier for this layer, the generate ID is prefixed by layerName.
void opacityChanged(double opacity)
Emitted when the layer's opacity is changed, where opacity is a value between 0 (transparent) and 1 (...
virtual bool isModified() const
Returns true if the layer has been modified since last commit/save.
virtual QString loadNamedStyle(const QString &theURI, bool &resultFlag, bool loadFromLocalDb, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Loads a named style from file/local db/datasource db.
void styleLoaded(QgsMapLayer::StyleCategories categories)
Emitted when a style has been loaded.
virtual QString getStyleFromDatabase(const QString &styleId, QString &msgError)
Returns the named style corresponding to style id provided.
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
QString mShortName
Definition: qgsmaplayer.h:2126
QUndoStack * undoStackStyles()
Returns pointer to layer's style undo stack.
void dataChanged()
Data of layer changed.
virtual QStringList subLayers() const
Returns the sublayers of this layer.
virtual QString htmlMetadata() const
Obtain a formatted HTML string containing assorted metadata for this layer.
Q_DECL_DEPRECATED void setMetadataUrlFormat(const QString &metaUrlFormat)
Sets the metadata format of the layer used by QGIS Server in GetCapabilities request.
virtual bool loadNamedStyleFromDatabase(const QString &db, const QString &uri, QString &qml)
Retrieve a named style for this layer from a sqlite database.
virtual QgsBox3D extent3D() const
Returns the 3D extent of the layer.
static QString extensionPropertyType(PropertyType type)
Returns the extension of a Property.
Definition: qgsmaplayer.cpp:70
void blendModeChanged(QPainter::CompositionMode blendMode)
Signal emitted when the blend mode is changed, through QgsMapLayer::setBlendMode()
virtual QString saveSldStyleV2(bool &resultFlag, const QgsSldExportContext &exportContext) const
Saves the properties of this layer to an SLD format file.
void setName(const QString &name)
Set the display name of the layer.
void setAutoRefreshInterval(int interval)
Sets the auto refresh interval (in milliseconds) for the layer.
virtual bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)=0
Read the symbology for the current layer from the DOM node supplied.
Q_DECL_DEPRECATED QString metadataUrl() const
Returns the metadata URL of the layer used by QGIS Server in GetCapabilities request.
virtual void setExtent(const QgsRectangle &rect)
Sets the extent.
virtual void resolveReferences(QgsProject *project)
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects.
QString saveNamedMetadata(const QString &uri, bool &resultFlag)
Save the current metadata of this layer as a named metadata (either as a .qmd file on disk or as a re...
bool isValid
Definition: qgsmaplayer.h:83
QString mDataSource
Data source description string, varies by layer type.
Definition: qgsmaplayer.h:2121
void setAutoRefreshMode(Qgis::AutoRefreshMode mode)
Sets the automatic refresh mode for the layer.
QString refreshOnNotifyMessage() const
Returns the message that should be notified by the provider to triggerRepaint.
Definition: qgsmaplayer.h:1570
virtual bool readSld(const QDomNode &node, QString &errorMessage)
Definition: qgsmaplayer.h:1232
void setMapTipsEnabled(bool enabled)
Enable or disable map tips for this layer.
virtual QString loadDefaultMetadata(bool &resultFlag)
Retrieve the default metadata for this layer if one exists (either as a .qmd file on disk or as a rec...
@ FlagReadExtentFromXml
Read extent from xml and skip get extent from provider.
Definition: qgsmaplayer.h:642
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:641
@ FlagForceReadOnly
Force open as read only.
Definition: qgsmaplayer.h:643
void setValid(bool valid)
Sets whether layer is valid or not.
QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:414
void readCommonStyle(const QDomElement &layerElement, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read style data common to all layer types.
QString mAbstract
Description of the layer.
Definition: qgsmaplayer.h:2130
void customPropertyChanged(const QString &key)
Emitted when a custom property of the layer has been changed or removed.
QgsMapLayer::ReadFlags mReadFlags
Read flags. It's up to the subclass to respect these when restoring state from XML.
Definition: qgsmaplayer.h:2167
void setDataSource(const QString &dataSource, const QString &baseName, const QString &provider, bool loadDefaultStyleFlag=false)
Updates the data source of the layer.
double minimumScale() const
Returns the minimum map scale (i.e.
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
QString legendUrl() const
Returns the URL for the layer's legend.
Definition: qgsmaplayer.h:1402
QString mDataUrlFormat
Definition: qgsmaplayer.h:2135
void flagsChanged()
Emitted when layer's flags have been modified.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
void setLegendUrlFormat(const QString &legendUrlFormat)
Sets the format for a URL based layer legend.
Definition: qgsmaplayer.h:1407
void exportNamedMetadata(QDomDocument &doc, QString &errorMsg) const
Export the current metadata of this layer as named metadata in a QDomDocument.
virtual QString saveNamedStyle(const QString &uri, bool &resultFlag, StyleCategories categories=AllStyleCategories)
Save the properties of this layer as a named style (either as a .qml file on disk or as a record in t...
virtual void exportSldStyle(QDomDocument &doc, QString &errorMsg) const
Export the properties of this layer as SLD style in a QDomDocument.
void beforeResolveReferences(QgsProject *project)
Emitted when all layers are loaded and references can be resolved, just before the references of this...
void setMapTipTemplate(const QString &mapTipTemplate)
The mapTip is a pretty, html representation for feature information.
Q_DECL_DEPRECATED void setMetadataUrl(const QString &metaUrl)
Sets the metadata URL of the layer used by QGIS Server in GetCapabilities request.
Q_INVOKABLE QStringList customPropertyKeys() const
Returns list of all keys within custom properties.
QgsProject * project() const
Returns the parent project if this map layer is added to a project.
Q_DECL_DEPRECATED void setMetadataUrlType(const QString &metaUrlType)
Set the metadata type of the layer used by QGIS Server in GetCapabilities request MetadataUrlType ind...
bool mapTipsEnabled
Definition: qgsmaplayer.h:86
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags(), QgsDataProvider *preloadedProvider=nullptr)
Sets state from DOM document.
void setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
Definition: qgsmaplayer.h:1633
double opacity
Definition: qgsmaplayer.h:84
virtual QString decodedSource(const QString &source, const QString &dataProvider, const QgsReadWriteContext &context) const
Called by readLayerXML(), used by derived classes to decode provider's specific data source from proj...
void nameChanged()
Emitted when the name has been changed.
virtual QString metadataUri() const
Retrieve the metadata URI for this layer (either as a .qmd file on disk or as a record in the users s...
int autoRefreshInterval
Definition: qgsmaplayer.h:79
virtual bool writeStyle(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write just the symbology information for the layer into the document.
bool mIsRefreshOnNofifyEnabled
Definition: qgsmaplayer.h:2158
QString mDataUrl
DataUrl of the layer.
Definition: qgsmaplayer.h:2134
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double mLayerOpacity
Layer opacity.
Definition: qgsmaplayer.h:2181
bool mValid
Indicates if the layer is valid and can be drawn.
Definition: qgsmaplayer.h:2118
@ AllStyleCategories
Definition: qgsmaplayer.h:184
@ LayerConfiguration
General configuration: identifiable, removable, searchable, display expression, read-only.
Definition: qgsmaplayer.h:166
@ Symbology
Symbology.
Definition: qgsmaplayer.h:167
@ Notes
Layer user notes (since QGIS 3.20)
Definition: qgsmaplayer.h:183
@ Temporal
Temporal properties (since QGIS 3.14)
Definition: qgsmaplayer.h:180
@ Rendering
Rendering: scale visibility, simplify method, opacity.
Definition: qgsmaplayer.h:176
@ Elevation
Elevation settings (since QGIS 3.18)
Definition: qgsmaplayer.h:182
@ Symbology3D
3D symbology
Definition: qgsmaplayer.h:168
@ CustomProperties
Custom properties (by plugins for instance)
Definition: qgsmaplayer.h:177
virtual Q_INVOKABLE void reload()
Synchronises with changes in the datasource.
Definition: qgsmaplayer.h:532
virtual QDateTime timestamp() const
Time stamp of data source in the moment when data/metadata were loaded by provider.
void setProviderType(const QString &providerType)
Sets the providerType (provider key)
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
Definition: qgsmaplayer.h:1626
void mapTipsEnabledChanged()
Emitted when map tips are enabled or disabled for the layer.
virtual QString saveDefaultStyle(bool &resultFlag, StyleCategories categories)
Save the properties of this layer as the default style (either as a .qml file on disk or as a record ...
void setRenderer3D(QgsAbstract3DRenderer *renderer)
Sets 3D renderer for the layer.
QString mAttribution
Attribution of the layer.
Definition: qgsmaplayer.h:2138
~QgsMapLayer() override
const QgsObjectCustomProperties & customProperties() const
Read all custom properties from layer.
virtual void exportSldStyleV2(QDomDocument &doc, QString &errorMsg, const QgsSldExportContext &exportContext) const
Export the properties of this layer as SLD style in a QDomDocument.
QString generalHtmlMetadata() const
Returns an HTML fragment containing general metadata information, for use in the htmlMetadata() metho...
Q_DECL_DEPRECATED QString metadataUrlType() const
Returns the metadata type of the layer used by QGIS Server in GetCapabilities request.
void writeCommonStyle(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write style data common to all layer types.
double maximumScale() const
Returns the maximum map scale (i.e.
QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:342
virtual void setLayerOrder(const QStringList &layers)
Reorders the previously selected sublayers of this layer from bottom to top.
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
QString mapTipTemplate
Definition: qgsmaplayer.h:85
void setTitle(const QString &title)
Sets the title of the layer used by QGIS Server in GetCapabilities request.
Definition: qgsmaplayer.h:304
PropertyType
Maplayer has a style and a metadata property.
Definition: qgsmaplayer.h:139
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
Definition: qgsmaplayer.h:2174
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
static QgsDataProvider::ReadFlags providerReadFlags(const QDomNode &layerNode, QgsMapLayer::ReadFlags layerReadFlags)
Returns provider read flag deduced from layer read flags layerReadFlags and a dom node layerNode that...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
Definition: qgsexception.h:118
Simple key-value store (keys = strings, values = variants) that supports loading/saving to/from XML i...
void setValue(const QString &key, const QVariant &value)
Add an entry to the store with the specified key.
QStringList keys() const
Returns a list of all stored keys.
void writeXml(QDomNode &parentNode, QDomDocument &doc) const
Writes the store contents to an XML node.
void remove(const QString &key)
Removes a key (entry) from the store.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant()) const
Returns the value for the given key.
void readXml(const QDomNode &parentNode, const QString &keyStartsWith=QString())
Read store contents from an XML node.
bool contains(const QString &key) const
Returns true if the properties contains a key with the specified name.
Contains information about a PROJ operation.
QString description() const
Description.
Class to convert from older project file versions to newer.
bool updateRevision(const QgsProjectVersion &version)
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
The derived translate() translates with QTranslator and qm file the sourceText.
A class to describe the version of a project.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
bool removeAttachedFile(const QString &path)
Removes the attached file.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
QString baseName() const
Returns the base name of the project file without the path and without extension - derived from fileN...
Definition: qgsproject.cpp:896
QString absoluteFilePath() const
Returns full absolute path to the project file if the project is stored in a file system - derived fr...
Definition: qgsproject.cpp:882
Holds data provider key, description, and associated shared library file or function pointer informat...
@ SaveLayerMetadata
Indicates that the provider supports saving native layer metadata (since QGIS 3.20)
QString absoluteToRelativeUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts absolute path(s) to relative path(s) in the given provider-specific URI.
QString getStyleById(const QString &providerKey, const QString &uri, const QString &styleId, QString &errCause)
Gets a layer style defined by styleId.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
bool saveLayerMetadata(const QString &providerKey, const QString &uri, const QgsLayerMetadata &metadata, QString &errorMessage)
Saves metadata to the layer corresponding to the specified uri.
bool deleteStyleById(const QString &providerKey, const QString &uri, const QString &styleId, QString &errCause)
Deletes a layer style defined by styleId.
QString loadStoredStyle(const QString &providerKey, const QString &uri, QString &styleName, QString &errCause)
Loads a layer style from the provider storage, reporting its name.
QString relativeToAbsoluteUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts relative path(s) to absolute path(s) in the given provider-specific URI.
int listStyles(const QString &providerKey, const QString &uri, QStringList &ids, QStringList &names, QStringList &descriptions, QString &errCause)
Lists stored layer styles in the provider defined by providerKey and uri.
bool saveStyle(const QString &providerKey, const QString &uri, const QString &qmlStyle, const QString &sldStyle, const QString &styleName, const QString &styleDescription, const QString &uiFileContent, bool useAsDefault, QString &errCause)
Saves a layer style to provider.
Represents a raster layer.
bool writeSld(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props=QVariantMap()) const
Writes the symbology of the layer into the document provided in SLD 1.0.0 format.
Allows entering a context category and takes care of leaving this category on deletion of the class.
The class is used as a container of context for various read/write operations on other objects.
MAYBE_UNUSED NODISCARD QgsReadWriteContextCategoryPopper enterCategory(const QString &category, const QString &details=QString()) const
Push a category to the stack.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
bool isNull() const
Test if the rectangle is null (holding no spatial information).
Definition: qgsrectangle.h:505
void setMetadataUrls(const QList< QgsServerMetadataUrlProperties::MetadataUrl > &metaUrls)
Sets a the list of metadata URL for the layer.
QList< QgsServerMetadataUrlProperties::MetadataUrl > metadataUrls() const
Returns a list of metadataUrl resources associated for the layer.
The QgsSldExportContext class holds SLD export options and other information related to SLD export of...
QString exportFilePath() const
Returns the export file path for the SLD.
void setExportFilePath(const QString &exportFilePath)
Sets the export file path for the SLD to exportFilePath.
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
An interface for classes which can visit style entity (e.g.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
Represents a vector layer which manages a vector based data sets.
bool writeSld(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props=QVariantMap()) const
Writes the symbology of the layer into the document provided in SLD 1.1 format.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
static T readFlagAttribute(const QDomElement &element, const QString &attributeName, T defaultValue)
Read a flag value from an attribute of the element.
Definition: qgsxmlutils.h:118
static QDomElement writeBox3D(const QgsBox3D &box, QDomDocument &doc, const QString &elementName=QStringLiteral("extent3D"))
Encodes a 3D box to a DOM element.
static QgsBox3D readBox3D(const QDomElement &element)
Decodes a DOM element to a 3D box.
Definition: qgsxmlutils.cpp:39
static QDomElement writeRectangle(const QgsRectangle &rect, QDomDocument &doc, const QString &elementName=QStringLiteral("extent"))
Encodes a rectangle to a DOM element.
static QgsRectangle readRectangle(const QDomElement &element)
Definition: qgsxmlutils.cpp:64
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
int open(const QString &path)
Opens the database at the specified file path.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition: qgis.h:5382
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:5363
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5172
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:5653
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.
QString format
Format specification of online resource.