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