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