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