QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
qgsproject.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsproject.cpp - description
3  -------------------
4  begin : July 23, 2004
5  copyright : (C) 2004 by Mark Coletti
6  email : mcoletti at gmail.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 #include "qgsproject.h"
19 
20 #include "qgsdatasourceuri.h"
22 #include "qgslayertree.h"
23 #include "qgslayertreeutils.h"
25 #include "qgslogger.h"
26 #include "qgsmessagelog.h"
27 #include "qgsmaplayerfactory.h"
28 #include "qgspluginlayer.h"
29 #include "qgspluginlayerregistry.h"
31 #include "qgssnappingconfig.h"
32 #include "qgspathresolver.h"
33 #include "qgsprojectstorage.h"
35 #include "qgsprojectversion.h"
36 #include "qgsrasterlayer.h"
37 #include "qgsreadwritecontext.h"
38 #include "qgsrectangle.h"
39 #include "qgsrelationmanager.h"
40 #include "qgsannotationmanager.h"
41 #include "qgsvectorlayerjoininfo.h"
42 #include "qgsmapthemecollection.h"
43 #include "qgslayerdefinition.h"
44 #include "qgsunittypes.h"
45 #include "qgstransaction.h"
46 #include "qgstransactiongroup.h"
47 #include "qgsvectordataprovider.h"
49 #include "qgsmaplayerlistutils.h"
50 #include "qgsmeshlayer.h"
51 #include "qgslayoutmanager.h"
52 #include "qgsbookmarkmanager.h"
53 #include "qgsmaplayerstore.h"
54 #include "qgsziputils.h"
55 #include "qgsauxiliarystorage.h"
56 #include "qgssymbollayerutils.h"
57 #include "qgsapplication.h"
59 #include "qgsstyleentityvisitor.h"
60 #include "qgsprojectviewsettings.h"
62 #include "qgsprojecttimesettings.h"
63 #include "qgsvectortilelayer.h"
64 #include "qgsruntimeprofiler.h"
65 #include "qgsannotationlayer.h"
66 #include "qgspointcloudlayer.h"
67 #include "qgsattributeeditorcontainer.h"
68 
69 
70 #include <algorithm>
71 #include <QApplication>
72 #include <QFileInfo>
73 #include <QDomNode>
74 #include <QObject>
75 #include <QTextStream>
76 #include <QTemporaryFile>
77 #include <QDir>
78 #include <QUrl>
79 #include <QStandardPaths>
80 #include <QUuid>
81 #include <QRegularExpression>
82 
83 #ifdef _MSC_VER
84 #include <sys/utime.h>
85 #else
86 #include <utime.h>
87 #endif
88 
89 // canonical project instance
90 QgsProject *QgsProject::sProject = nullptr;
91 
93 class ScopedIntIncrementor
94 {
95  public:
96 
97  ScopedIntIncrementor( int *variable )
98  : mVariable( variable )
99  {
100  ( *mVariable )++;
101  }
102 
103  ScopedIntIncrementor( const ScopedIntIncrementor &other ) = delete;
104  ScopedIntIncrementor &operator=( const ScopedIntIncrementor &other ) = delete;
105 
106  void release()
107  {
108  if ( mVariable )
109  ( *mVariable )--;
110 
111  mVariable = nullptr;
112  }
113 
114  ~ScopedIntIncrementor()
115  {
116  release();
117  }
118 
119  private:
120  int *mVariable = nullptr;
121 };
123 
124 
133 QStringList makeKeyTokens_( const QString &scope, const QString &key )
134 {
135  QStringList keyTokens = QStringList( scope );
136 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
137  keyTokens += key.split( '/', QString::SkipEmptyParts );
138 #else
139  keyTokens += key.split( '/', Qt::SkipEmptyParts );
140 #endif
141 
142  // be sure to include the canonical root node
143  keyTokens.push_front( QStringLiteral( "properties" ) );
144 
145  //check validy of keys since an invalid xml name will will be dropped upon saving the xml file. If not valid, we print a message to the console.
146  for ( int i = 0; i < keyTokens.size(); ++i )
147  {
148  QString keyToken = keyTokens.at( i );
149 
150  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
151  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
152  const thread_local QRegularExpression sInvalidRegexp = QRegularExpression( "([^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\-\\.0-9\\x{B7}\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]|^[^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}])" );
153  if ( keyToken.contains( sInvalidRegexp ) )
154  {
155  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
156  QgsMessageLog::logMessage( errorString, QString(), Qgis::Critical );
157  }
158  }
159 
160  return keyTokens;
161 }
162 
163 
164 
174 QgsProjectProperty *findKey_( const QString &scope,
175  const QString &key,
176  QgsProjectPropertyKey &rootProperty )
177 {
178  QgsProjectPropertyKey *currentProperty = &rootProperty;
179  QgsProjectProperty *nextProperty; // link to next property down hierarchy
180 
181  QStringList keySequence = makeKeyTokens_( scope, key );
182 
183  while ( !keySequence.isEmpty() )
184  {
185  // if the current head of the sequence list matches the property name,
186  // then traverse down the property hierarchy
187  if ( keySequence.first() == currentProperty->name() )
188  {
189  // remove front key since we're traversing down a level
190  keySequence.pop_front();
191 
192  if ( 1 == keySequence.count() )
193  {
194  // if we have only one key name left, then return the key found
195  return currentProperty->find( keySequence.front() );
196  }
197  else if ( keySequence.isEmpty() )
198  {
199  // if we're out of keys then the current property is the one we
200  // want; i.e., we're in the rate case of being at the top-most
201  // property node
202  return currentProperty;
203  }
204  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
205  {
206  if ( nextProperty->isKey() )
207  {
208  currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
209  }
210  else if ( nextProperty->isValue() && 1 == keySequence.count() )
211  {
212  // it may be that this may be one of several property value
213  // nodes keyed by QDict string; if this is the last remaining
214  // key token and the next property is a value node, then
215  // that's the situation, so return the currentProperty
216  return currentProperty;
217  }
218  else
219  {
220  // QgsProjectPropertyValue not Key, so return null
221  return nullptr;
222  }
223  }
224  else
225  {
226  // if the next key down isn't found
227  // then the overall key sequence doesn't exist
228  return nullptr;
229  }
230  }
231  else
232  {
233  return nullptr;
234  }
235  }
236 
237  return nullptr;
238 }
239 
240 
241 
251 QgsProjectProperty *addKey_( const QString &scope,
252  const QString &key,
253  QgsProjectPropertyKey *rootProperty,
254  const QVariant &value,
255  bool &propertiesModified )
256 {
257  QStringList keySequence = makeKeyTokens_( scope, key );
258 
259  // cursor through property key/value hierarchy
260  QgsProjectPropertyKey *currentProperty = rootProperty;
261  QgsProjectProperty *nextProperty; // link to next property down hierarchy
262  QgsProjectPropertyKey *newPropertyKey = nullptr;
263 
264  propertiesModified = false;
265  while ( ! keySequence.isEmpty() )
266  {
267  // if the current head of the sequence list matches the property name,
268  // then traverse down the property hierarchy
269  if ( keySequence.first() == currentProperty->name() )
270  {
271  // remove front key since we're traversing down a level
272  keySequence.pop_front();
273 
274  // if key sequence has one last element, then we use that as the
275  // name to store the value
276  if ( 1 == keySequence.count() )
277  {
278  QgsProjectProperty *property = currentProperty->find( keySequence.front() );
279  if ( !property || property->value() != value )
280  {
281  currentProperty->setValue( keySequence.front(), value );
282  propertiesModified = true;
283  }
284 
285  return currentProperty;
286  }
287  // we're at the top element if popping the keySequence element
288  // will leave it empty; in that case, just add the key
289  else if ( keySequence.isEmpty() )
290  {
291  if ( currentProperty->value() != value )
292  {
293  currentProperty->setValue( value );
294  propertiesModified = true;
295  }
296 
297  return currentProperty;
298  }
299  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
300  {
301  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
302 
303  if ( currentProperty )
304  {
305  continue;
306  }
307  else // QgsProjectPropertyValue not Key, so return null
308  {
309  return nullptr;
310  }
311  }
312  else // the next subkey doesn't exist, so add it
313  {
314  if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
315  {
316  currentProperty = newPropertyKey;
317  }
318  continue;
319  }
320  }
321  else
322  {
323  return nullptr;
324  }
325  }
326 
327  return nullptr;
328 }
329 
338 void removeKey_( const QString &scope,
339  const QString &key,
340  QgsProjectPropertyKey &rootProperty )
341 {
342  QgsProjectPropertyKey *currentProperty = &rootProperty;
343 
344  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
345  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
346 
347  QStringList keySequence = makeKeyTokens_( scope, key );
348 
349  while ( ! keySequence.isEmpty() )
350  {
351  // if the current head of the sequence list matches the property name,
352  // then traverse down the property hierarchy
353  if ( keySequence.first() == currentProperty->name() )
354  {
355  // remove front key since we're traversing down a level
356  keySequence.pop_front();
357 
358  // if we have only one key name left, then try to remove the key
359  // with that name
360  if ( 1 == keySequence.count() )
361  {
362  currentProperty->removeKey( keySequence.front() );
363  }
364  // if we're out of keys then the current property is the one we
365  // want to remove, but we can't delete it directly; we need to
366  // delete it from the parent property key container
367  else if ( keySequence.isEmpty() )
368  {
369  previousQgsPropertyKey->removeKey( currentProperty->name() );
370  }
371  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
372  {
373  previousQgsPropertyKey = currentProperty;
374  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
375 
376  if ( currentProperty )
377  {
378  continue;
379  }
380  else // QgsProjectPropertyValue not Key, so return null
381  {
382  return;
383  }
384  }
385  else // if the next key down isn't found
386  {
387  // then the overall key sequence doesn't exist
388  return;
389  }
390  }
391  else
392  {
393  return;
394  }
395  }
396 }
397 
398 QgsProject::QgsProject( QObject *parent )
399  : QObject( parent )
400  , mLayerStore( new QgsMapLayerStore( this ) )
401  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
402  , mSnappingConfig( this )
403  , mRelationManager( new QgsRelationManager( this ) )
404  , mAnnotationManager( new QgsAnnotationManager( this ) )
405  , mLayoutManager( new QgsLayoutManager( this ) )
406  , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
407  , mViewSettings( new QgsProjectViewSettings( this ) )
408  , mTimeSettings( new QgsProjectTimeSettings( this ) )
409  , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
410  , mRootGroup( new QgsLayerTree )
411  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
412  , mArchive( new QgsProjectArchive() )
413  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
414 {
415  mProperties.setName( QStringLiteral( "properties" ) );
416 
417  mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
418  mMainAnnotationLayer->setParent( this );
419 
420  clear();
421 
422  // bind the layer tree to the map layer registry.
423  // whenever layers are added to or removed from the registry,
424  // layer tree will be updated
425  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
426  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
427  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
428  connect( this, qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
429 
430  // proxy map layer store signals to this
431  connect( mLayerStore.get(), qgis::overload<const QStringList &>::of( &QgsMapLayerStore::layersWillBeRemoved ),
432  this, [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
433  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ),
434  this, [ = ]( const QList<QgsMapLayer *> &layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
435  connect( mLayerStore.get(), qgis::overload< const QString & >::of( &QgsMapLayerStore::layerWillBeRemoved ),
436  this, [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
437  connect( mLayerStore.get(), qgis::overload< QgsMapLayer * >::of( &QgsMapLayerStore::layerWillBeRemoved ),
438  this, [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
439  connect( mLayerStore.get(), qgis::overload<const QStringList & >::of( &QgsMapLayerStore::layersRemoved ), this,
440  [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
441  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this,
442  [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
443  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this,
444  [ = ]() { mProjectScope.reset(); emit removeAll(); } );
445  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this,
446  [ = ]( const QList< QgsMapLayer * > &layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
447  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this,
448  [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
449 
450  if ( QgsApplication::instance() )
451  {
453  }
454 
455  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ), this,
456  [ = ]( const QList<QgsMapLayer *> &layers )
457  {
458  for ( const auto &layer : layers )
459  {
460  disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
461  }
462  }
463  );
464  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersAdded ), this,
465  [ = ]( const QList<QgsMapLayer *> &layers )
466  {
467  for ( const auto &layer : layers )
468  {
469  connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
470  }
471  }
472  );
473 
477 }
478 
479 
481 {
482  mIsBeingDeleted = true;
483 
484  clear();
485  delete mBadLayerHandler;
486  delete mRelationManager;
487  delete mLayerTreeRegistryBridge;
488  delete mRootGroup;
489  if ( this == sProject )
490  {
491  sProject = nullptr;
492  }
493 }
494 
496 {
497  sProject = project;
498 }
499 
500 
502 {
503  if ( !sProject )
504  {
505  sProject = new QgsProject;
506  }
507  return sProject;
508 }
509 
510 void QgsProject::setTitle( const QString &title )
511 {
512  if ( title == mMetadata.title() )
513  return;
514 
515  mMetadata.setTitle( title );
516  mProjectScope.reset();
517  emit metadataChanged();
518 
519  setDirty( true );
520 }
521 
522 QString QgsProject::title() const
523 {
524  return mMetadata.title();
525 }
526 
527 QString QgsProject::saveUser() const
528 {
529  return mSaveUser;
530 }
531 
533 {
534  return mSaveUserFull;
535 }
536 
538 {
539  return mSaveDateTime;
540 }
541 
543 {
544  return mSaveVersion;
545 }
546 
548 {
549  return mDirty;
550 }
551 
552 void QgsProject::setDirty( const bool dirty )
553 {
554  if ( dirty && mDirtyBlockCount > 0 )
555  return;
556 
557  if ( mDirty == dirty )
558  return;
559 
560  mDirty = dirty;
561  emit isDirtyChanged( mDirty );
562 }
563 
564 void QgsProject::setPresetHomePath( const QString &path )
565 {
566  if ( path == mHomePath )
567  return;
568 
569  mHomePath = path;
570  mCachedHomePath.clear();
571  mProjectScope.reset();
572 
573  emit homePathChanged();
574 
575  setDirty( true );
576 }
577 
578 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
579 {
580  const QList<QgsAttributeEditorElement *> elements = parent->children();
581 
582  for ( QgsAttributeEditorElement *element : elements )
583  {
584  if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
585  {
586  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
587 
588  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
589 
590  if ( !container->children().empty() )
591  registerTranslatableContainers( translationContext, container, layerId );
592  }
593  }
594 }
595 
597 {
598  //register layers
599  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
600 
601  for ( const QgsLayerTreeLayer *layer : layers )
602  {
603  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
604 
605  QgsMapLayer *mapLayer = layer->layer();
607  {
608  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
609 
610  //register aliases and fields
611  const QgsFields fields = vlayer->fields();
612  for ( const QgsField &field : fields )
613  {
614  QString fieldName;
615  if ( field.alias().isEmpty() )
616  fieldName = field.name();
617  else
618  fieldName = field.alias();
619 
620  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
621 
622  if ( field.editorWidgetSetup().type() == QLatin1String( "ValueRelation" ) )
623  {
624  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
625  }
626  }
627 
628  //register formcontainers
629  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
630 
631  }
632  }
633 
634  //register layergroups
635  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
636  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
637  {
638  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
639  }
640 
641  //register relations
642  const QList<QgsRelation> &relations = mRelationManager->relations().values();
643  for ( const QgsRelation &relation : relations )
644  {
645  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
646  }
647 }
648 
650 {
651  mDataDefinedServerProperties = properties;
652 }
653 
655 {
656  return mDataDefinedServerProperties;
657 }
658 
659 void QgsProject::setFileName( const QString &name )
660 {
661  if ( name == mFile.fileName() )
662  return;
663 
664  QString oldHomePath = homePath();
665 
666  mFile.setFileName( name );
667  mCachedHomePath.clear();
668  mProjectScope.reset();
669 
670  emit fileNameChanged();
671 
672  QString newHomePath = homePath();
673  if ( newHomePath != oldHomePath )
674  emit homePathChanged();
675 
676  setDirty( true );
677 }
678 
679 QString QgsProject::fileName() const
680 {
681  return mFile.fileName();
682 }
683 
684 void QgsProject::setOriginalPath( const QString &path )
685 {
686  mOriginalPath = path;
687 }
688 
690 {
691  return mOriginalPath;
692 }
693 
694 QFileInfo QgsProject::fileInfo() const
695 {
696  return QFileInfo( mFile );
697 }
698 
700 {
702 }
703 
704 QDateTime QgsProject::lastModified() const
705 {
706  if ( QgsProjectStorage *storage = projectStorage() )
707  {
709  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
710  return metadata.lastModified;
711  }
712  else
713  {
714  return QFileInfo( mFile.fileName() ).lastModified();
715  }
716 }
717 
719 {
720  if ( projectStorage() )
721  return QString();
722 
723  if ( mFile.fileName().isEmpty() )
724  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
725 
726  return QFileInfo( mFile.fileName() ).absolutePath();
727 }
728 
730 {
731  if ( projectStorage() )
732  return QString();
733 
734  if ( mFile.fileName().isEmpty() )
735  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
736 
737  return QFileInfo( mFile.fileName() ).absoluteFilePath();
738 }
739 
740 QString QgsProject::baseName() const
741 {
742  if ( QgsProjectStorage *storage = projectStorage() )
743  {
745  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
746  return metadata.name;
747  }
748  else
749  {
750  return QFileInfo( mFile.fileName() ).completeBaseName();
751  }
752 }
753 
755 {
756  return mCrs;
757 }
758 
759 void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
760 {
761  if ( crs != mCrs )
762  {
763  mCrs = crs;
764  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
765  mProjectScope.reset();
766  setDirty( true );
767  emit crsChanged();
768  }
769 
770  if ( adjustEllipsoid )
772 }
773 
774 QString QgsProject::ellipsoid() const
775 {
776  if ( !crs().isValid() )
777  return geoNone();
778 
779  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), geoNone() );
780 }
781 
782 void QgsProject::setEllipsoid( const QString &ellipsoid )
783 {
784  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
785  return;
786 
787  mProjectScope.reset();
788  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
789  emit ellipsoidChanged( ellipsoid );
790 }
791 
793 {
794  return mTransformContext;
795 }
796 
798 {
799  if ( context == mTransformContext )
800  return;
801 
802  mTransformContext = context;
803  mProjectScope.reset();
804 
805  mMainAnnotationLayer->setTransformContext( context );
806  for ( auto &layer : mLayerStore.get()->mapLayers() )
807  {
808  layer->setTransformContext( context );
809  }
811 }
812 
814 {
815  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
816 
817  mProjectScope.reset();
818  mFile.setFileName( QString() );
819  mProperties.clearKeys();
820  mSaveUser.clear();
821  mSaveUserFull.clear();
822  mSaveDateTime = QDateTime();
823  mSaveVersion = QgsProjectVersion();
824  mHomePath.clear();
825  mCachedHomePath.clear();
826  mAutoTransaction = false;
827  mEvaluateDefaultValues = false;
828  mDirty = false;
829  mTrustLayerMetadata = false;
830  mCustomVariables.clear();
832  mMetadata = QgsProjectMetadata();
833  if ( !mSettings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
834  {
835  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
837  }
838  emit metadataChanged();
839 
841  context.readSettings();
842  setTransformContext( context );
843 
844  mEmbeddedLayers.clear();
845  mRelationManager->clear();
846  mAnnotationManager->clear();
847  mLayoutManager->clear();
848  mBookmarkManager->clear();
849  mViewSettings->reset();
850  mTimeSettings->reset();
851  mDisplaySettings->reset();
852  mSnappingConfig.reset();
853  mAvoidIntersectionsMode = AvoidIntersectionsMode::AllowIntersections;
856 
857  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
859 
860  mLabelingEngineSettings->clear();
861 
862  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
863  mArchive->clear();
864 
866 
867  if ( !mIsBeingDeleted )
868  {
869  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
870  emit projectColorsChanged();
871  }
872 
873  // reset some default project properties
874  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
875  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
876  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
877  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
878 
879  //copy default units to project
880  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
881  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
882 
883  int red = mSettings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
884  int green = mSettings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
885  int blue = mSettings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
886  setBackgroundColor( QColor( red, green, blue ) );
887 
888  red = mSettings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
889  green = mSettings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
890  blue = mSettings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
891  int alpha = mSettings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
892  setSelectionColor( QColor( red, green, blue, alpha ) );
893 
894  mSnappingConfig.clearIndividualLayerSettings();
895 
897  mRootGroup->clear();
898  if ( mMainAnnotationLayer )
899  mMainAnnotationLayer->reset();
900 
901  snapSingleBlocker.release();
902 
903  if ( !mBlockSnappingUpdates )
904  emit snappingConfigChanged( mSnappingConfig );
905 
906  setDirty( false );
907  emit homePathChanged();
908  emit cleared();
909 }
910 
911 // basically a debugging tool to dump property list values
912 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
913 {
914  QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
915  topQgsPropertyKey.dump();
916 }
917 
948 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
949 {
950  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
951 
952  if ( propertiesElem.isNull() ) // no properties found, so we're done
953  {
954  return;
955  }
956 
957  QDomNodeList scopes = propertiesElem.childNodes();
958 
959  if ( propertiesElem.firstChild().isNull() )
960  {
961  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
962  return;
963  }
964 
965  if ( ! project_properties.readXml( propertiesElem ) )
966  {
967  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
968  }
969 }
970 
977 QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
978 {
979  QgsPropertyCollection ddServerProperties;
980  // Read data defined server properties
981  QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral( "dataDefinedServerProperties" ) );
982  if ( !ddElem.isNull() )
983  {
984  if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
985  {
986  QgsDebugMsg( QStringLiteral( "dataDefinedServerProperties.readXml() failed" ) );
987  }
988  }
989  return ddServerProperties;
990 }
991 
996 static void _getTitle( const QDomDocument &doc, QString &title )
997 {
998  QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral( "title" ) );
999 
1000  title.clear(); // by default the title will be empty
1001 
1002  if ( titleNode.isNull() )
1003  {
1004  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1005  return;
1006  }
1007 
1008  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1009  {
1010  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1011  return;
1012  }
1013 
1014  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1015 
1016  if ( !titleTextNode.isText() )
1017  {
1018  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1019  return;
1020  }
1021 
1022  QDomText titleText = titleTextNode.toText();
1023 
1024  title = titleText.data();
1025 
1026 }
1027 
1028 static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1029 {
1030  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1031 
1032  if ( !nl.count() )
1033  {
1034  QgsDebugMsg( "unable to find qgis element" );
1035  return;
1036  }
1037 
1038  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1039 
1040  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1041  lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
1042  lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
1043  lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral( "saveDateTime" ), QString() ), Qt::ISODate );
1044 }
1045 
1046 
1047 QgsProjectVersion getVersion( const QDomDocument &doc )
1048 {
1049  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1050 
1051  if ( !nl.count() )
1052  {
1053  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
1054  return QgsProjectVersion( 0, 0, 0, QString() );
1055  }
1056 
1057  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1058 
1059  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1060  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
1061  return projectVersion;
1062 }
1063 
1064 
1066 {
1067  return mSnappingConfig;
1068 }
1069 
1071 {
1072  if ( mSnappingConfig == snappingConfig )
1073  return;
1074 
1075  mSnappingConfig = snappingConfig;
1076  setDirty( true );
1077  emit snappingConfigChanged( mSnappingConfig );
1078 }
1079 
1081 {
1082  if ( mAvoidIntersectionsMode == mode )
1083  return;
1084 
1085  mAvoidIntersectionsMode = mode;
1087 }
1088 
1089 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, QgsProject::ReadFlags flags )
1090 {
1091  // Layer order is set by the restoring the legend settings from project file.
1092  // This is done on the 'readProject( ... )' signal
1093 
1094  QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
1095 
1096  // process the map layer nodes
1097 
1098  if ( layerElement.isNull() ) // if we have no layers to process, bail
1099  {
1100  return true; // Decided to return "true" since it's
1101  // possible for there to be a project with no
1102  // layers; but also, more imporantly, this
1103  // would cause the tests/qgsproject to fail
1104  // since the test suite doesn't currently
1105  // support test layers
1106  }
1107 
1108  bool returnStatus = true;
1109  int numLayers = 0;
1110 
1111  while ( ! layerElement.isNull() )
1112  {
1113  numLayers++;
1114  layerElement = layerElement.nextSiblingElement( QStringLiteral( "maplayer" ) );
1115  }
1116 
1117  // order layers based on their dependencies
1118  QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), QStringLiteral( "projectload" ) );
1119  QgsLayerDefinition::DependencySorter depSorter( doc );
1120  if ( depSorter.hasCycle() )
1121  return false;
1122 
1123  // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1124  if ( depSorter.hasMissingDependency() )
1125  returnStatus = false;
1126 
1127  emit layerLoaded( 0, numLayers );
1128 
1129  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1130  const int totalLayerCount = sortedLayerNodes.count();
1131 
1132  int i = 0;
1133  for ( const QDomNode &node : sortedLayerNodes )
1134  {
1135  const QDomElement element = node.toElement();
1136 
1137  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1138  if ( !name.isNull() )
1139  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1140 
1141  profile.switchTask( name );
1142 
1143  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1144  {
1145  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1146  }
1147  else
1148  {
1149  QgsReadWriteContext context;
1150  context.setPathResolver( pathResolver() );
1151  context.setProjectTranslator( this );
1153 
1154  if ( !addLayer( element, brokenNodes, context, flags ) )
1155  {
1156  returnStatus = false;
1157  }
1158  const auto messages = context.takeMessages();
1159  if ( !messages.isEmpty() )
1160  {
1161  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1162  }
1163  }
1164  emit layerLoaded( i + 1, totalLayerCount );
1165  i++;
1166  }
1167 
1168  return returnStatus;
1169 }
1170 
1171 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, QgsProject::ReadFlags flags )
1172 {
1173  QString type = layerElem.attribute( QStringLiteral( "type" ) );
1174  QgsDebugMsgLevel( "Layer type is " + type, 4 );
1175  std::unique_ptr<QgsMapLayer> mapLayer;
1176 
1177  QgsScopedRuntimeProfile profile( tr( "Create layer" ), QStringLiteral( "projectload" ) );
1178 
1179  bool ok = false;
1180  const QgsMapLayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1181  if ( !ok )
1182  {
1183  QgsDebugMsg( QStringLiteral( "Unknown layer type \"%1\"" ).arg( type ) );
1184  return false;
1185  }
1186 
1187  switch ( layerType )
1188  {
1190  {
1191  mapLayer = qgis::make_unique<QgsVectorLayer>();
1192  // apply specific settings to vector layer
1193  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1194  {
1195  vl->setReadExtentFromXml( mTrustLayerMetadata || ( flags & QgsProject::ReadFlag::FlagTrustLayerMetadata ) );
1196  }
1197  break;
1198  }
1199 
1201  mapLayer = qgis::make_unique<QgsRasterLayer>();
1202  break;
1203 
1205  mapLayer = qgis::make_unique<QgsMeshLayer>();
1206  break;
1207 
1209  mapLayer = qgis::make_unique<QgsVectorTileLayer>();
1210  break;
1211 
1213  mapLayer = qgis::make_unique<QgsPointCloudLayer>();
1214  break;
1215 
1217  {
1218  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1219  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1220  break;
1221  }
1222 
1224  {
1225  QgsAnnotationLayer::LayerOptions options( mTransformContext );
1226  mapLayer = qgis::make_unique<QgsAnnotationLayer>( QString(), options );
1227  break;
1228  }
1229  }
1230 
1231  if ( !mapLayer )
1232  {
1233  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1234  return false;
1235  }
1236 
1237  Q_CHECK_PTR( mapLayer ); // NOLINT
1238 
1239  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1240  // because if it was, the newly created layer will not be added to the store and it would leak.
1241  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1242  Q_ASSERT( ! layerId.isEmpty() );
1243  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1244 
1245  // have the layer restore state that is stored in Dom node
1246  QgsMapLayer::ReadFlags layerFlags = QgsMapLayer::ReadFlags();
1248  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1249  // Propagate trust layer metadata flag
1250  if ( mTrustLayerMetadata || ( flags & QgsProject::ReadFlag::FlagTrustLayerMetadata ) )
1252 
1253  profile.switchTask( tr( "Load layer source" ) );
1254  bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1255 
1256  profile.switchTask( tr( "Add layer to project" ) );
1257  QList<QgsMapLayer *> newLayers;
1258  newLayers << mapLayer.get();
1259  if ( layerIsValid || flags & QgsProject::ReadFlag::FlagDontResolveLayers )
1260  {
1261  emit readMapLayer( mapLayer.get(), layerElem );
1262  addMapLayers( newLayers );
1263  }
1264  else
1265  {
1266  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1267  addMapLayers( newLayers, false );
1268  newLayers.first();
1269  QgsDebugMsg( "Unable to load " + type + " layer" );
1270  brokenNodes.push_back( layerElem );
1271  }
1272 
1273  // It should be safe to delete the layer now if layer was stored, because all the store
1274  // had to to was to reset the data source in case the validity changed.
1275  if ( ! layerWasStored )
1276  {
1277  mapLayer.release();
1278  }
1279 
1280  return layerIsValid;
1281 }
1282 
1283 bool QgsProject::read( const QString &filename, QgsProject::ReadFlags flags )
1284 {
1285  mFile.setFileName( filename );
1286  mCachedHomePath.clear();
1287  mProjectScope.reset();
1288 
1289  return read( flags );
1290 }
1291 
1292 bool QgsProject::read( QgsProject::ReadFlags flags )
1293 {
1294  QString filename = mFile.fileName();
1295  bool returnValue;
1296 
1297  if ( QgsProjectStorage *storage = projectStorage() )
1298  {
1299  QTemporaryFile inDevice;
1300  if ( !inDevice.open() )
1301  {
1302  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1303  return false;
1304  }
1305 
1306  QgsReadWriteContext context;
1307  context.setProjectTranslator( this );
1308  if ( !storage->readProject( filename, &inDevice, context ) )
1309  {
1310  QString err = tr( "Unable to open %1" ).arg( filename );
1311  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1312  if ( !messages.isEmpty() )
1313  err += QStringLiteral( "\n\n" ) + messages.last().message();
1314  setError( err );
1315  return false;
1316  }
1317  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1318  }
1319  else
1320  {
1321  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1322  {
1323  returnValue = unzip( mFile.fileName(), flags );
1324  }
1325  else
1326  {
1327  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1328  returnValue = readProjectFile( mFile.fileName(), flags );
1329  }
1330 
1331  //on translation we should not change the filename back
1332  if ( !mTranslator )
1333  {
1334  mFile.setFileName( filename );
1335  mCachedHomePath.clear();
1336  mProjectScope.reset();
1337  }
1338  else
1339  {
1340  //but delete the translator
1341  mTranslator.reset( nullptr );
1342  }
1343  }
1344  emit homePathChanged();
1345  return returnValue;
1346 }
1347 
1348 bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags flags )
1349 {
1350  // avoid multiple emission of snapping updated signals
1351  ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
1352 
1353  QFile projectFile( filename );
1354  clearError();
1355 
1356  QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
1357  QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), QStringLiteral( "projectload" ) );
1358 
1359  QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), mSettings.value( QStringLiteral( "locale/userLocale" ), QString() ).toString() );
1360 
1361  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1362  {
1363  mTranslator.reset( new QTranslator() );
1364  mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1365  }
1366 
1367  profile.switchTask( tr( "Reading project file" ) );
1368  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1369 
1370  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1371  {
1372  projectFile.close();
1373 
1374  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1375 
1376  return false;
1377  }
1378 
1379  // location of problem associated with errorMsg
1380  int line, column;
1381  QString errorMsg;
1382 
1383  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1384  {
1385  // want to make this class as GUI independent as possible; so commented out
1386 #if 0
1387  QMessageBox::critical( 0, tr( "Read Project File" ),
1388  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
1389 #endif
1390 
1391  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1392  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1393 
1394  QgsDebugMsg( errorString );
1395 
1396  projectFile.close();
1397 
1398  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1399 
1400  return false;
1401  }
1402 
1403  projectFile.close();
1404 
1405  QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1406 
1407  // get project version string, if any
1408  QgsProjectVersion fileVersion = getVersion( *doc );
1409  const QgsProjectVersion thisVersion( Qgis::version() );
1410 
1411  profile.switchTask( tr( "Updating project file" ) );
1412  if ( thisVersion > fileVersion )
1413  {
1414  QgsLogger::warning( "Loading a file that was saved with an older "
1415  "version of qgis (saved in " + fileVersion.text() +
1416  ", loaded in " + Qgis::version() +
1417  "). Problems may occur." );
1418 
1419  QgsProjectFileTransform projectFile( *doc, fileVersion );
1420 
1421  // Shows a warning when an old project file is read.
1422  emit oldProjectVersionWarning( fileVersion.text() );
1423 
1424  projectFile.updateRevision( thisVersion );
1425  }
1426 
1427  // start new project, just keep the file name and auxiliary storage
1428  profile.switchTask( tr( "Creating auxiliary storage" ) );
1429  QString fileName = mFile.fileName();
1430  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1431  clear();
1432  mAuxiliaryStorage = std::move( aStorage );
1433  mFile.setFileName( fileName );
1434  mCachedHomePath.clear();
1435  mProjectScope.reset();
1436  mSaveVersion = fileVersion;
1437 
1438  // now get any properties
1439  profile.switchTask( tr( "Reading properties" ) );
1440  _getProperties( *doc, mProperties );
1441 
1442  // now get the data defined server properties
1443  mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
1444 
1445  QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1446 
1447 #if 0
1448  dump_( mProperties );
1449 #endif
1450 
1451  // get older style project title
1452  QString oldTitle;
1453  _getTitle( *doc, oldTitle );
1454 
1455  readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
1456 
1457  QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1458  if ( homePathNl.count() > 0 )
1459  {
1460  QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1461  QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1462  if ( !homePath.isEmpty() )
1464  }
1465  else
1466  {
1467  emit homePathChanged();
1468  }
1469 
1470  const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1471  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1472  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1474  const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1475  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1476  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1477  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1479 
1480  QgsReadWriteContext context;
1481  context.setPathResolver( pathResolver() );
1482  context.setProjectTranslator( this );
1483 
1484  //crs
1485  QgsCoordinateReferenceSystem projectCrs;
1486  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1487  {
1488  // first preference - dedicated projectCrs node
1489  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1490  if ( !srsNode.isNull() )
1491  {
1492  projectCrs.readXml( srsNode );
1493  }
1494 
1495  if ( !projectCrs.isValid() )
1496  {
1497  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1498  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1499  const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1500 
1501  // authid should be prioritized over all
1502  bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
1503  if ( !authid.isEmpty() && !isUserAuthId )
1504  projectCrs = QgsCoordinateReferenceSystem( authid );
1505 
1506  // try the CRS
1507  if ( !projectCrs.isValid() && currentCRS >= 0 )
1508  {
1509  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1510  }
1511 
1512  // if that didn't produce a match, try the proj.4 string
1513  if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
1514  {
1515  projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
1516  }
1517 
1518  // last just take the given id
1519  if ( !projectCrs.isValid() )
1520  {
1521  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1522  }
1523  }
1524  }
1525  mCrs = projectCrs;
1526 
1527  QStringList datumErrors;
1528  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1529  {
1530  emit missingDatumTransforms( datumErrors );
1531  }
1532  emit transformContextChanged();
1533 
1534  //add variables defined in project file - do this early in the reading cycle, as other components
1535  //(e.g. layouts) may depend on these variables
1536  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1537  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1538 
1539  mCustomVariables.clear();
1540  if ( variableNames.length() == variableValues.length() )
1541  {
1542  for ( int i = 0; i < variableNames.length(); ++i )
1543  {
1544  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1545  }
1546  }
1547  else
1548  {
1549  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1550  }
1551 
1552  QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectMetadata" ) );
1553 
1554  if ( !element.isNull() )
1555  {
1556  mMetadata.readMetadataXml( element );
1557  }
1558  else
1559  {
1560  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1561  mMetadata = QgsProjectMetadata();
1562  }
1563  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1564  {
1565  // upgrade older title storage to storing within project metadata.
1566  mMetadata.setTitle( oldTitle );
1567  }
1568  emit metadataChanged();
1569 
1570  element = doc->documentElement().firstChildElement( QStringLiteral( "autotransaction" ) );
1571  if ( ! element.isNull() )
1572  {
1573  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1574  mAutoTransaction = true;
1575  }
1576 
1577  element = doc->documentElement().firstChildElement( QStringLiteral( "evaluateDefaultValues" ) );
1578  if ( !element.isNull() )
1579  {
1580  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1581  mEvaluateDefaultValues = true;
1582  }
1583 
1584  // Read trust layer metadata config in the project
1585  element = doc->documentElement().firstChildElement( QStringLiteral( "trust" ) );
1586  if ( !element.isNull() )
1587  {
1588  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1589  mTrustLayerMetadata = true;
1590  }
1591 
1592  // read the layer tree from project file
1593  profile.switchTask( tr( "Loading layer tree" ) );
1594  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1595 
1596  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1597  if ( !layerTreeElem.isNull() )
1598  {
1599  // Use a temporary tree to read the nodes to prevent signals being delivered to the models
1600  QgsLayerTree tempTree;
1601  tempTree.readChildrenFromXml( layerTreeElem, context );
1602  mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
1603  }
1604  else
1605  {
1606  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1607  }
1608 
1609  mLayerTreeRegistryBridge->setEnabled( false );
1610 
1611  // get the map layers
1612  profile.switchTask( tr( "Reading map layers" ) );
1613 
1614  QList<QDomNode> brokenNodes;
1615  bool clean = _getMapLayers( *doc, brokenNodes, flags );
1616 
1617  // review the integrity of the retrieved map layers
1618  if ( !clean )
1619  {
1620  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1621 
1622  if ( !brokenNodes.isEmpty() )
1623  {
1624  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1625  }
1626 
1627  // we let a custom handler decide what to do with missing layers
1628  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1629  mBadLayerHandler->handleBadLayers( brokenNodes );
1630  }
1631 
1632  mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
1633  mMainAnnotationLayer->setTransformContext( mTransformContext );
1634 
1635  // Resolve references to other layers
1636  // Needs to be done here once all dependent layers are loaded
1637  profile.switchTask( tr( "Resolving layer references" ) );
1638  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1639  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
1640  {
1641  it.value()->resolveReferences( this );
1642  }
1643 
1644  mLayerTreeRegistryBridge->setEnabled( true );
1645 
1646  // load embedded groups and layers
1647  profile.switchTask( tr( "Loading embedded layers" ) );
1648  loadEmbeddedNodes( mRootGroup, flags );
1649 
1650  // now that layers are loaded, we can resolve layer tree's references to the layers
1651  profile.switchTask( tr( "Resolving references" ) );
1652  mRootGroup->resolveReferences( this );
1653 
1654  if ( !layerTreeElem.isNull() )
1655  {
1656  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1657  }
1658 
1659  // Load pre 3.0 configuration
1660  QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1661  if ( !layerTreeCanvasElem.isNull( ) )
1662  {
1663  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1664  }
1665 
1666  // Convert pre 3.4 to create layers flags
1667  if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
1668  {
1669  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1670  for ( const QString &layerId : requiredLayerIds )
1671  {
1672  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1673  {
1674  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1675  }
1676  }
1677  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1678  for ( const QString &layerId : disabledLayerIds )
1679  {
1680  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1681  {
1682  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1683  }
1684  }
1685  }
1686 
1687  // After bad layer handling we might still have invalid layers,
1688  // store them in case the user wanted to handle them later
1689  // or wanted to pass them through when saving
1691  {
1692  profile.switchTask( tr( "Storing original layer properties" ) );
1693  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1694  }
1695 
1696  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1697 
1698  profile.switchTask( tr( "Loading map themes" ) );
1699  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1701  mMapThemeCollection->readXml( *doc );
1702 
1703  profile.switchTask( tr( "Loading label settings" ) );
1704  mLabelingEngineSettings->readSettingsFromProject( this );
1706 
1707  profile.switchTask( tr( "Loading annotations" ) );
1708  mAnnotationManager->readXml( doc->documentElement(), context );
1709  if ( !( flags & QgsProject::ReadFlag::FlagDontLoadLayouts ) )
1710  {
1711  profile.switchTask( tr( "Loading layouts" ) );
1712  mLayoutManager->readXml( doc->documentElement(), *doc );
1713  }
1714  profile.switchTask( tr( "Loading bookmarks" ) );
1715  mBookmarkManager->readXml( doc->documentElement(), *doc );
1716 
1717  // reassign change dependencies now that all layers are loaded
1718  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1719  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
1720  {
1721  it.value()->setDependencies( it.value()->dependencies() );
1722  }
1723 
1724  profile.switchTask( tr( "Loading snapping settings" ) );
1725  mSnappingConfig.readProject( *doc );
1726  mAvoidIntersectionsMode = static_cast<AvoidIntersectionsMode>( readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
1727 
1728  profile.switchTask( tr( "Loading view settings" ) );
1729  // restore older project scales settings
1730  mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
1731  const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
1732  QVector<double> res;
1733  for ( const QString &scale : scales )
1734  {
1735  const QStringList parts = scale.split( ':' );
1736  if ( parts.size() != 2 )
1737  continue;
1738 
1739  bool ok = false;
1740  const double denominator = QLocale().toDouble( parts[1], &ok );
1741  if ( ok )
1742  {
1743  res << denominator;
1744  }
1745  }
1746  mViewSettings->setMapScales( res );
1747  QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
1748  if ( !viewSettingsElement.isNull() )
1749  mViewSettings->readXml( viewSettingsElement, context );
1750 
1751  // restore time settings
1752  profile.switchTask( tr( "Loading temporal settings" ) );
1753  QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectTimeSettings" ) );
1754  if ( !timeSettingsElement.isNull() )
1755  mTimeSettings->readXml( timeSettingsElement, context );
1756 
1757  profile.switchTask( tr( "Loading display settings" ) );
1758  QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
1759  if ( !displaySettingsElement.isNull() )
1760  mDisplaySettings->readXml( displaySettingsElement, context );
1761 
1762  profile.switchTask( tr( "Updating variables" ) );
1763  emit customVariablesChanged();
1764  profile.switchTask( tr( "Updating CRS" ) );
1765  emit crsChanged();
1766  emit ellipsoidChanged( ellipsoid() );
1767 
1768  // read the project: used by map canvas and legend
1769  profile.switchTask( tr( "Reading external settings" ) );
1770  emit readProject( *doc );
1771  emit readProjectWithContext( *doc, context );
1772 
1773  profile.switchTask( tr( "Updating interface" ) );
1774 
1775  snapSignalBlock.release();
1776  if ( !mBlockSnappingUpdates )
1777  emit snappingConfigChanged( mSnappingConfig );
1778 
1781  emit projectColorsChanged();
1782 
1783  // if all went well, we're allegedly in pristine state
1784  if ( clean )
1785  setDirty( false );
1786 
1787  QgsDebugMsgLevel( QString( "Project save user: %1" ).arg( mSaveUser ), 2 );
1788  QgsDebugMsgLevel( QString( "Project save user: %1" ).arg( mSaveUserFull ), 2 );
1789 
1793 
1794  if ( mTranslator )
1795  {
1796  //project possibly translated -> rename it with locale postfix
1797  QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
1798  setFileName( newFileName );
1799 
1800  if ( write() )
1801  {
1802  setTitle( localeFileName );
1803  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Success );
1804  }
1805  else
1806  {
1807  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Critical );
1808  }
1809  }
1810  return true;
1811 }
1812 
1813 
1814 bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
1815 {
1816  bool valid = true;
1817  const auto constChildren = group->children();
1818  for ( QgsLayerTreeNode *child : constChildren )
1819  {
1820  if ( QgsLayerTree::isGroup( child ) )
1821  {
1822  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1823  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1824  {
1825  // make sure to convert the path from relative to absolute
1826  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1827  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1828  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
1829  if ( newGroup )
1830  {
1831  QList<QgsLayerTreeNode *> clonedChildren;
1832  const auto constChildren = newGroup->children();
1833  for ( QgsLayerTreeNode *newGroupChild : constChildren )
1834  clonedChildren << newGroupChild->clone();
1835  delete newGroup;
1836 
1837  childGroup->insertChildNodes( 0, clonedChildren );
1838  }
1839  }
1840  else
1841  {
1842  loadEmbeddedNodes( childGroup, flags );
1843  }
1844  }
1845  else if ( QgsLayerTree::isLayer( child ) )
1846  {
1847  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1848  {
1849  QList<QDomNode> brokenNodes;
1850  if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
1851  {
1852  valid = valid && false;
1853  }
1854  }
1855  }
1856 
1857  }
1858 
1859  return valid;
1860 }
1861 
1862 QVariantMap QgsProject::customVariables() const
1863 {
1864  return mCustomVariables;
1865 }
1866 
1867 void QgsProject::setCustomVariables( const QVariantMap &variables )
1868 {
1869  if ( variables == mCustomVariables )
1870  return;
1871 
1872  //write variable to project
1873  QStringList variableNames;
1874  QStringList variableValues;
1875 
1876  QVariantMap::const_iterator it = variables.constBegin();
1877  for ( ; it != variables.constEnd(); ++it )
1878  {
1879  variableNames << it.key();
1880  variableValues << it.value().toString();
1881  }
1882 
1883  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1884  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1885 
1886  mCustomVariables = variables;
1887  mProjectScope.reset();
1888 
1889  emit customVariablesChanged();
1890 }
1891 
1893 {
1894  *mLabelingEngineSettings = settings;
1896 }
1897 
1899 {
1900  return *mLabelingEngineSettings;
1901 }
1902 
1904 {
1905  mProjectScope.reset();
1906  return mLayerStore.get();
1907 }
1908 
1910 {
1911  return mLayerStore.get();
1912 }
1913 
1914 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1915 {
1916  QList<QgsVectorLayer *> layers;
1917  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1918  const auto constLayerIds = layerIds;
1919  for ( const QString &layerId : constLayerIds )
1920  {
1921  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1922  layers << vlayer;
1923  }
1924  return layers;
1925 }
1926 
1927 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1928 {
1929  QStringList list;
1930  const auto constLayers = layers;
1931  for ( QgsVectorLayer *layer : constLayers )
1932  list << layer->id();
1933  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1935 }
1936 
1938 {
1939  QgsExpressionContext context;
1940 
1943 
1944  return context;
1945 }
1946 
1948 {
1949  // MUCH cheaper to clone than build
1950  if ( mProjectScope )
1951  {
1952  std::unique_ptr< QgsExpressionContextScope > projectScope = qgis::make_unique< QgsExpressionContextScope >( *mProjectScope );
1953  // we can't cache these
1954  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
1955  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
1956  return projectScope.release();
1957  }
1958 
1959  mProjectScope = qgis::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
1960 
1961  const QVariantMap vars = customVariables();
1962 
1963  QVariantMap::const_iterator it = vars.constBegin();
1964 
1965  for ( ; it != vars.constEnd(); ++it )
1966  {
1967  mProjectScope->setVariable( it.key(), it.value(), true );
1968  }
1969 
1970  QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
1971  if ( projectPath.isEmpty() )
1972  projectPath = mOriginalPath;
1973  QString projectFolder = QFileInfo( projectPath ).path();
1974  QString projectFilename = QFileInfo( projectPath ).fileName();
1975  QString projectBasename = baseName();
1976 
1977  //add other known project variables
1978  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
1979  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
1980  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
1981  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
1982  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
1983  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
1984  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_last_saved" ), mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
1985  QgsCoordinateReferenceSystem projectCrs = crs();
1986  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
1987  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
1988  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_description" ), projectCrs.description(), true, true ) );
1989  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
1990  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
1991  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_units" ), QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
1992  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_acronym" ), projectCrs.projectionAcronym(), true ) );
1993  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_ellipsoid" ), projectCrs.ellipsoidAcronym(), true ) );
1994  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_proj4" ), projectCrs.toProj(), true ) );
1995  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_wkt" ), projectCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), true ) );
1996 
1997  // metadata
1998  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
1999  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
2000  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
2001  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
2002 
2003  // keywords
2004  QVariantMap keywords;
2005  QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2006  for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2007  {
2008  keywords.insert( it.key(), it.value() );
2009  }
2010  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
2011 
2012  // layers
2013  QVariantList layersIds;
2014  QVariantList layers;
2015  const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2016  layersIds.reserve( layersInProject.count() );
2017  layers.reserve( layersInProject.count() );
2018  for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2019  {
2020  layersIds << it.value()->id();
2021  layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( it.value() ) );
2022  }
2023  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_ids" ), layersIds, true ) );
2024  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
2025 
2026  mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
2027 
2029 }
2030 
2031 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
2032 {
2033  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2034 
2035  bool tgChanged = false;
2036 
2037  const auto constLayers = layers;
2038  for ( QgsMapLayer *layer : constLayers )
2039  {
2040  if ( layer->isValid() )
2041  {
2042  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2043  if ( vlayer )
2044  {
2045  if ( autoTransaction() )
2046  {
2047  if ( QgsTransaction::supportsTransaction( vlayer ) )
2048  {
2049  const QString connString = QgsTransaction::connectionString( vlayer->source() );
2050  const QString key = vlayer->providerType();
2051 
2052  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
2053 
2054  if ( !tg )
2055  {
2056  tg = new QgsTransactionGroup();
2057  mTransactionGroups.insert( qMakePair( key, connString ), tg );
2058  tgChanged = true;
2059  }
2060  tg->addLayer( vlayer );
2061  }
2062  }
2064  }
2065 
2066  if ( tgChanged )
2067  emit transactionGroupsChanged();
2068 
2069  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
2070 
2071  // check if we have to update connections for layers with dependencies
2072  for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2073  {
2074  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2075  if ( deps.contains( layer->id() ) )
2076  {
2077  // reconnect to change signals
2078  it.value()->setDependencies( deps );
2079  }
2080  }
2081  }
2082  }
2083 
2084  if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
2085  emit snappingConfigChanged( mSnappingConfig );
2086 }
2087 
2088 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
2089 {
2090  if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
2091  emit snappingConfigChanged( mSnappingConfig );
2092 }
2093 
2094 void QgsProject::cleanTransactionGroups( bool force )
2095 {
2096  bool changed = false;
2097  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2098  {
2099  if ( tg.value()->isEmpty() || force )
2100  {
2101  delete tg.value();
2102  tg = mTransactionGroups.erase( tg );
2103  changed = true;
2104  }
2105  else
2106  {
2107  ++tg;
2108  }
2109  }
2110  if ( changed )
2111  emit transactionGroupsChanged();
2112 }
2113 
2114 bool QgsProject::readLayer( const QDomNode &layerNode )
2115 {
2116  QgsReadWriteContext context;
2117  context.setPathResolver( pathResolver() );
2118  context.setProjectTranslator( this );
2120  QList<QDomNode> brokenNodes;
2121  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
2122  {
2123  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
2124  // added layer for joins
2125  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
2126  const auto constVectorLayers = vectorLayers;
2127  for ( QgsVectorLayer *layer : constVectorLayers )
2128  {
2129  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
2130  layer->resolveReferences( this );
2131  }
2132 
2133  return true;
2134  }
2135  return false;
2136 }
2137 
2138 bool QgsProject::write( const QString &filename )
2139 {
2140  mFile.setFileName( filename );
2141  mCachedHomePath.clear();
2142  return write();
2143 }
2144 
2146 {
2147  mProjectScope.reset();
2148  if ( QgsProjectStorage *storage = projectStorage() )
2149  {
2150  QgsReadWriteContext context;
2151  // for projects stored in a custom storage, we have to check for the support
2152  // of relative paths since the storage most likely will not be in a file system
2153  QString storageFilePath { storage->filePath( mFile.fileName() ) };
2154  if ( storageFilePath.isEmpty() )
2155  {
2156  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
2157  }
2158  context.setPathResolver( pathResolver() );
2159 
2160  QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
2161  QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
2162 
2163  if ( !zip( tmpZipFilename ) )
2164  return false; // zip() already calls setError() when returning false
2165 
2166  QFile tmpZipFile( tmpZipFilename );
2167  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
2168  {
2169  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
2170  return false;
2171  }
2172 
2174  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
2175  {
2176  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
2177  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2178  if ( !messages.isEmpty() )
2179  err += QStringLiteral( "\n\n" ) + messages.last().message();
2180  setError( err );
2181  return false;
2182  }
2183 
2184  tmpZipFile.close();
2185  QFile::remove( tmpZipFilename );
2186 
2187  return true;
2188  }
2189 
2190  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2191  {
2192  return zip( mFile.fileName() );
2193  }
2194  else
2195  {
2196  // write project file even if the auxiliary storage is not correctly
2197  // saved
2198  const bool asOk = saveAuxiliaryStorage();
2199  const bool writeOk = writeProjectFile( mFile.fileName() );
2200 
2201  // errors raised during writing project file are more important
2202  if ( !asOk && writeOk )
2203  {
2204  const QString err = mAuxiliaryStorage->errorString();
2205  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2206  }
2207 
2208  return asOk && writeOk;
2209  }
2210 }
2211 
2212 bool QgsProject::writeProjectFile( const QString &filename )
2213 {
2214  QFile projectFile( filename );
2215  clearError();
2216 
2217  // if we have problems creating or otherwise writing to the project file,
2218  // let's find out up front before we go through all the hand-waving
2219  // necessary to create all the Dom objects
2220  QFileInfo myFileInfo( projectFile );
2221  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
2222  {
2223  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
2224  .arg( projectFile.fileName() ) );
2225  return false;
2226  }
2227 
2228  QgsReadWriteContext context;
2229  context.setPathResolver( pathResolver() );
2231 
2232  QDomImplementation DomImplementation;
2233  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
2234 
2235  QDomDocumentType documentType =
2236  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
2237  QStringLiteral( "SYSTEM" ) );
2238  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
2239 
2240  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
2241  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
2242  qgisNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
2243 
2244  if ( !mSettings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
2245  {
2246  QString newSaveUser = QgsApplication::userLoginName();
2247  QString newSaveUserFull = QgsApplication::userFullName();
2248  qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2249  qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2250  mSaveUser = newSaveUser;
2251  mSaveUserFull = newSaveUserFull;
2252  mSaveDateTime = QDateTime::currentDateTime();
2253  qgisNode.setAttribute( QStringLiteral( "saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
2254  }
2255  else
2256  {
2257  mSaveUser.clear();
2258  mSaveUserFull.clear();
2259  mSaveDateTime = QDateTime();
2260  }
2261  doc->appendChild( qgisNode );
2262  mSaveVersion = QgsProjectVersion( Qgis::version() );
2263 
2264  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2265  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2266  qgisNode.appendChild( homePathNode );
2267 
2268  // title
2269  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2270  qgisNode.appendChild( titleNode );
2271 
2272  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
2273  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? 1 : 0 );
2274  qgisNode.appendChild( transactionNode );
2275 
2276  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
2277  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? 1 : 0 );
2278  qgisNode.appendChild( evaluateDefaultValuesNode );
2279 
2280  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
2281  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? 1 : 0 );
2282  qgisNode.appendChild( trustNode );
2283 
2284  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
2285  titleNode.appendChild( titleText );
2286 
2287  // write project CRS
2288  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
2289  mCrs.writeXml( srsNode, *doc );
2290  qgisNode.appendChild( srsNode );
2291 
2292  // write layer tree - make sure it is without embedded subgroups
2293  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
2295  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
2296 
2297  clonedRoot->writeXml( qgisNode, context );
2298  delete clonedRoot;
2299 
2300  mSnappingConfig.writeProject( *doc );
2301  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( mAvoidIntersectionsMode ) );
2302 
2303  // let map canvas and legend write their information
2304  emit writeProject( *doc );
2305 
2306  // within top level node save list of layers
2307  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2308 
2309  QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
2310  mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
2311  qgisNode.appendChild( annotationLayerNode );
2312 
2313  // Iterate over layers in zOrder
2314  // Call writeXml() on each
2315  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
2316 
2317  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
2318  while ( li != layers.end() )
2319  {
2320  QgsMapLayer *ml = li.value();
2321 
2322  if ( ml )
2323  {
2324  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
2325  if ( emIt == mEmbeddedLayers.constEnd() )
2326  {
2327  QDomElement maplayerElem;
2328  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
2329  // not available, just write what we DO have
2330  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
2331  {
2332  // general layer metadata
2333  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2334  ml->writeLayerXml( maplayerElem, *doc, context );
2335  }
2336  else if ( ! ml->originalXmlProperties().isEmpty() )
2337  {
2338  QDomDocument document;
2339  if ( document.setContent( ml->originalXmlProperties() ) )
2340  {
2341  maplayerElem = document.firstChildElement();
2342  }
2343  else
2344  {
2345  QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
2346  }
2347  }
2348 
2349  emit writeMapLayer( ml, maplayerElem, *doc );
2350 
2351  projectLayersNode.appendChild( maplayerElem );
2352  }
2353  else
2354  {
2355  // layer defined in an external project file
2356  // only save embedded layer if not managed by a legend group
2357  if ( emIt.value().second )
2358  {
2359  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2360  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
2361  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
2362  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
2363  projectLayersNode.appendChild( mapLayerElem );
2364  }
2365  }
2366  }
2367  li++;
2368  }
2369 
2370  qgisNode.appendChild( projectLayersNode );
2371 
2372  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
2373  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
2374  for ( QgsMapLayer *layer : constCustomLayerOrder )
2375  {
2376  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
2377  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
2378  layerOrderNode.appendChild( mapLayerElem );
2379  }
2380  qgisNode.appendChild( layerOrderNode );
2381 
2382  mLabelingEngineSettings->writeSettingsToProject( this );
2383 
2384  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
2385  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
2386  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
2387 
2388  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
2389  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
2390  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
2391  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
2392 
2393  // now add the optional extra properties
2394 #if 0
2395  dump_( mProperties );
2396 #endif
2397 
2398  QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 2 );
2399 
2400  if ( !mProperties.isEmpty() ) // only worry about properties if we
2401  // actually have any properties
2402  {
2403  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2404  }
2405 
2406  QDomElement ddElem = doc->createElement( QStringLiteral( "dataDefinedServerProperties" ) );
2407  mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
2408  qgisNode.appendChild( ddElem );
2409 
2410  mMapThemeCollection->writeXml( *doc );
2411 
2412  mTransformContext.writeXml( qgisNode, context );
2413 
2414  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2415  mMetadata.writeMetadataXml( metadataElem, *doc );
2416  qgisNode.appendChild( metadataElem );
2417 
2418  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2419  qgisNode.appendChild( annotationsElem );
2420 
2421  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2422  qgisNode.appendChild( layoutElem );
2423 
2424  QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2425  qgisNode.appendChild( bookmarkElem );
2426 
2427  QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
2428  qgisNode.appendChild( viewSettingsElem );
2429 
2430  QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
2431  qgisNode.appendChild( timeSettingsElement );
2432 
2433  QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
2434  qgisNode.appendChild( displaySettingsElem );
2435 
2436  // now wrap it up and ship it to the project file
2437  doc->normalize(); // XXX I'm not entirely sure what this does
2438 
2439  // Create backup file
2440  if ( QFile::exists( fileName() ) )
2441  {
2442  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2443  bool ok = true;
2444  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2445  ok &= projectFile.open( QIODevice::ReadOnly );
2446 
2447  QByteArray ba;
2448  while ( ok && !projectFile.atEnd() )
2449  {
2450  ba = projectFile.read( 10240 );
2451  ok &= backupFile.write( ba ) == ba.size();
2452  }
2453 
2454  projectFile.close();
2455  backupFile.close();
2456 
2457  if ( !ok )
2458  {
2459  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2460  return false;
2461  }
2462 
2463  QFileInfo fi( fileName() );
2464  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2465  utime( backupFile.fileName().toUtf8().constData(), &tb );
2466  }
2467 
2468  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2469  {
2470  projectFile.close(); // even though we got an error, let's make
2471  // sure it's closed anyway
2472 
2473  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2474  return false;
2475  }
2476 
2477  QTemporaryFile tempFile;
2478  bool ok = tempFile.open();
2479  if ( ok )
2480  {
2481  QTextStream projectFileStream( &tempFile );
2482  doc->save( projectFileStream, 2 ); // save as utf-8
2483  ok &= projectFileStream.pos() > -1;
2484 
2485  ok &= tempFile.seek( 0 );
2486 
2487  QByteArray ba;
2488  while ( ok && !tempFile.atEnd() )
2489  {
2490  ba = tempFile.read( 10240 );
2491  ok &= projectFile.write( ba ) == ba.size();
2492  }
2493 
2494  ok &= projectFile.error() == QFile::NoError;
2495 
2496  projectFile.close();
2497  }
2498 
2499  tempFile.close();
2500 
2501  if ( !ok )
2502  {
2503  setError( tr( "Unable to save to file %1. Your project "
2504  "may be corrupted on disk. Try clearing some space on the volume and "
2505  "check file permissions before pressing save again." )
2506  .arg( projectFile.fileName() ) );
2507  return false;
2508  }
2509 
2510  setDirty( false ); // reset to pristine state
2511 
2512  emit projectSaved();
2513  return true;
2514 }
2515 
2516 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2517 {
2518  bool propertiesModified;
2519  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2520 
2521  if ( propertiesModified )
2522  setDirty( true );
2523 
2524  return success;
2525 }
2526 
2527 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2528 {
2529  bool propertiesModified;
2530  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2531 
2532  if ( propertiesModified )
2533  setDirty( true );
2534 
2535  return success;
2536 }
2537 
2538 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2539 {
2540  bool propertiesModified;
2541  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2542 
2543  if ( propertiesModified )
2544  setDirty( true );
2545 
2546  return success;
2547 }
2548 
2549 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2550 {
2551  bool propertiesModified;
2552  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2553 
2554  if ( propertiesModified )
2555  setDirty( true );
2556 
2557  return success;
2558 }
2559 
2560 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2561 {
2562  bool propertiesModified;
2563  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2564 
2565  if ( propertiesModified )
2566  setDirty( true );
2567 
2568  return success;
2569 }
2570 
2571 QStringList QgsProject::readListEntry( const QString &scope,
2572  const QString &key,
2573  const QStringList &def,
2574  bool *ok ) const
2575 {
2576  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2577 
2578  QVariant value;
2579 
2580  if ( property )
2581  {
2582  value = property->value();
2583 
2584  bool valid = QVariant::StringList == value.type();
2585  if ( ok )
2586  *ok = valid;
2587 
2588  if ( valid )
2589  {
2590  return value.toStringList();
2591  }
2592  }
2593  else if ( ok )
2594  *ok = false;
2595 
2596 
2597  return def;
2598 }
2599 
2600 
2601 QString QgsProject::readEntry( const QString &scope,
2602  const QString &key,
2603  const QString &def,
2604  bool *ok ) const
2605 {
2606  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2607 
2608  QVariant value;
2609 
2610  if ( property )
2611  {
2612  value = property->value();
2613 
2614  bool valid = value.canConvert( QVariant::String );
2615  if ( ok )
2616  *ok = valid;
2617 
2618  if ( valid )
2619  return value.toString();
2620  }
2621  else if ( ok )
2622  *ok = false;
2623 
2624  return def;
2625 }
2626 
2627 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2628  bool *ok ) const
2629 {
2630  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2631 
2632  QVariant value;
2633 
2634  if ( property )
2635  {
2636  value = property->value();
2637  }
2638 
2639  bool valid = value.canConvert( QVariant::Int );
2640 
2641  if ( ok )
2642  {
2643  *ok = valid;
2644  }
2645 
2646  if ( valid )
2647  {
2648  return value.toInt();
2649  }
2650 
2651  return def;
2652 }
2653 
2654 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2655  double def,
2656  bool *ok ) const
2657 {
2658  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2659  if ( property )
2660  {
2661  QVariant value = property->value();
2662 
2663  bool valid = value.canConvert( QVariant::Double );
2664  if ( ok )
2665  *ok = valid;
2666 
2667  if ( valid )
2668  return value.toDouble();
2669  }
2670  else if ( ok )
2671  *ok = false;
2672 
2673  return def;
2674 }
2675 
2676 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
2677  bool *ok ) const
2678 {
2679  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2680 
2681  if ( property )
2682  {
2683  QVariant value = property->value();
2684 
2685  bool valid = value.canConvert( QVariant::Bool );
2686  if ( ok )
2687  *ok = valid;
2688 
2689  if ( valid )
2690  return value.toBool();
2691  }
2692  else if ( ok )
2693  *ok = false;
2694 
2695  return def;
2696 }
2697 
2698 bool QgsProject::removeEntry( const QString &scope, const QString &key )
2699 {
2700  if ( findKey_( scope, key, mProperties ) )
2701  {
2702  removeKey_( scope, key, mProperties );
2703  setDirty( true );
2704  }
2705 
2706  return !findKey_( scope, key, mProperties );
2707 }
2708 
2709 
2710 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
2711 {
2712  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2713 
2714  QStringList entries;
2715 
2716  if ( foundProperty )
2717  {
2718  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2719 
2720  if ( propertyKey )
2721  { propertyKey->entryList( entries ); }
2722  }
2723 
2724  return entries;
2725 }
2726 
2727 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
2728 {
2729  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2730 
2731  QStringList entries;
2732 
2733  if ( foundProperty )
2734  {
2735  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2736 
2737  if ( propertyKey )
2738  { propertyKey->subkeyList( entries ); }
2739  }
2740 
2741  return entries;
2742 }
2743 
2745 {
2746  dump_( mProperties );
2747 }
2748 
2750 {
2751  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
2752  QString filePath;
2753  if ( ! absolutePaths )
2754  {
2755  // for projects stored in a custom storage, we need to ask to the
2756  // storage for the path, if the storage returns an empty path
2757  // relative paths are not supported
2758  if ( QgsProjectStorage *storage = projectStorage() )
2759  {
2760  filePath = storage->filePath( mFile.fileName() );
2761  }
2762  else
2763  {
2764  filePath = fileName();
2765  }
2766  }
2767  return QgsPathResolver( filePath );
2768 }
2769 
2770 QString QgsProject::readPath( const QString &src ) const
2771 {
2772  return pathResolver().readPath( src );
2773 }
2774 
2775 QString QgsProject::writePath( const QString &src ) const
2776 {
2777  return pathResolver().writePath( src );
2778 }
2779 
2780 void QgsProject::setError( const QString &errorMessage )
2781 {
2782  mErrorMessage = errorMessage;
2783 }
2784 
2785 QString QgsProject::error() const
2786 {
2787  return mErrorMessage;
2788 }
2789 
2790 void QgsProject::clearError()
2791 {
2792  setError( QString() );
2793 }
2794 
2796 {
2797  delete mBadLayerHandler;
2798  mBadLayerHandler = handler;
2799 }
2800 
2801 QString QgsProject::layerIsEmbedded( const QString &id ) const
2802 {
2803  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
2804  if ( it == mEmbeddedLayers.constEnd() )
2805  {
2806  return QString();
2807  }
2808  return it.value().first;
2809 }
2810 
2811 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
2812  bool saveFlag, QgsProject::ReadFlags flags )
2813 {
2814  QgsDebugCall;
2815 
2816  static QString sPrevProjectFilePath;
2817  static QDateTime sPrevProjectFileTimestamp;
2818  static QDomDocument sProjectDocument;
2819 
2820  QString qgsProjectFile = projectFilePath;
2821  QgsProjectArchive archive;
2822  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
2823  {
2824  archive.unzip( projectFilePath );
2825  qgsProjectFile = archive.projectFile();
2826  }
2827 
2828  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
2829 
2830  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
2831  {
2832  sPrevProjectFilePath.clear();
2833 
2834  QFile projectFile( qgsProjectFile );
2835  if ( !projectFile.open( QIODevice::ReadOnly ) )
2836  {
2837  return false;
2838  }
2839 
2840  if ( !sProjectDocument.setContent( &projectFile ) )
2841  {
2842  return false;
2843  }
2844 
2845  sPrevProjectFilePath = projectFilePath;
2846  sPrevProjectFileTimestamp = projectFileTimestamp;
2847  }
2848 
2849  // does project store paths absolute or relative?
2850  bool useAbsolutePaths = true;
2851 
2852  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
2853  if ( !propertiesElem.isNull() )
2854  {
2855  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
2856  if ( !absElem.isNull() )
2857  {
2858  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
2859  }
2860  }
2861 
2862  QgsReadWriteContext embeddedContext;
2863  if ( !useAbsolutePaths )
2864  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
2865  embeddedContext.setProjectTranslator( this );
2866  embeddedContext.setTransformContext( transformContext() );
2867 
2868  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
2869  if ( projectLayersElem.isNull() )
2870  {
2871  return false;
2872  }
2873 
2874  QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral( "maplayer" ) );
2875  while ( ! mapLayerElem.isNull() )
2876  {
2877  // get layer id
2878  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
2879  if ( id == layerId )
2880  {
2881  // layer can be embedded only once
2882  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
2883  {
2884  return false;
2885  }
2886 
2887  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
2888 
2889  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
2890  {
2891  return true;
2892  }
2893  else
2894  {
2895  mEmbeddedLayers.remove( layerId );
2896  return false;
2897  }
2898  }
2899  mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
2900  }
2901 
2902  return false;
2903 }
2904 
2905 
2906 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags )
2907 {
2908  QString qgsProjectFile = projectFilePath;
2909  QgsProjectArchive archive;
2910  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
2911  {
2912  archive.unzip( projectFilePath );
2913  qgsProjectFile = archive.projectFile();
2914  }
2915 
2916  // open project file, get layer ids in group, add the layers
2917  QFile projectFile( qgsProjectFile );
2918  if ( !projectFile.open( QIODevice::ReadOnly ) )
2919  {
2920  return nullptr;
2921  }
2922 
2923  QDomDocument projectDocument;
2924  if ( !projectDocument.setContent( &projectFile ) )
2925  {
2926  return nullptr;
2927  }
2928 
2929  QgsReadWriteContext context;
2930  context.setPathResolver( pathResolver() );
2931  context.setProjectTranslator( this );
2933 
2935 
2936  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2937  if ( !layerTreeElem.isNull() )
2938  {
2939  root->readChildrenFromXml( layerTreeElem, context );
2940  }
2941  else
2942  {
2943  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2944  }
2945 
2946  QgsLayerTreeGroup *group = root->findGroup( groupName );
2947  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
2948  {
2949  // embedded groups cannot be embedded again
2950  delete root;
2951  return nullptr;
2952  }
2953 
2954  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
2955  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
2956  delete root;
2957  root = nullptr;
2958 
2959  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2960  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2961 
2962  // set "embedded" to all children + load embedded layers
2963  mLayerTreeRegistryBridge->setEnabled( false );
2964  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
2965  mLayerTreeRegistryBridge->setEnabled( true );
2966 
2967  // consider the layers might be identify disabled in its project
2968  const auto constFindLayerIds = newGroup->findLayerIds();
2969  for ( const QString &layerId : constFindLayerIds )
2970  {
2971  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2972  if ( layer )
2973  {
2974  layer->resolveReferences( this );
2975  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
2976  }
2977  }
2978 
2979  return newGroup;
2980 }
2981 
2982 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
2983 {
2984  const auto constChildren = group->children();
2985  for ( QgsLayerTreeNode *child : constChildren )
2986  {
2987  // all nodes in the subtree will have "embedded" custom property set
2988  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2989 
2990  if ( QgsLayerTree::isGroup( child ) )
2991  {
2992  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
2993  }
2994  else if ( QgsLayerTree::isLayer( child ) )
2995  {
2996  // load the layer into our project
2997  QList<QDomNode> brokenNodes;
2998  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
2999  }
3000  }
3001 }
3002 
3004 {
3005  return mEvaluateDefaultValues;
3006 }
3007 
3008 void QgsProject::setEvaluateDefaultValues( bool evaluateDefaultValues )
3009 {
3010  if ( evaluateDefaultValues == mEvaluateDefaultValues )
3011  return;
3012 
3013  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3014  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
3015  for ( ; layerIt != layers.constEnd(); ++layerIt )
3016  {
3017  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
3018  if ( vl )
3019  {
3021  }
3022  }
3023 
3024  mEvaluateDefaultValues = evaluateDefaultValues;
3025 }
3026 
3028 {
3029  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
3031 }
3032 
3034 {
3035  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
3036 }
3037 
3039 {
3040  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
3041  if ( !distanceUnitString.isEmpty() )
3042  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
3043 
3044  //fallback to QGIS default measurement unit
3045  bool ok = false;
3046  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
3047  return ok ? type : QgsUnitTypes::DistanceMeters;
3048 }
3049 
3051 {
3052  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
3053 }
3054 
3056 {
3057  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
3058  if ( !areaUnitString.isEmpty() )
3059  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
3060 
3061  //fallback to QGIS default area unit
3062  bool ok = false;
3063  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
3064  return ok ? type : QgsUnitTypes::AreaSquareMeters;
3065 }
3066 
3068 {
3069  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
3070 }
3071 
3072 QString QgsProject::homePath() const
3073 {
3074  if ( !mCachedHomePath.isEmpty() )
3075  return mCachedHomePath;
3076 
3077  QFileInfo pfi( fileName() );
3078 
3079  if ( !mHomePath.isEmpty() )
3080  {
3081  QFileInfo homeInfo( mHomePath );
3082  if ( !homeInfo.isRelative() )
3083  {
3084  mCachedHomePath = mHomePath;
3085  return mHomePath;
3086  }
3087  }
3088  else if ( !fileName().isEmpty() )
3089  {
3090  mCachedHomePath = pfi.path();
3091 
3092  return mCachedHomePath;
3093  }
3094 
3095  if ( !pfi.exists() )
3096  {
3097  mCachedHomePath = mHomePath;
3098  return mHomePath;
3099  }
3100 
3101  if ( !mHomePath.isEmpty() )
3102  {
3103  // path is relative to project file
3104  mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
3105  }
3106  else
3107  {
3108  mCachedHomePath = pfi.canonicalPath();
3109  }
3110  return mCachedHomePath;
3111 }
3112 
3114 {
3115  return mHomePath;
3116 }
3117 
3119 {
3120  return mRelationManager;
3121 }
3122 
3124 {
3125  return mLayoutManager.get();
3126 }
3127 
3129 {
3130  return mLayoutManager.get();
3131 }
3132 
3134 {
3135  return mBookmarkManager;
3136 }
3137 
3139 {
3140  return mBookmarkManager;
3141 }
3142 
3144 {
3145  return mViewSettings;
3146 }
3147 
3149 {
3150  return mViewSettings;
3151 }
3152 
3154 {
3155  return mTimeSettings;
3156 }
3157 
3159 {
3160  return mTimeSettings;
3161 }
3162 
3164 {
3165  return mDisplaySettings;
3166 }
3167 
3169 {
3170  return mDisplaySettings;
3171 }
3172 
3174 {
3175  return mRootGroup;
3176 }
3177 
3179 {
3180  return mMapThemeCollection.get();
3181 }
3182 
3184 {
3185  return mAnnotationManager.get();
3186 }
3187 
3189 {
3190  return mAnnotationManager.get();
3191 }
3192 
3193 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
3194 {
3195  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3196  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3197  {
3198  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3199  continue;
3200 
3201  if ( layers.contains( it.value() ) )
3202  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
3203  else
3204  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
3205  }
3206 
3210 }
3211 
3212 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
3213 {
3214  QList<QgsMapLayer *> nonIdentifiableLayers;
3215  nonIdentifiableLayers.reserve( layerIds.count() );
3216  for ( const QString &layerId : layerIds )
3217  {
3218  QgsMapLayer *layer = mapLayer( layerId );
3219  if ( layer )
3220  nonIdentifiableLayers << layer;
3221  }
3225 }
3226 
3228 {
3229  QStringList nonIdentifiableLayers;
3230 
3231  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3232  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3233  {
3234  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3235  {
3236  nonIdentifiableLayers.append( it.value()->id() );
3237  }
3238  }
3239  return nonIdentifiableLayers;
3240 }
3241 
3243 {
3244  return mAutoTransaction;
3245 }
3246 
3247 void QgsProject::setAutoTransaction( bool autoTransaction )
3248 {
3249  if ( autoTransaction != mAutoTransaction )
3250  {
3251  mAutoTransaction = autoTransaction;
3252 
3253  if ( autoTransaction )
3254  onMapLayersAdded( mapLayers().values() );
3255  else
3256  cleanTransactionGroups( true );
3257  }
3258 }
3259 
3260 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
3261 {
3262  return mTransactionGroups;
3263 }
3264 
3265 
3266 //
3267 // QgsMapLayerStore methods
3268 //
3269 
3270 
3272 {
3273  return mLayerStore->count();
3274 }
3275 
3277 {
3278  return mLayerStore->validCount();
3279 }
3280 
3281 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
3282 {
3283  return mLayerStore->mapLayer( layerId );
3284 }
3285 
3286 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
3287 {
3288  return mLayerStore->mapLayersByName( layerName );
3289 }
3290 
3291 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
3292 {
3293  QList<QgsMapLayer *> layers;
3294  const auto constMapLayers { mLayerStore->mapLayers() };
3295  for ( const auto &l : constMapLayers )
3296  {
3297  if ( ! l->shortName().isEmpty() )
3298  {
3299  if ( l->shortName() == shortName )
3300  layers << l;
3301  }
3302  else if ( l->name() == shortName )
3303  {
3304  layers << l;
3305  }
3306  }
3307  return layers;
3308 }
3309 
3310 bool QgsProject::unzip( const QString &filename, QgsProject::ReadFlags flags )
3311 {
3312  clearError();
3313  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3314 
3315  // unzip the archive
3316  if ( !archive->unzip( filename ) )
3317  {
3318  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
3319  return false;
3320  }
3321 
3322  // test if zip provides a .qgs file
3323  if ( archive->projectFile().isEmpty() )
3324  {
3325  setError( tr( "Zip archive does not provide a project file" ) );
3326  return false;
3327  }
3328 
3329  // load auxiliary storage
3330  if ( !archive->auxiliaryStorageFile().isEmpty() )
3331  {
3332  // database file is already a copy as it's been unzipped. So we don't open
3333  // auxiliary storage in copy mode in this case
3334  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
3335  }
3336  else
3337  {
3338  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
3339  }
3340 
3341  // read the project file
3342  if ( ! readProjectFile( archive->projectFile(), flags ) )
3343  {
3344  setError( tr( "Cannot read unzipped qgs project file" ) );
3345  return false;
3346  }
3347 
3348  // keep the archive and remove the temporary .qgs file
3349  mArchive = std::move( archive );
3350  mArchive->clearProjectFile();
3351 
3352  return true;
3353 }
3354 
3355 bool QgsProject::zip( const QString &filename )
3356 {
3357  clearError();
3358 
3359  // save the current project in a temporary .qgs file
3360  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3361  const QString baseName = QFileInfo( filename ).baseName();
3362  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
3363  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
3364 
3365  bool writeOk = false;
3366  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3367  {
3368  writeOk = writeProjectFile( qgsFile.fileName() );
3369  qgsFile.close();
3370  }
3371 
3372  // stop here with an error message
3373  if ( ! writeOk )
3374  {
3375  setError( tr( "Unable to write temporary qgs file" ) );
3376  return false;
3377  }
3378 
3379  // save auxiliary storage
3380  const QFileInfo info( qgsFile );
3381  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
3382 
3383  if ( ! saveAuxiliaryStorage( asFileName ) )
3384  {
3385  const QString err = mAuxiliaryStorage->errorString();
3386  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
3387  return false;
3388  }
3389 
3390  // create the archive
3391  archive->addFile( qgsFile.fileName() );
3392  archive->addFile( asFileName );
3393 
3394  // zip
3395  if ( !archive->zip( filename ) )
3396  {
3397  setError( tr( "Unable to perform zip" ) );
3398  return false;
3399  }
3400 
3401  return true;
3402 }
3403 
3405 {
3406  return QgsZipUtils::isZipFile( mFile.fileName() );
3407 }
3408 
3409 QList<QgsMapLayer *> QgsProject::addMapLayers(
3410  const QList<QgsMapLayer *> &layers,
3411  bool addToLegend,
3412  bool takeOwnership )
3413 {
3414  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
3415  if ( !myResultList.isEmpty() )
3416  {
3417  // Update transform context
3418  for ( auto &l : myResultList )
3419  {
3420  l->setTransformContext( transformContext() );
3421  }
3422  if ( addToLegend )
3423  {
3424  emit legendLayersAdded( myResultList );
3425  }
3426  }
3427 
3428  if ( mAuxiliaryStorage )
3429  {
3430  for ( QgsMapLayer *mlayer : myResultList )
3431  {
3432  if ( mlayer->type() != QgsMapLayerType::VectorLayer )
3433  continue;
3434 
3435  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
3436  if ( vl )
3437  {
3438  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
3439  }
3440  }
3441  }
3442 
3443  mProjectScope.reset();
3444 
3445  return myResultList;
3446 }
3447 
3448 QgsMapLayer *
3450  bool addToLegend,
3451  bool takeOwnership )
3452 {
3453  QList<QgsMapLayer *> addedLayers;
3454  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
3455  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
3456 }
3457 
3458 void QgsProject::removeMapLayers( const QStringList &layerIds )
3459 {
3460  mProjectScope.reset();
3461  mLayerStore->removeMapLayers( layerIds );
3462 }
3463 
3464 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3465 {
3466  mProjectScope.reset();
3467  mLayerStore->removeMapLayers( layers );
3468 }
3469 
3470 void QgsProject::removeMapLayer( const QString &layerId )
3471 {
3472  mProjectScope.reset();
3473  mLayerStore->removeMapLayer( layerId );
3474 }
3475 
3477 {
3478  mProjectScope.reset();
3479  mLayerStore->removeMapLayer( layer );
3480 }
3481 
3483 {
3484  mProjectScope.reset();
3485  return mLayerStore->takeMapLayer( layer );
3486 }
3487 
3489 {
3490  return mMainAnnotationLayer;
3491 }
3492 
3494 {
3495  if ( mLayerStore->count() == 0 )
3496  return;
3497 
3498  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
3499  mProjectScope.reset();
3500  mLayerStore->removeAllMapLayers();
3501 
3502  snapSingleBlocker.release();
3503  mSnappingConfig.clearIndividualLayerSettings();
3504  if ( !mBlockSnappingUpdates )
3505  emit snappingConfigChanged( mSnappingConfig );
3506 }
3507 
3509 {
3510  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3511  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3512  for ( ; it != layers.constEnd(); ++it )
3513  {
3514  it.value()->reload();
3515  }
3516 }
3517 
3518 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3519 {
3520  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3521 }
3522 
3523 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3524 {
3525  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3526 }
3527 
3529 {
3530  QgsCoordinateReferenceSystem defaultCrs;
3531 
3532  // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
3533  // in the meantime, we have a slightly hacky way to read the settings key using an enum which isn't available (since it lives in app)
3534  if ( mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
3535  || mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == 2 )
3536  {
3537  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
3538  defaultCrs = crs();
3539  }
3540  else
3541  {
3542  // global crs
3543  QString layerDefaultCrs = mSettings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
3544  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
3545  }
3546 
3547  return defaultCrs;
3548 }
3549 
3551 {
3552  mTrustLayerMetadata = trust;
3553 
3554  auto layers = mapLayers();
3555  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3556  {
3557  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3558  if ( vl )
3559  {
3560  vl->setReadExtentFromXml( trust );
3561  }
3562  }
3563 }
3564 
3565 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
3566 {
3567  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3568  bool empty = true;
3569  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3570  {
3571  if ( it.value()->type() != QgsMapLayerType::VectorLayer )
3572  continue;
3573 
3574  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3575  if ( vl && vl->auxiliaryLayer() )
3576  {
3577  vl->auxiliaryLayer()->save();
3578  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
3579  }
3580  }
3581 
3582  if ( !mAuxiliaryStorage->exists( *this ) && filename.isEmpty() && empty )
3583  {
3584  return true; // it's not an error
3585  }
3586  else if ( !filename.isEmpty() )
3587  {
3588  return mAuxiliaryStorage->saveAs( filename );
3589  }
3590  else
3591  {
3592  return mAuxiliaryStorage->saveAs( *this );
3593  }
3594 }
3595 
3596 QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
3597 {
3598  static QgsPropertiesDefinition sPropertyDefinitions
3599  {
3600  {
3601  QgsProject::DataDefinedServerProperty::WMSOnlineResource,
3602  QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
3603  },
3604  };
3605  return sPropertyDefinitions;
3606 }
3607 
3609 {
3610  return mAuxiliaryStorage.get();
3611 }
3612 
3614 {
3615  return mAuxiliaryStorage.get();
3616 }
3617 
3619 {
3620  return mMetadata;
3621 }
3622 
3624 {
3625  if ( metadata == mMetadata )
3626  return;
3627 
3628  mMetadata = metadata;
3629  mProjectScope.reset();
3630 
3631  emit metadataChanged();
3632 
3633  setDirty( true );
3634 }
3635 
3636 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
3637 {
3638  QSet<QgsMapLayer *> requiredLayers;
3639 
3640  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3641  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3642  {
3643  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3644  {
3645  requiredLayers.insert( it.value() );
3646  }
3647  }
3648  return requiredLayers;
3649 }
3650 
3651 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
3652 {
3653  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3654  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3655  {
3656  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3657  continue;
3658 
3659  if ( layers.contains( it.value() ) )
3660  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
3661  else
3662  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
3663  }
3664 }
3665 
3667 {
3668  // save colors to project
3669  QStringList customColors;
3670  QStringList customColorLabels;
3671 
3672  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
3673  for ( ; colorIt != colors.constEnd(); ++colorIt )
3674  {
3675  QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
3676  QString label = ( *colorIt ).second;
3677  customColors.append( color );
3678  customColorLabels.append( label );
3679  }
3680  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
3681  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
3682  mProjectScope.reset();
3683  emit projectColorsChanged();
3684 }
3685 
3686 void QgsProject::setBackgroundColor( const QColor &color )
3687 {
3688  if ( mBackgroundColor == color )
3689  return;
3690 
3691  mBackgroundColor = color;
3692  emit backgroundColorChanged();
3693 }
3694 
3696 {
3697  return mBackgroundColor;
3698 }
3699 
3700 void QgsProject::setSelectionColor( const QColor &color )
3701 {
3702  if ( mSelectionColor == color )
3703  return;
3704 
3705  mSelectionColor = color;
3706  emit selectionColorChanged();
3707 }
3708 
3710 {
3711  return mSelectionColor;
3712 }
3713 
3714 void QgsProject::setMapScales( const QVector<double> &scales )
3715 {
3716  mViewSettings->setMapScales( scales );
3717 }
3718 
3719 QVector<double> QgsProject::mapScales() const
3720 {
3721  return mViewSettings->mapScales();
3722 }
3723 
3725 {
3726  mViewSettings->setUseProjectScales( enabled );
3727 }
3728 
3730 {
3731  return mViewSettings->useProjectScales();
3732 }
3733 
3734 void QgsProject::generateTsFile( const QString &locale )
3735 {
3736  QgsTranslationContext translationContext;
3737  translationContext.setProject( this );
3738  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
3739 
3740  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
3741 
3742  translationContext.writeTsFile( locale );
3743 }
3744 
3745 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
3746 {
3747  if ( !mTranslator )
3748  {
3749  return sourceText;
3750  }
3751 
3752  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
3753 
3754  if ( result.isEmpty() )
3755  {
3756  return sourceText;
3757  }
3758  return result;
3759 }
3760 
3762 {
3763  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
3764  if ( !layers.empty() )
3765  {
3766  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3767  {
3768  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
3769  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3770  {
3771  if ( !( ( *it )->accept( visitor ) ) )
3772  return false;
3773 
3774  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3775  return false;
3776  }
3777  }
3778  }
3779 
3780  if ( !mLayoutManager->accept( visitor ) )
3781  return false;
3782 
3783  if ( !mAnnotationManager->accept( visitor ) )
3784  return false;
3785 
3786  return true;
3787 }
3788 
3790 GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
3791  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3792 {
3793  if ( !project )
3794  return;
3795 
3796  //build up color list from project. Do this in advance for speed
3797  QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
3798  QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
3799 
3800  //generate list from custom colors
3801  int colorIndex = 0;
3802  for ( QStringList::iterator it = colorStrings.begin();
3803  it != colorStrings.end(); ++it )
3804  {
3805  QColor color = QgsSymbolLayerUtils::decodeColor( *it );
3806  QString label;
3807  if ( colorLabels.length() > colorIndex )
3808  {
3809  label = colorLabels.at( colorIndex );
3810  }
3811 
3812  mColors.insert( label.toLower(), color );
3813  colorIndex++;
3814  }
3815 }
3816 
3817 GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
3818  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3819  , mColors( colors )
3820 {
3821 }
3822 
3823 QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3824 {
3825  QString colorName = values.at( 0 ).toString().toLower();
3826  if ( mColors.contains( colorName ) )
3827  {
3828  return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
3829  }
3830  else
3831  return QVariant();
3832 }
3833 
3834 QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
3835 {
3836  return new GetNamedProjectColor( mColors );
3837 }
static QString version()
Version string.
Definition: qgis.cpp:276
@ Critical
Definition: qgis.h:92
@ Success
Definition: qgis.h:93
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
virtual bool writeXml(QDomElement &collectionElem, const QgsPropertiesDefinition &definitions) const
Writes the current state of the property collection into an XML element.
Represents a map layer containing a set of georeferenced annotations, e.g.
void setTransformContext(const QgsCoordinateTransformContext &context) override
Sets the coordinate transform context to transformContext.
void reset()
Resets the annotation layer to a default state, and clears all items from it.
Manages storage of a set of QgsAnnotation annotation objects.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ....
static QString userFullName()
Returns the user's operating system login account full display name.
static QString userLoginName()
Returns the user's operating system login account name.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
bool save()
Commits changes and starts editing then.
Class providing some utility methods to manage auxiliary storage.
static QString extension()
Returns the extension used for auxiliary databases.
Manages storage of a set of bookmarks.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all bookmarks present in the XML document.
void clear()
Removes and deletes all bookmarks from the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
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.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString description() const
Returns the descriptive name of the CRS, e.g., "WGS 84" or "GDA 94 / Vicgrid94".
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
QString authid() const
Returns the authority identifier for the CRS.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
Contains information about the context in which a coordinate transform is executed.
void readSettings()
Reads the context's state from application settings.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context's state to a DOM element.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context's state from a DOM element.
void setProviderProperty(ProviderProperty property, const QVariant &value)
Allows setting arbitrary properties on the provider.
@ EvaluateDefaultValues
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
QVariantMap config() const
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
An expression node for expression functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
QString alias
Definition: qgsfield.h:61
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:559
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
Stores global configuration for labeling engine.
Class used to work with layer dependencies stored in a XML project or layer definition file.
Layer tree group node serves as a container for layers and further groups.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QString name() const override
Returns the group's name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QList< QgsLayerTreeGroup * > findGroups() const
Find all group layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already)
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the childrens, disconnect all the forwarded and external signals and sets their parent to nul...
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Listens to the updates in map layer registry and does changes in layer tree.
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
static void storeOriginalLayersProperties(QgsLayerTreeGroup *group, const QDomDocument *doc)
Stores in a layer's originalXmlProperties the layer properties information.
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *project)
Updates an embedded group from a project.
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Manages storage of a set of layouts.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
static QgsMapLayerType typeFromString(const QString &string, bool &ok)
Returns the map layer type corresponding a string value.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the store.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the store.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the store.
void allLayersRemoved()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the store.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the store.
QgsMapLayer * mapLayer(const QString &id) const
Retrieve a pointer to a layer by layer id.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the store.
Base class for all map layer types.
Definition: qgsmaplayer.h:85
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:92
void configChanged()
Emitted whenever the configuration is changed.
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
void dataSourceChanged()
Emitted whenever the layer's data source has been changed.
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
bool isValid
Definition: qgsmaplayer.h:93
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:154
@ Removable
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
Definition: qgsmaplayer.h:155
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:585
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:584
Container class that allows storage of map themes consisting of visible map layers and layer styles.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Resolves relative paths into absolute paths and vice versa.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:116
QString projectFile() const
Returns the current .qgs project file or an empty string if there's none.
Definition: qgsarchive.cpp:129
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Definition: qgsarchive.cpp:142
Interface for classes that handle missing layer files when reading project file.
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed.
Contains settings and properties relating to how a QgsProject should display values such as map coord...
void reset()
Resets the settings to a default state.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
Class to convert from older project file versions to newer.
A structured metadata store for a map layer.
bool writeMetadataXml(QDomElement &metadataElement, QDomDocument &document) const override
Stores state in a DOM node.
void setCreationDateTime(const QDateTime &creationDateTime)
Sets the project's creation date/timestamp.
bool readMetadataXml(const QDomElement &metadataElement) override
Sets state from DOM document.
void setAuthor(const QString &author)
Sets the project author string.
Project property key node.
QString name() const
The name of the property is used as identifier.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
void removeKey(const QString &keyName)
Removes the specified key.
void dump(int tabs=0) const override
Dumps out the keys and values.
bool isEmpty() const
Returns true if this property contains no sub-keys.
virtual void clearKeys()
Deletes any sub-nodes from the property.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
QgsProjectProperty * find(const QString &propertyName) const
Attempts to find a property with a matching sub-key name.
void setName(const QString &name)
The name of the property is used as identifier.
QVariant value() const override
If this key has a value, it will be stored by its name in its properties.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
int count() const
Returns the number of sub-keys contained by this property.
QgsProjectPropertyValue * setValue(const QString &name, const QVariant &value)
Sets the value associated with this key.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
An Abstract Base Class for QGIS project property hierarchys.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
virtual bool isValue() const =0
Returns true if the property is a QgsProjectPropertyValue.
QgsProjectStorage * projectStorageFromUri(const QString &uri)
Returns storage implementation if the URI matches one. Returns nullptr otherwise (it is a normal file...
Metadata associated with a project.
Abstract interface for project storage - to be implemented by various backends and registered in QgsP...
Contains temporal settings and properties for the project, this may be used when animating maps or sh...
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void reset()
Resets the settings to a default state.
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
A class to describe the version of a project.
QString text() const
Returns a string representation of the version.
Contains settings and properties relating to how a QgsProject should be displayed inside map canvas,...
bool useProjectScales() const
Returns true if project mapScales() are enabled.
void reset()
Resets the settings to a default state.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
QVector< double > mapScales() const
Returns the list of custom project map scales.
void mapScalesChanged()
Emitted when the list of custom project map scales changes.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:99
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
QgsProject(QObject *parent=nullptr)
Create a new QgsProject.
Definition: qgsproject.cpp:398
void setAvoidIntersectionsMode(const AvoidIntersectionsMode mode)
Sets the avoid intersections mode.
QgsRelationManager * relationManager
Definition: qgsproject.h:109
bool write()
Writes the project to its current associated file (see fileName() ).
void removeMapLayer(const QString &layerId)
Remove a layer from the registry by layer ID.
bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the default area measurement units for the project.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
void clear()
Clears the project, removing all settings and resetting it back to an empty, default state.
Definition: qgsproject.cpp:813
~QgsProject() override
Definition: qgsproject.cpp:480
QString error() const
Returns error message from previous read/write.
Q_DECL_DEPRECATED void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
Q_DECL_DEPRECATED void setNonIdentifiableLayers(const QList< QgsMapLayer * > &layers)
Set a list of layers which should not be taken into account on map identification.
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project's associated file.
Definition: qgsproject.cpp:694
QString presetHomePath() const
Returns any manual project home path setting, or an empty string if not set.
void setBackgroundColor(const QColor &color)
Sets the default background color used by default map canvases.
void setCrs(const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid=false)
Sets the project's native coordinate reference system.
Definition: qgsproject.cpp:759
QColor selectionColor
Definition: qgsproject.h:114
QString title() const
Returns the project's title.
Definition: qgsproject.cpp:522
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:501
Q_DECL_DEPRECATED void mapScalesChanged()
Emitted when the list of custom project map scales changes.
QString ellipsoid
Definition: qgsproject.h:106
void fileNameChanged()
Emitted when the file name of the project changes.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted when a layer is being saved.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the default distance measurement units for the project.
QgsPropertyCollection dataDefinedServerProperties() const
Returns the data defined properties used for overrides in user defined server parameters.
Definition: qgsproject.cpp:654
Q_DECL_DEPRECATED void nonIdentifiableLayersChanged(QStringList nonIdentifiableLayers)
Emitted when the list of layer which are excluded from map identification changes.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
void setSelectionColor(const QColor &color)
Sets the color used to highlight selected features.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
void setEvaluateDefaultValues(bool evaluateDefaultValues)
Defines if default values should be evaluated on provider side when requested and not when committed.
void crsChanged()
Emitted when the CRS of the project has changed.
QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const override
Translates the project with QTranslator and qm file.
QgsSnappingConfig snappingConfig
Definition: qgsproject.h:108
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:659
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
void avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
void setDataDefinedServerProperties(const QgsPropertyCollection &properties)
Sets the data defined properties used for overrides in user defined server parameters to properties.
Definition: qgsproject.cpp:649
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
Definition: qgsproject.cpp:596
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
const QgsBookmarkManager * bookmarkManager() const
Returns the project's bookmark manager, which manages bookmarks within the project.
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted after the basic initialization of a layer from the project file is done.
void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
void setTrustLayerMetadata(bool trust)
Sets the trust option allowing to indicate if the extent has to be read from the XML document when da...
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets project's global labeling engine settings.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void metadataChanged()
Emitted when the project's metadata is changed.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:1075
QString absolutePath() const
Returns full absolute path to the project folder if the project is stored in a file system - derived ...
Definition: qgsproject.cpp:718
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
Q_DECL_DEPRECATED void setRequiredLayers(const QSet< QgsMapLayer * > &layers)
Configures a set of map layers that are required in the project and therefore they should not get rem...
QList< QgsVectorLayer * > avoidIntersectionsLayers
Definition: qgsproject.h:111
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
Definition: qgsproject.h:148
@ AvoidIntersectionsLayers
Overlap with features from a specified list of layers when digitizing new features not allowed.
@ AllowIntersections
Overlap with any feature allowed when digitizing new features.
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString baseName() const
Returns the base name of the project file without the path and without extension - derived from fileN...
Definition: qgsproject.cpp:740
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:107
void generateTsFile(const QString &locale)
Triggers the collection strings of .qgs to be included in ts file and calls writeTsFile()
QStringList entryList(const QString &scope, const QString &key) const
Returns a list of child keys with values which exist within the the specified scope and key.
QgsAnnotationManager * annotationManager()
Returns pointer to the project's annotation manager.
QgsProjectMetadata metadata
Definition: qgsproject.h:112
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
QgsUnitTypes::DistanceUnit distanceUnits() const
Convenience function to query default distance measurement units for project.
QString saveUser() const
Returns the user name that did the last save.
Definition: qgsproject.cpp:527
void setProjectColors(const QgsNamedColorList &colors)
Sets the colors for the project's color scheme (see QgsProjectColorScheme).
QgsCoordinateTransformContext transformContext
Definition: qgsproject.h:105
void labelingEngineSettingsChanged()
Emitted when global configuration of the labeling engine changes.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Reads a boolean from the specified scope and key.
QgsMapLayerStore * layerStore()
Returns a pointer to the project's internal layer store.
void readProject(const QDomDocument &)
Emitted when a project is being read.
QString originalPath() const
Returns the original path associated with the project.
Definition: qgsproject.cpp:689
void setOriginalPath(const QString &path)
Sets the original path associated with the project.
Definition: qgsproject.cpp:684
void dumpProperties() const
Dump out current project properties to stderr.
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
Definition: qgsproject.cpp:495
int validCount() const
Returns the number of registered valid layers.
const QgsLayoutManager * layoutManager() const
Returns the project's layout manager, which manages print layouts, atlases and reports within the pro...
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QString fileName
Definition: qgsproject.h:102
bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
void setMetadata(const QgsProjectMetadata &metadata)
Sets the project's metadata store.
void missingDatumTransforms(const QStringList &missingTransforms)
Emitted when datum transforms stored in the project are not available locally.
QgsTransactionGroup * transactionGroup(const QString &providerKey, const QString &connString)
Returns the matching transaction group from a provider key and connection string.
QgsCoordinateReferenceSystem crs
Definition: qgsproject.h:104
void readProjectWithContext(const QDomDocument &, QgsReadWriteContext &context)
Emitted when a project is being read.
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
QStringList nonIdentifiableLayers
Definition: qgsproject.h:101
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed.
const QgsAuxiliaryStorage * auxiliaryStorage() const
Returns the current const auxiliary storage.
void reloadAllLayers()
Reload all registered layer's provider data caches, synchronising the layer with any changes in the d...
int count() const
Returns the number of registered layers.
void loadingLayerMessageReceived(const QString &layerName, const QList< QgsReadWriteContext::ReadWriteMessage > &messages)
Emitted when loading layers has produced some messages.
void setTitle(const QString &title)
Sets the project's title.
Definition: qgsproject.cpp:510
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
void writeProject(QDomDocument &)
Emitted when the project is being written.
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:704
bool readLayer(const QDomNode &layerNode)
Reads the layer described in the associated DOM node.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
Reads a double from the specified scope and key.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean value to the project file.
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags=QgsProject::ReadFlags())
Create layer group instance defined in an arbitrary project file.
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:729
QDateTime lastSaveDateTime() const
Returns the date and time when the project was last saved.
Definition: qgsproject.cpp:537
void projectSaved()
Emitted when the project file has been written and closed.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void setEllipsoid(const QString &ellipsoid)
Sets the project's ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:782
bool read(const QString &filename, QgsProject::ReadFlags flags=QgsProject::ReadFlags())
Reads given project file from the given file.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:797
QColor backgroundColor
Definition: qgsproject.h:113
void layerLoaded(int i, int n)
Emitted when a layer from a projects was read.
QStringList subkeyList(const QString &scope, const QString &key) const
Returns a list of child keys which contain other keys that exist within the the specified scope and k...
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Reads a string list from the specified scope and key.
void selectionColorChanged()
Emitted whenever the project's selection color has been changed.
bool topologicalEditing
Definition: qgsproject.h:115
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, bool saveFlag=true, QgsProject::ReadFlags flags=QgsProject::ReadFlags())
Creates a maplayer instance defined in an arbitrary project file.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project's global labeling engine settings.
void removeAllMapLayers()
Removes all registered layers.
Q_DECL_DEPRECATED QVector< double > mapScales() const
Returns the list of custom project map scales.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:552
void backgroundColorChanged()
Emitted whenever the project's canvas background color has been changed.
@ FlagTrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ FlagDontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
@ FlagDontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
@ FlagDontResolveLayers
Don't resolve layer paths (i.e. don't load any layer content). Dramatically improves project read tim...
const QgsProjectViewSettings * viewSettings() const
Returns the project's view settings, which contains settings and properties relating to how a QgsProj...
QString readPath(const QString &filename) const
Transforms a filename read from the project file to an absolute path.
void registerTranslatableContainers(QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId)
Registers the containers that require translation into the translationContext.
Definition: qgsproject.cpp:578
void oldProjectVersionWarning(const QString &)
Emitted when an old project file is read.
Q_DECL_DEPRECATED QSet< QgsMapLayer * > requiredLayers() const
Returns a set of map layers that are required in the project and therefore they should not get remove...
void transformContextChanged()
Emitted when the project transformContext() is changed.
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
void legendLayersAdded(const QList< QgsMapLayer * > &layers)
Emitted, when a layer was added to the registry and the legend.
QVariantMap customVariables() const
A map of custom project variables.
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer * > &layers)
Sets the list of layers with which intersections should be avoided.
void homePathChanged()
Emitted when the home path of the project changes.
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const
Returns the default CRS for new layers based on the settings and the current project CRS.
const QgsProjectDisplaySettings * displaySettings() const
Returns the project's display settings, which settings and properties relating to how a QgsProject sh...
QString saveUserFullName() const
Returns the full user name that did the last save.
Definition: qgsproject.cpp:532
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the registry.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
QString homePath
Definition: qgsproject.h:103
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:547
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
void isDirtyChanged(bool dirty)
Emitted when the project dirty status changes.
Q_DECL_DEPRECATED bool useProjectScales() const
Returns true if project mapScales() are enabled.
Q_DECL_DEPRECATED void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
void setPresetHomePath(const QString &path)
Sets the project's home path.
Definition: qgsproject.cpp:564
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName.
QgsProjectStorage * projectStorage() const
Returns pointer to project storage implementation that handles read/write of the project file.
Definition: qgsproject.cpp:699
QString layerIsEmbedded(const QString &id) const
Returns the source project file path if the layer with matching id is embedded from other project fil...
const QgsProjectTimeSettings * timeSettings() const
Returns the project's time settings, which contains the project's temporal range and other time based...
void topologicalEditingChanged()
Emitted when the topological editing flag has changed.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key from the specified scope.
QgsProjectVersion lastSaveVersion() const
Returns the QGIS version which the project was last saved using.
Definition: qgsproject.cpp:542
void avoidIntersectionsModeChanged()
Emitted whenever the avoid intersections mode has changed.
void loadingLayer(const QString &layerName)
Emitted when a layer is loaded.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Definition for a property.
Definition: qgsproperty.h:48
@ String
Any string value.
Definition: qgsproperty.h:62
The class is used as a container of context for various read/write operations on other objects.
void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets data coordinate transform context to transformContext.
QList< QgsReadWriteContext::ReadWriteMessage > takeMessages()
Returns the stored messages and remove them.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
This class manages a set of relations between layers.
void clear()
Remove any relation managed by this class.
void updateRelationsStatus()
Updates relations status.
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
void clear(const QString &group="startup")
clear Clear all profile data.
Expression function for use within a QgsExpressionContextScope.
Scoped object for logging of the runtime for a single operation or group of operations.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
This is a container for configuration of the snapping of the project.
bool addLayers(const QList< QgsMapLayer * > &layers)
Adds the specified layers as individual layers to the configuration with standard configuration.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
void reset()
reset to default values
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
void clearIndividualLayerSettings()
Removes all individual layer snapping settings.
bool removeLayers(const QList< QgsMapLayer * > &layers)
Removes the specified layers from the individual layer configuration.
An interface for classes which can visit style entity (e.g.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
bool isEmpty() const
Returns true if there are no layers in this transaction group.
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
Used for the collecting of strings from projects for translation and creation of ts files.
void registerTranslation(const QString &context, const QString &source)
Registers the source to be translated.
void writeTsFile(const QString &locale)
Writes the Ts-file.
void setFileName(const QString &fileName)
Sets the fileName of the TS file.
void setProject(QgsProject *project)
Sets the project being translated.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE QgsUnitTypes::AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
AreaUnit
Units of area.
Definition: qgsunittypes.h:94
@ AreaSquareMeters
Square meters.
Definition: qgsunittypes.h:95
Represents a vector layer which manages a vector based data sets.
bool loadAuxiliaryLayer(const QgsAuxiliaryStorage &storage, const QString &key=QString())
Loads the auxiliary layer for this vector layer.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
void setReadExtentFromXml(bool readExtentFromXml)
Flag allowing to indicate if the extent has to be read from the XML document when data source has no ...
QgsEditFormConfig editFormConfig
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgsmaplayer.h:69
@ PointCloudLayer
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
CORE_EXPORT bool isZipFile(const QString &filename)
Returns true if the file name is a zipped file ( i.e with a '.qgz' extension, false otherwise.
Definition: qgsziputils.cpp:29
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:716
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:798
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:797
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:710
const QgsField & field
Definition: qgsfield.h:472
#define QgsDebugCall
Definition: qgslogger.h:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:1769
void _getProperties(const QDomDocument &doc, QgsProjectPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:948
QgsPropertyCollection getDataDefinedServerProperties(const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions)
Returns the data defined server properties collection found in "doc" to "dataDefinedServerProperties"...
Definition: qgsproject.cpp:977
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:174
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Remove a given key.
Definition: qgsproject.cpp:338
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value, bool &propertiesModified)
Add the given key and value.
Definition: qgsproject.cpp:251
QgsProjectVersion getVersion(const QDomDocument &doc)
Returns the version string found in the given DOM document.
QStringList makeKeyTokens_(const QString &scope, const QString &key)
Take the given scope and key and convert them to a string list of key tokens that will be used to nav...
Definition: qgsproject.cpp:133
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
Definition: qgsproject.cpp:912
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
const QgsCoordinateReferenceSystem & crs
const QString & typeName
Setting options for loading annotation layers.
Single variable definition for use within a QgsExpressionContextScope.
Contains information relating to a node (i.e.