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