QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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"
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 
755  // if annotation layer doesn't have a crs (i.e. in a newly created project), it should
756  // initially inherit the project CRS
757  if ( !mMainAnnotationLayer->crs().isValid() )
758  mMainAnnotationLayer->setCrs( crs );
759 
760  setDirty( true );
761  emit crsChanged();
762  }
763 
764  if ( adjustEllipsoid )
766 }
767 
768 QString QgsProject::ellipsoid() const
769 {
770  if ( !crs().isValid() )
771  return geoNone();
772 
773  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), geoNone() );
774 }
775 
776 void QgsProject::setEllipsoid( const QString &ellipsoid )
777 {
778  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
779  return;
780 
781  mProjectScope.reset();
782  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
783  emit ellipsoidChanged( ellipsoid );
784 }
785 
787 {
788  return mTransformContext;
789 }
790 
792 {
793  if ( context == mTransformContext )
794  return;
795 
796  mTransformContext = context;
797  mProjectScope.reset();
798 
799  mMainAnnotationLayer->setTransformContext( context );
800  for ( auto &layer : mLayerStore.get()->mapLayers() )
801  {
802  layer->setTransformContext( context );
803  }
805 }
806 
808 {
809  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
810 
811  mProjectScope.reset();
812  mFile.setFileName( QString() );
813  mProperties.clearKeys();
814  mSaveUser.clear();
815  mSaveUserFull.clear();
816  mSaveDateTime = QDateTime();
817  mSaveVersion = QgsProjectVersion();
818  mHomePath.clear();
819  mCachedHomePath.clear();
820  mAutoTransaction = false;
821  mEvaluateDefaultValues = false;
822  mDirty = false;
823  mTrustLayerMetadata = false;
824  mCustomVariables.clear();
826  mMetadata = QgsProjectMetadata();
827  if ( !mSettings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
828  {
829  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
831  }
832  emit metadataChanged();
833 
835  context.readSettings();
836  setTransformContext( context );
837 
838  mEmbeddedLayers.clear();
839  mRelationManager->clear();
840  mAnnotationManager->clear();
841  mLayoutManager->clear();
842  mBookmarkManager->clear();
843  mViewSettings->reset();
844  mTimeSettings->reset();
845  mDisplaySettings->reset();
846  mSnappingConfig.reset();
847  mAvoidIntersectionsMode = AvoidIntersectionsMode::AllowIntersections;
850 
851  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
853 
854  mLabelingEngineSettings->clear();
855 
856  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
857  mArchive.reset( new QgsArchive() );
858 
860 
861  if ( !mIsBeingDeleted )
862  {
863  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
864  emit projectColorsChanged();
865  }
866 
867  // reset some default project properties
868  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
869  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
870  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
871 
872  const bool defaultRelativePaths = mSettings.value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true ).toBool();
874 
875  //copy default units to project
876  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
877  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
878 
879  int red = mSettings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
880  int green = mSettings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
881  int blue = mSettings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
882  setBackgroundColor( QColor( red, green, blue ) );
883 
884  red = mSettings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
885  green = mSettings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
886  blue = mSettings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
887  const int alpha = mSettings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
888  setSelectionColor( QColor( red, green, blue, alpha ) );
889 
890  mSnappingConfig.clearIndividualLayerSettings();
891 
893  mRootGroup->clear();
894  if ( mMainAnnotationLayer )
895  mMainAnnotationLayer->reset();
896 
897  snapSingleBlocker.release();
898 
899  if ( !mBlockSnappingUpdates )
900  emit snappingConfigChanged( mSnappingConfig );
901 
902  setDirty( false );
903  emit homePathChanged();
904  emit cleared();
905 }
906 
907 // basically a debugging tool to dump property list values
908 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
909 {
910  QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
911  topQgsPropertyKey.dump();
912 }
913 
942 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
943 {
944  const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
945 
946  if ( propertiesElem.isNull() ) // no properties found, so we're done
947  {
948  return;
949  }
950 
951  const QDomNodeList scopes = propertiesElem.childNodes();
952 
953  if ( propertiesElem.firstChild().isNull() )
954  {
955  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
956  return;
957  }
958 
959  if ( ! project_properties.readXml( propertiesElem ) )
960  {
961  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
962  }
963 }
964 
971 QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
972 {
973  QgsPropertyCollection ddServerProperties;
974  // Read data defined server properties
975  const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral( "dataDefinedServerProperties" ) );
976  if ( !ddElem.isNull() )
977  {
978  if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
979  {
980  QgsDebugMsg( QStringLiteral( "dataDefinedServerProperties.readXml() failed" ) );
981  }
982  }
983  return ddServerProperties;
984 }
985 
990 static void _getTitle( const QDomDocument &doc, QString &title )
991 {
992  const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral( "title" ) );
993 
994  title.clear(); // by default the title will be empty
995 
996  if ( titleNode.isNull() )
997  {
998  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
999  return;
1000  }
1001 
1002  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1003  {
1004  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1005  return;
1006  }
1007 
1008  const QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1009 
1010  if ( !titleTextNode.isText() )
1011  {
1012  QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1013  return;
1014  }
1015 
1016  const QDomText titleText = titleTextNode.toText();
1017 
1018  title = titleText.data();
1019 
1020 }
1021 
1022 static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1023 {
1024  const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1025 
1026  if ( !nl.count() )
1027  {
1028  QgsDebugMsg( "unable to find qgis element" );
1029  return;
1030  }
1031 
1032  const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1033 
1034  const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1035  lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
1036  lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
1037  lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral( "saveDateTime" ), QString() ), Qt::ISODate );
1038 }
1039 
1040 
1041 QgsProjectVersion getVersion( const QDomDocument &doc )
1042 {
1043  const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1044 
1045  if ( !nl.count() )
1046  {
1047  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
1048  return QgsProjectVersion( 0, 0, 0, QString() );
1049  }
1050 
1051  const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1052 
1053  const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1054  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
1055  return projectVersion;
1056 }
1057 
1058 
1060 {
1061  return mSnappingConfig;
1062 }
1063 
1065 {
1066  if ( mSnappingConfig == snappingConfig )
1067  return;
1068 
1069  mSnappingConfig = snappingConfig;
1070  setDirty( true );
1071  emit snappingConfigChanged( mSnappingConfig );
1072 }
1073 
1075 {
1076  if ( mAvoidIntersectionsMode == mode )
1077  return;
1078 
1079  mAvoidIntersectionsMode = mode;
1081 }
1082 
1083 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, QgsProject::ReadFlags flags )
1084 {
1085  // Layer order is set by the restoring the legend settings from project file.
1086  // This is done on the 'readProject( ... )' signal
1087 
1088  QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
1089 
1090  // process the map layer nodes
1091 
1092  if ( layerElement.isNull() ) // if we have no layers to process, bail
1093  {
1094  return true; // Decided to return "true" since it's
1095  // possible for there to be a project with no
1096  // layers; but also, more imporantly, this
1097  // would cause the tests/qgsproject to fail
1098  // since the test suite doesn't currently
1099  // support test layers
1100  }
1101 
1102  bool returnStatus = true;
1103  int numLayers = 0;
1104 
1105  while ( ! layerElement.isNull() )
1106  {
1107  numLayers++;
1108  layerElement = layerElement.nextSiblingElement( QStringLiteral( "maplayer" ) );
1109  }
1110 
1111  // order layers based on their dependencies
1112  QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), QStringLiteral( "projectload" ) );
1113  const QgsLayerDefinition::DependencySorter depSorter( doc );
1114  if ( depSorter.hasCycle() )
1115  return false;
1116 
1117  // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1118  if ( depSorter.hasMissingDependency() )
1119  returnStatus = false;
1120 
1121  emit layerLoaded( 0, numLayers );
1122 
1123  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1124  const int totalLayerCount = sortedLayerNodes.count();
1125 
1126  int i = 0;
1127  for ( const QDomNode &node : sortedLayerNodes )
1128  {
1129  const QDomElement element = node.toElement();
1130 
1131  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1132  if ( !name.isNull() )
1133  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1134 
1135  profile.switchTask( name );
1136 
1137  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1138  {
1139  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1140  }
1141  else
1142  {
1143  QgsReadWriteContext context;
1144  context.setPathResolver( pathResolver() );
1145  context.setProjectTranslator( this );
1147 
1148  if ( !addLayer( element, brokenNodes, context, flags ) )
1149  {
1150  returnStatus = false;
1151  }
1152  const auto messages = context.takeMessages();
1153  if ( !messages.isEmpty() )
1154  {
1155  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1156  }
1157  }
1158  emit layerLoaded( i + 1, totalLayerCount );
1159  i++;
1160  }
1161 
1162  return returnStatus;
1163 }
1164 
1165 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, QgsProject::ReadFlags flags )
1166 {
1167  const QString type = layerElem.attribute( QStringLiteral( "type" ) );
1168  QgsDebugMsgLevel( "Layer type is " + type, 4 );
1169  std::unique_ptr<QgsMapLayer> mapLayer;
1170 
1171  QgsScopedRuntimeProfile profile( tr( "Create layer" ), QStringLiteral( "projectload" ) );
1172 
1173  bool ok = false;
1174  const QgsMapLayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1175  if ( !ok )
1176  {
1177  QgsDebugMsg( QStringLiteral( "Unknown layer type \"%1\"" ).arg( type ) );
1178  return false;
1179  }
1180 
1181  switch ( layerType )
1182  {
1184  {
1185  mapLayer = std::make_unique<QgsVectorLayer>();
1186  // apply specific settings to vector layer
1187  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1188  {
1189  vl->setReadExtentFromXml( mTrustLayerMetadata || ( flags & QgsProject::ReadFlag::FlagTrustLayerMetadata ) );
1190  }
1191  break;
1192  }
1193 
1195  mapLayer = std::make_unique<QgsRasterLayer>();
1196  break;
1197 
1199  mapLayer = std::make_unique<QgsMeshLayer>();
1200  break;
1201 
1203  mapLayer = std::make_unique<QgsVectorTileLayer>();
1204  break;
1205 
1207  mapLayer = std::make_unique<QgsPointCloudLayer>();
1208  break;
1209 
1211  {
1212  const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1213  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1214  break;
1215  }
1216 
1218  {
1219  const QgsAnnotationLayer::LayerOptions options( mTransformContext );
1220  mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1221  break;
1222  }
1223  }
1224 
1225  if ( !mapLayer )
1226  {
1227  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1228  return false;
1229  }
1230 
1231  Q_CHECK_PTR( mapLayer ); // NOLINT
1232 
1233  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1234  // because if it was, the newly created layer will not be added to the store and it would leak.
1235  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1236  Q_ASSERT( ! layerId.isEmpty() );
1237  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1238 
1239  // have the layer restore state that is stored in Dom node
1240  QgsMapLayer::ReadFlags layerFlags = QgsMapLayer::ReadFlags();
1242  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1243  // Propagate trust layer metadata flag
1244  if ( mTrustLayerMetadata || ( flags & QgsProject::ReadFlag::FlagTrustLayerMetadata ) )
1246 
1247  profile.switchTask( tr( "Load layer source" ) );
1248  const bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1249 
1250  profile.switchTask( tr( "Add layer to project" ) );
1251  QList<QgsMapLayer *> newLayers;
1252  newLayers << mapLayer.get();
1253  if ( layerIsValid || flags & QgsProject::ReadFlag::FlagDontResolveLayers )
1254  {
1255  emit readMapLayer( mapLayer.get(), layerElem );
1256  addMapLayers( newLayers );
1257  }
1258  else
1259  {
1260  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1261  addMapLayers( newLayers, false );
1262  newLayers.first();
1263  QgsDebugMsg( "Unable to load " + type + " layer" );
1264  brokenNodes.push_back( layerElem );
1265  }
1266 
1267  // It should be safe to delete the layer now if layer was stored, because all the store
1268  // had to to was to reset the data source in case the validity changed.
1269  if ( ! layerWasStored )
1270  {
1271  mapLayer.release();
1272  }
1273 
1274  return layerIsValid;
1275 }
1276 
1277 bool QgsProject::read( const QString &filename, QgsProject::ReadFlags flags )
1278 {
1279  mFile.setFileName( filename );
1280  mCachedHomePath.clear();
1281  mProjectScope.reset();
1282 
1283  return read( flags );
1284 }
1285 
1286 bool QgsProject::read( QgsProject::ReadFlags flags )
1287 {
1288  const QString filename = mFile.fileName();
1289  bool returnValue;
1290 
1291  if ( QgsProjectStorage *storage = projectStorage() )
1292  {
1293  QTemporaryFile inDevice;
1294  if ( !inDevice.open() )
1295  {
1296  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1297  return false;
1298  }
1299 
1300  QgsReadWriteContext context;
1301  context.setProjectTranslator( this );
1302  if ( !storage->readProject( filename, &inDevice, context ) )
1303  {
1304  QString err = tr( "Unable to open %1" ).arg( filename );
1305  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1306  if ( !messages.isEmpty() )
1307  err += QStringLiteral( "\n\n" ) + messages.last().message();
1308  setError( err );
1309  return false;
1310  }
1311  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1312  }
1313  else
1314  {
1315  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1316  {
1317  returnValue = unzip( mFile.fileName(), flags );
1318  }
1319  else
1320  {
1321  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1322  const QFileInfo finfo( mFile.fileName() );
1323  const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1324  if ( QFile( attachmentsZip ).exists() )
1325  {
1326  std::unique_ptr<QgsArchive> archive( new QgsArchive() );
1327  if ( archive->unzip( attachmentsZip ) )
1328  {
1329  mArchive = std::move( archive );
1330  }
1331  }
1332  returnValue = readProjectFile( mFile.fileName(), flags );
1333  }
1334 
1335  //on translation we should not change the filename back
1336  if ( !mTranslator )
1337  {
1338  mFile.setFileName( filename );
1339  mCachedHomePath.clear();
1340  mProjectScope.reset();
1341  }
1342  else
1343  {
1344  //but delete the translator
1345  mTranslator.reset( nullptr );
1346  }
1347  }
1348  emit homePathChanged();
1349  return returnValue;
1350 }
1351 
1352 bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags flags )
1353 {
1354  // avoid multiple emission of snapping updated signals
1355  ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
1356 
1357  QFile projectFile( filename );
1358  clearError();
1359 
1360  QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
1361  QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), QStringLiteral( "projectload" ) );
1362 
1363  const QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), QgsApplication::settingsLocaleUserLocale.value() );
1364 
1365  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1366  {
1367  mTranslator.reset( new QTranslator() );
1368  ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1369  }
1370 
1371  profile.switchTask( tr( "Reading project file" ) );
1372  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1373 
1374  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1375  {
1376  projectFile.close();
1377 
1378  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1379 
1380  return false;
1381  }
1382 
1383  // location of problem associated with errorMsg
1384  int line, column;
1385  QString errorMsg;
1386 
1387  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1388  {
1389  const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1390  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1391 
1392  QgsDebugMsg( errorString );
1393 
1394  projectFile.close();
1395 
1396  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1397 
1398  return false;
1399  }
1400 
1401  projectFile.close();
1402 
1403  QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1404 
1405  // get project version string, if any
1406  const QgsProjectVersion fileVersion = getVersion( *doc );
1407  const QgsProjectVersion thisVersion( Qgis::version() );
1408 
1409  profile.switchTask( tr( "Updating project file" ) );
1410  if ( thisVersion > fileVersion )
1411  {
1412  QgsLogger::warning( "Loading a file that was saved with an older "
1413  "version of qgis (saved in " + fileVersion.text() +
1414  ", loaded in " + Qgis::version() +
1415  "). Problems may occur." );
1416 
1417  QgsProjectFileTransform projectFile( *doc, fileVersion );
1418 
1419  // Shows a warning when an old project file is read.
1420  emit oldProjectVersionWarning( fileVersion.text() );
1421 
1422  projectFile.updateRevision( thisVersion );
1423  }
1424 
1425  // start new project, just keep the file name and auxiliary storage
1426  profile.switchTask( tr( "Creating auxiliary storage" ) );
1427  const QString fileName = mFile.fileName();
1428  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1429  std::unique_ptr<QgsArchive> archive = std::move( mArchive );
1430  clear();
1431  mAuxiliaryStorage = std::move( aStorage );
1432  mArchive = std::move( archive );
1433  mFile.setFileName( fileName );
1434  mCachedHomePath.clear();
1435  mProjectScope.reset();
1436  mSaveVersion = fileVersion;
1437 
1438  // now get any properties
1439  profile.switchTask( tr( "Reading properties" ) );
1440  _getProperties( *doc, mProperties );
1441 
1442  // now get the data defined server properties
1443  mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
1444 
1445  QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1446 
1447 #if 0
1448  dump_( mProperties );
1449 #endif
1450 
1451  // get older style project title
1452  QString oldTitle;
1453  _getTitle( *doc, oldTitle );
1454 
1455  readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
1456 
1457  const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1458  if ( homePathNl.count() > 0 )
1459  {
1460  const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1461  const QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1462  if ( !homePath.isEmpty() )
1464  }
1465  else
1466  {
1467  emit homePathChanged();
1468  }
1469 
1470  const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1471  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1472  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1474  const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1475  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1476  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1477  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1479 
1480  QgsReadWriteContext context;
1481  context.setPathResolver( pathResolver() );
1482  context.setProjectTranslator( this );
1483 
1484  //crs
1485  QgsCoordinateReferenceSystem projectCrs;
1486  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1487  {
1488  // first preference - dedicated projectCrs node
1489  const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1490  if ( !srsNode.isNull() )
1491  {
1492  projectCrs.readXml( srsNode );
1493  }
1494 
1495  if ( !projectCrs.isValid() )
1496  {
1497  const QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1498  const long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1499  const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1500 
1501  // authid should be prioritized over all
1502  const bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
1503  if ( !authid.isEmpty() && !isUserAuthId )
1504  projectCrs = QgsCoordinateReferenceSystem( authid );
1505 
1506  // try the CRS
1507  if ( !projectCrs.isValid() && currentCRS >= 0 )
1508  {
1509  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1510  }
1511 
1512  // if that didn't produce a match, try the proj.4 string
1513  if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
1514  {
1515  projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
1516  }
1517 
1518  // last just take the given id
1519  if ( !projectCrs.isValid() )
1520  {
1521  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1522  }
1523  }
1524  }
1525  mCrs = projectCrs;
1526 
1527  QStringList datumErrors;
1528  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1529  {
1530  emit missingDatumTransforms( datumErrors );
1531  }
1532  emit transformContextChanged();
1533 
1534  //add variables defined in project file - do this early in the reading cycle, as other components
1535  //(e.g. layouts) may depend on these variables
1536  const QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1537  const QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1538 
1539  mCustomVariables.clear();
1540  if ( variableNames.length() == variableValues.length() )
1541  {
1542  for ( int i = 0; i < variableNames.length(); ++i )
1543  {
1544  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1545  }
1546  }
1547  else
1548  {
1549  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1550  }
1551 
1552  QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectMetadata" ) );
1553 
1554  if ( !element.isNull() )
1555  {
1556  mMetadata.readMetadataXml( element );
1557  }
1558  else
1559  {
1560  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1561  mMetadata = QgsProjectMetadata();
1562  }
1563  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1564  {
1565  // upgrade older title storage to storing within project metadata.
1566  mMetadata.setTitle( oldTitle );
1567  }
1568  emit metadataChanged();
1569 
1570  element = doc->documentElement().firstChildElement( QStringLiteral( "autotransaction" ) );
1571  if ( ! element.isNull() )
1572  {
1573  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1574  mAutoTransaction = true;
1575  }
1576 
1577  element = doc->documentElement().firstChildElement( QStringLiteral( "evaluateDefaultValues" ) );
1578  if ( !element.isNull() )
1579  {
1580  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1581  mEvaluateDefaultValues = true;
1582  }
1583 
1584  // Read trust layer metadata config in the project
1585  element = doc->documentElement().firstChildElement( QStringLiteral( "trust" ) );
1586  if ( !element.isNull() )
1587  {
1588  if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1589  mTrustLayerMetadata = true;
1590  }
1591 
1592  // read the layer tree from project file
1593  profile.switchTask( tr( "Loading layer tree" ) );
1594  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1595 
1596  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1597  if ( !layerTreeElem.isNull() )
1598  {
1599  // Use a temporary tree to read the nodes to prevent signals being delivered to the models
1600  QgsLayerTree tempTree;
1601  tempTree.readChildrenFromXml( layerTreeElem, context );
1602  mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
1603  }
1604  else
1605  {
1606  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1607  }
1608 
1609  mLayerTreeRegistryBridge->setEnabled( false );
1610 
1611  // get the map layers
1612  profile.switchTask( tr( "Reading map layers" ) );
1613 
1614  QList<QDomNode> brokenNodes;
1615  const bool clean = _getMapLayers( *doc, brokenNodes, flags );
1616 
1617  // review the integrity of the retrieved map layers
1618  if ( !clean )
1619  {
1620  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1621 
1622  if ( !brokenNodes.isEmpty() )
1623  {
1624  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1625  }
1626 
1627  // we let a custom handler decide what to do with missing layers
1628  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1629  mBadLayerHandler->handleBadLayers( brokenNodes );
1630  }
1631 
1632  mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
1633  mMainAnnotationLayer->setTransformContext( mTransformContext );
1634 
1635  // load embedded groups and layers
1636  profile.switchTask( tr( "Loading embedded layers" ) );
1637  loadEmbeddedNodes( mRootGroup, flags );
1638 
1639  // Resolve references to other layers
1640  // Needs to be done here once all dependent layers are loaded
1641  profile.switchTask( tr( "Resolving layer references" ) );
1642  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1643  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
1644  {
1645  it.value()->resolveReferences( this );
1646  }
1647 
1648  mLayerTreeRegistryBridge->setEnabled( true );
1649 
1650  // now that layers are loaded, we can resolve layer tree's references to the layers
1651  profile.switchTask( tr( "Resolving references" ) );
1652  mRootGroup->resolveReferences( this );
1653 
1654  if ( !layerTreeElem.isNull() )
1655  {
1656  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1657  }
1658 
1659  // Load pre 3.0 configuration
1660  const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1661  if ( !layerTreeCanvasElem.isNull( ) )
1662  {
1663  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1664  }
1665 
1666  // Convert pre 3.4 to create layers flags
1667  if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
1668  {
1669  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1670  for ( const QString &layerId : requiredLayerIds )
1671  {
1672  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1673  {
1674  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1675  }
1676  }
1677  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1678  for ( const QString &layerId : disabledLayerIds )
1679  {
1680  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1681  {
1682  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1683  }
1684  }
1685  }
1686 
1687  // After bad layer handling we might still have invalid layers,
1688  // store them in case the user wanted to handle them later
1689  // or wanted to pass them through when saving
1691  {
1692  profile.switchTask( tr( "Storing original layer properties" ) );
1693  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1694  }
1695 
1696  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1697 
1698  profile.switchTask( tr( "Loading map themes" ) );
1699  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1701  mMapThemeCollection->readXml( *doc );
1702 
1703  profile.switchTask( tr( "Loading label settings" ) );
1704  mLabelingEngineSettings->readSettingsFromProject( this );
1706 
1707  profile.switchTask( tr( "Loading annotations" ) );
1708  mAnnotationManager->readXml( doc->documentElement(), context );
1709  if ( !( flags & QgsProject::ReadFlag::FlagDontLoadLayouts ) )
1710  {
1711  profile.switchTask( tr( "Loading layouts" ) );
1712  mLayoutManager->readXml( doc->documentElement(), *doc );
1713  }
1714  profile.switchTask( tr( "Loading bookmarks" ) );
1715  mBookmarkManager->readXml( doc->documentElement(), *doc );
1716 
1717  // reassign change dependencies now that all layers are loaded
1718  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1719  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
1720  {
1721  it.value()->setDependencies( it.value()->dependencies() );
1722  }
1723 
1724  profile.switchTask( tr( "Loading snapping settings" ) );
1725  mSnappingConfig.readProject( *doc );
1726  mAvoidIntersectionsMode = static_cast<AvoidIntersectionsMode>( readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
1727 
1728  profile.switchTask( tr( "Loading view settings" ) );
1729  // restore older project scales settings
1730  mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
1731  const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
1732  QVector<double> res;
1733  for ( const QString &scale : scales )
1734  {
1735  const QStringList parts = scale.split( ':' );
1736  if ( parts.size() != 2 )
1737  continue;
1738 
1739  bool ok = false;
1740  const double denominator = QLocale().toDouble( parts[1], &ok );
1741  if ( ok )
1742  {
1743  res << denominator;
1744  }
1745  }
1746  mViewSettings->setMapScales( res );
1747  const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
1748  if ( !viewSettingsElement.isNull() )
1749  mViewSettings->readXml( viewSettingsElement, context );
1750 
1751  // restore time settings
1752  profile.switchTask( tr( "Loading temporal settings" ) );
1753  const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectTimeSettings" ) );
1754  if ( !timeSettingsElement.isNull() )
1755  mTimeSettings->readXml( timeSettingsElement, context );
1756 
1757  profile.switchTask( tr( "Loading display settings" ) );
1758  const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
1759  if ( !displaySettingsElement.isNull() )
1760  mDisplaySettings->readXml( displaySettingsElement, context );
1761 
1762  profile.switchTask( tr( "Updating variables" ) );
1763  emit customVariablesChanged();
1764  profile.switchTask( tr( "Updating CRS" ) );
1765  emit crsChanged();
1766  emit ellipsoidChanged( ellipsoid() );
1767 
1768  // read the project: used by map canvas and legend
1769  profile.switchTask( tr( "Reading external settings" ) );
1770  emit readProject( *doc );
1771  emit readProjectWithContext( *doc, context );
1772 
1773  profile.switchTask( tr( "Updating interface" ) );
1774 
1775  snapSignalBlock.release();
1776  if ( !mBlockSnappingUpdates )
1777  emit snappingConfigChanged( mSnappingConfig );
1778 
1781  emit projectColorsChanged();
1782 
1783  // if all went well, we're allegedly in pristine state
1784  if ( clean )
1785  setDirty( false );
1786 
1787  QgsDebugMsgLevel( QString( "Project save user: %1" ).arg( mSaveUser ), 2 );
1788  QgsDebugMsgLevel( QString( "Project save user: %1" ).arg( mSaveUserFull ), 2 );
1789 
1793 
1794  if ( mTranslator )
1795  {
1796  //project possibly translated -> rename it with locale postfix
1797  const QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
1798  setFileName( newFileName );
1799 
1800  if ( write() )
1801  {
1802  setTitle( localeFileName );
1803  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Success );
1804  }
1805  else
1806  {
1807  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Critical );
1808  }
1809  }
1810  return true;
1811 }
1812 
1813 
1814 bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
1815 {
1816  bool valid = true;
1817  const auto constChildren = group->children();
1818  for ( QgsLayerTreeNode *child : constChildren )
1819  {
1820  if ( QgsLayerTree::isGroup( child ) )
1821  {
1822  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1823  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1824  {
1825  // make sure to convert the path from relative to absolute
1826  const QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1827  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1828  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
1829  if ( newGroup )
1830  {
1831  QList<QgsLayerTreeNode *> clonedChildren;
1832  const auto constChildren = newGroup->children();
1833  for ( QgsLayerTreeNode *newGroupChild : constChildren )
1834  clonedChildren << newGroupChild->clone();
1835  delete newGroup;
1836 
1837  childGroup->insertChildNodes( 0, clonedChildren );
1838  }
1839  }
1840  else
1841  {
1842  loadEmbeddedNodes( childGroup, flags );
1843  }
1844  }
1845  else if ( QgsLayerTree::isLayer( child ) )
1846  {
1847  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1848  {
1849  QList<QDomNode> brokenNodes;
1850  if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
1851  {
1852  valid = valid && false;
1853  }
1854  }
1855  }
1856 
1857  }
1858 
1859  return valid;
1860 }
1861 
1862 QVariantMap QgsProject::customVariables() const
1863 {
1864  return mCustomVariables;
1865 }
1866 
1867 void QgsProject::setCustomVariables( const QVariantMap &variables )
1868 {
1869  if ( variables == mCustomVariables )
1870  return;
1871 
1872  //write variable to project
1873  QStringList variableNames;
1874  QStringList variableValues;
1875 
1876  QVariantMap::const_iterator it = variables.constBegin();
1877  for ( ; it != variables.constEnd(); ++it )
1878  {
1879  variableNames << it.key();
1880  variableValues << it.value().toString();
1881  }
1882 
1883  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1884  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1885 
1886  mCustomVariables = variables;
1887  mProjectScope.reset();
1888 
1889  emit customVariablesChanged();
1890 }
1891 
1893 {
1894  *mLabelingEngineSettings = settings;
1896 }
1897 
1899 {
1900  return *mLabelingEngineSettings;
1901 }
1902 
1904 {
1905  mProjectScope.reset();
1906  return mLayerStore.get();
1907 }
1908 
1910 {
1911  return mLayerStore.get();
1912 }
1913 
1914 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1915 {
1916  QList<QgsVectorLayer *> layers;
1917  const QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1918  const auto constLayerIds = layerIds;
1919  for ( const QString &layerId : constLayerIds )
1920  {
1921  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1922  layers << vlayer;
1923  }
1924  return layers;
1925 }
1926 
1927 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1928 {
1929  QStringList list;
1930  const auto constLayers = layers;
1931  for ( QgsVectorLayer *layer : constLayers )
1932  list << layer->id();
1933  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1935 }
1936 
1938 {
1939  QgsExpressionContext context;
1940 
1943 
1944  return context;
1945 }
1946 
1948 {
1949  // MUCH cheaper to clone than build
1950  if ( mProjectScope )
1951  {
1952  std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
1953  // we can't cache these
1954  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
1955  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
1956  return projectScope.release();
1957  }
1958 
1959  mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
1960 
1961  const QVariantMap vars = customVariables();
1962 
1963  QVariantMap::const_iterator it = vars.constBegin();
1964 
1965  for ( ; it != vars.constEnd(); ++it )
1966  {
1967  mProjectScope->setVariable( it.key(), it.value(), true );
1968  }
1969 
1970  QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
1971  if ( projectPath.isEmpty() )
1972  projectPath = mOriginalPath;
1973  const QString projectFolder = QFileInfo( projectPath ).path();
1974  const QString projectFilename = QFileInfo( projectPath ).fileName();
1975  const QString projectBasename = baseName();
1976 
1977  //add other known project variables
1978  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
1979  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
1980  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
1981  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
1982  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
1983  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
1984  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_last_saved" ), mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
1985  const QgsCoordinateReferenceSystem projectCrs = crs();
1986  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
1987  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
1988  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_description" ), projectCrs.description(), true, true ) );
1989  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
1990  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
1991  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_units" ), QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
1992  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_acronym" ), projectCrs.projectionAcronym(), true ) );
1993  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_ellipsoid" ), projectCrs.ellipsoidAcronym(), true ) );
1994  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_proj4" ), projectCrs.toProj(), true ) );
1995  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_wkt" ), projectCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), true ) );
1996 
1997  // metadata
1998  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
1999  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
2000  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
2001  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
2002 
2003  // keywords
2004  QVariantMap keywords;
2005  const QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2006  for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2007  {
2008  keywords.insert( it.key(), it.value() );
2009  }
2010  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
2011 
2012  // layers
2013  QVariantList layersIds;
2014  QVariantList layers;
2015  const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2016  layersIds.reserve( layersInProject.count() );
2017  layers.reserve( layersInProject.count() );
2018  for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2019  {
2020  layersIds << it.value()->id();
2021  layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( it.value() ) );
2022  }
2023  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_ids" ), layersIds, true ) );
2024  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
2025 
2026  mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
2027 
2029 }
2030 
2031 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
2032 {
2033  const QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2034 
2035  bool tgChanged = false;
2036 
2037  const auto constLayers = layers;
2038  for ( QgsMapLayer *layer : constLayers )
2039  {
2040  if ( layer->isValid() )
2041  {
2042  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2043  if ( vlayer )
2044  {
2045  if ( autoTransaction() )
2046  {
2047  if ( QgsTransaction::supportsTransaction( vlayer ) )
2048  {
2049  const QString connString = QgsTransaction::connectionString( vlayer->source() );
2050  const QString key = vlayer->providerType();
2051 
2052  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
2053 
2054  if ( !tg )
2055  {
2056  tg = new QgsTransactionGroup();
2057  mTransactionGroups.insert( qMakePair( key, connString ), tg );
2058  tgChanged = true;
2059  }
2060  tg->addLayer( vlayer );
2061  }
2062  }
2064  }
2065 
2066  if ( tgChanged )
2067  emit transactionGroupsChanged();
2068 
2069  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
2070 
2071  // check if we have to update connections for layers with dependencies
2072  for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2073  {
2074  const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2075  if ( deps.contains( layer->id() ) )
2076  {
2077  // reconnect to change signals
2078  it.value()->setDependencies( deps );
2079  }
2080  }
2081  }
2082  }
2083 
2084  if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
2085  emit snappingConfigChanged( mSnappingConfig );
2086 }
2087 
2088 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
2089 {
2090  if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
2091  emit snappingConfigChanged( mSnappingConfig );
2092 }
2093 
2094 void QgsProject::cleanTransactionGroups( bool force )
2095 {
2096  bool changed = false;
2097  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2098  {
2099  if ( tg.value()->isEmpty() || force )
2100  {
2101  delete tg.value();
2102  tg = mTransactionGroups.erase( tg );
2103  changed = true;
2104  }
2105  else
2106  {
2107  ++tg;
2108  }
2109  }
2110  if ( changed )
2111  emit transactionGroupsChanged();
2112 }
2113 
2114 bool QgsProject::readLayer( const QDomNode &layerNode )
2115 {
2116  QgsReadWriteContext context;
2117  context.setPathResolver( pathResolver() );
2118  context.setProjectTranslator( this );
2120  QList<QDomNode> brokenNodes;
2121  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
2122  {
2123  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
2124  // added layer for joins
2125  const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
2126  const auto constVectorLayers = vectorLayers;
2127  for ( QgsVectorLayer *layer : constVectorLayers )
2128  {
2129  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
2130  layer->resolveReferences( this );
2131  }
2132 
2133  return true;
2134  }
2135  return false;
2136 }
2137 
2138 bool QgsProject::write( const QString &filename )
2139 {
2140  mFile.setFileName( filename );
2141  mCachedHomePath.clear();
2142  return write();
2143 }
2144 
2146 {
2147  mProjectScope.reset();
2148  if ( QgsProjectStorage *storage = projectStorage() )
2149  {
2150  QgsReadWriteContext context;
2151  // for projects stored in a custom storage, we have to check for the support
2152  // of relative paths since the storage most likely will not be in a file system
2153  const QString storageFilePath { storage->filePath( mFile.fileName() ) };
2154  if ( storageFilePath.isEmpty() )
2155  {
2157  }
2158  context.setPathResolver( pathResolver() );
2159 
2160  const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
2161  const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
2162 
2163  if ( !zip( tmpZipFilename ) )
2164  return false; // zip() already calls setError() when returning false
2165 
2166  QFile tmpZipFile( tmpZipFilename );
2167  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
2168  {
2169  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
2170  return false;
2171  }
2172 
2174  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
2175  {
2176  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
2177  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2178  if ( !messages.isEmpty() )
2179  err += QStringLiteral( "\n\n" ) + messages.last().message();
2180  setError( err );
2181  return false;
2182  }
2183 
2184  tmpZipFile.close();
2185  QFile::remove( tmpZipFilename );
2186 
2187  return true;
2188  }
2189 
2190  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2191  {
2192  return zip( mFile.fileName() );
2193  }
2194  else
2195  {
2196  // write project file even if the auxiliary storage is not correctly
2197  // saved
2198  const bool asOk = saveAuxiliaryStorage();
2199  const bool writeOk = writeProjectFile( mFile.fileName() );
2200  bool attachmentsOk = true;
2201  if ( !mArchive->files().isEmpty() )
2202  {
2203  const QFileInfo finfo( mFile.fileName() );
2204  const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
2205  attachmentsOk = mArchive->zip( attachmentsZip );
2206  }
2207 
2208  // errors raised during writing project file are more important
2209  if ( ( !asOk || !attachmentsOk ) && writeOk )
2210  {
2211  QStringList errorMessage;
2212  if ( !asOk )
2213  {
2214  const QString err = mAuxiliaryStorage->errorString();
2215  errorMessage.append( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2216  }
2217  if ( !attachmentsOk )
2218  {
2219  errorMessage.append( tr( "Unable to save attachments archive" ) );
2220  }
2221  setError( errorMessage.join( "\n" ) );
2222  }
2223 
2224  return asOk && writeOk && attachmentsOk;
2225  }
2226 }
2227 
2228 bool QgsProject::writeProjectFile( const QString &filename )
2229 {
2230  QFile projectFile( filename );
2231  clearError();
2232 
2233  // if we have problems creating or otherwise writing to the project file,
2234  // let's find out up front before we go through all the hand-waving
2235  // necessary to create all the Dom objects
2236  const QFileInfo myFileInfo( projectFile );
2237  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
2238  {
2239  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
2240  .arg( projectFile.fileName() ) );
2241  return false;
2242  }
2243 
2244  QgsReadWriteContext context;
2245  context.setPathResolver( pathResolver() );
2247 
2248  QDomImplementation DomImplementation;
2249  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
2250 
2251  const QDomDocumentType documentType =
2252  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
2253  QStringLiteral( "SYSTEM" ) );
2254  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
2255 
2256  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
2257  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
2258  qgisNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
2259 
2260  if ( !mSettings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
2261  {
2262  const QString newSaveUser = QgsApplication::userLoginName();
2263  const QString newSaveUserFull = QgsApplication::userFullName();
2264  qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2265  qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2266  mSaveUser = newSaveUser;
2267  mSaveUserFull = newSaveUserFull;
2268  mSaveDateTime = QDateTime::currentDateTime();
2269  qgisNode.setAttribute( QStringLiteral( "saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
2270  }
2271  else
2272  {
2273  mSaveUser.clear();
2274  mSaveUserFull.clear();
2275  mSaveDateTime = QDateTime();
2276  }
2277  doc->appendChild( qgisNode );
2278  mSaveVersion = QgsProjectVersion( Qgis::version() );
2279 
2280  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2281  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2282  qgisNode.appendChild( homePathNode );
2283 
2284  // title
2285  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2286  qgisNode.appendChild( titleNode );
2287 
2288  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
2289  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? 1 : 0 );
2290  qgisNode.appendChild( transactionNode );
2291 
2292  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
2293  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? 1 : 0 );
2294  qgisNode.appendChild( evaluateDefaultValuesNode );
2295 
2296  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
2297  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? 1 : 0 );
2298  qgisNode.appendChild( trustNode );
2299 
2300  const QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
2301  titleNode.appendChild( titleText );
2302 
2303  // write project CRS
2304  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
2305  mCrs.writeXml( srsNode, *doc );
2306  qgisNode.appendChild( srsNode );
2307 
2308  // write layer tree - make sure it is without embedded subgroups
2309  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
2311  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
2312 
2313  clonedRoot->writeXml( qgisNode, context );
2314  delete clonedRoot;
2315 
2316  mSnappingConfig.writeProject( *doc );
2317  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( mAvoidIntersectionsMode ) );
2318 
2319  // let map canvas and legend write their information
2320  emit writeProject( *doc );
2321 
2322  // within top level node save list of layers
2323  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2324 
2325  QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
2326  mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
2327  qgisNode.appendChild( annotationLayerNode );
2328 
2329  // Iterate over layers in zOrder
2330  // Call writeXml() on each
2331  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
2332 
2333  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
2334  while ( li != layers.end() )
2335  {
2336  QgsMapLayer *ml = li.value();
2337 
2338  if ( ml )
2339  {
2340  const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
2341  if ( emIt == mEmbeddedLayers.constEnd() )
2342  {
2343  QDomElement maplayerElem;
2344  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
2345  // not available, just write what we DO have
2346  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
2347  {
2348  // general layer metadata
2349  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2350  ml->writeLayerXml( maplayerElem, *doc, context );
2351  }
2352  else if ( ! ml->originalXmlProperties().isEmpty() )
2353  {
2354  QDomDocument document;
2355  if ( document.setContent( ml->originalXmlProperties() ) )
2356  {
2357  maplayerElem = document.firstChildElement();
2358  }
2359  else
2360  {
2361  QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
2362  }
2363  }
2364 
2365  emit writeMapLayer( ml, maplayerElem, *doc );
2366 
2367  projectLayersNode.appendChild( maplayerElem );
2368  }
2369  else
2370  {
2371  // layer defined in an external project file
2372  // only save embedded layer if not managed by a legend group
2373  if ( emIt.value().second )
2374  {
2375  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2376  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
2377  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
2378  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
2379  projectLayersNode.appendChild( mapLayerElem );
2380  }
2381  }
2382  }
2383  li++;
2384  }
2385 
2386  qgisNode.appendChild( projectLayersNode );
2387 
2388  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
2389  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
2390  for ( QgsMapLayer *layer : constCustomLayerOrder )
2391  {
2392  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
2393  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
2394  layerOrderNode.appendChild( mapLayerElem );
2395  }
2396  qgisNode.appendChild( layerOrderNode );
2397 
2398  mLabelingEngineSettings->writeSettingsToProject( this );
2399 
2400  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
2401  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
2402  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
2403 
2404  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
2405  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
2406  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
2407  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
2408 
2409  // now add the optional extra properties
2410 #if 0
2411  dump_( mProperties );
2412 #endif
2413 
2414  QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 2 );
2415 
2416  if ( !mProperties.isEmpty() ) // only worry about properties if we
2417  // actually have any properties
2418  {
2419  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2420  }
2421 
2422  QDomElement ddElem = doc->createElement( QStringLiteral( "dataDefinedServerProperties" ) );
2423  mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
2424  qgisNode.appendChild( ddElem );
2425 
2426  mMapThemeCollection->writeXml( *doc );
2427 
2428  mTransformContext.writeXml( qgisNode, context );
2429 
2430  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2431  mMetadata.writeMetadataXml( metadataElem, *doc );
2432  qgisNode.appendChild( metadataElem );
2433 
2434  const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2435  qgisNode.appendChild( annotationsElem );
2436 
2437  const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2438  qgisNode.appendChild( layoutElem );
2439 
2440  const QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2441  qgisNode.appendChild( bookmarkElem );
2442 
2443  const QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
2444  qgisNode.appendChild( viewSettingsElem );
2445 
2446  const QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
2447  qgisNode.appendChild( timeSettingsElement );
2448 
2449  const QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
2450  qgisNode.appendChild( displaySettingsElem );
2451 
2452  // now wrap it up and ship it to the project file
2453  doc->normalize(); // XXX I'm not entirely sure what this does
2454 
2455  // Create backup file
2456  if ( QFile::exists( fileName() ) )
2457  {
2458  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2459  bool ok = true;
2460  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2461  ok &= projectFile.open( QIODevice::ReadOnly );
2462 
2463  QByteArray ba;
2464  while ( ok && !projectFile.atEnd() )
2465  {
2466  ba = projectFile.read( 10240 );
2467  ok &= backupFile.write( ba ) == ba.size();
2468  }
2469 
2470  projectFile.close();
2471  backupFile.close();
2472 
2473  if ( !ok )
2474  {
2475  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2476  return false;
2477  }
2478 
2479  const QFileInfo fi( fileName() );
2480  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2481  utime( backupFile.fileName().toUtf8().constData(), &tb );
2482  }
2483 
2484  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2485  {
2486  projectFile.close(); // even though we got an error, let's make
2487  // sure it's closed anyway
2488 
2489  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2490  return false;
2491  }
2492 
2493  QTemporaryFile tempFile;
2494  bool ok = tempFile.open();
2495  if ( ok )
2496  {
2497  QTextStream projectFileStream( &tempFile );
2498  doc->save( projectFileStream, 2 ); // save as utf-8
2499  ok &= projectFileStream.pos() > -1;
2500 
2501  ok &= tempFile.seek( 0 );
2502 
2503  QByteArray ba;
2504  while ( ok && !tempFile.atEnd() )
2505  {
2506  ba = tempFile.read( 10240 );
2507  ok &= projectFile.write( ba ) == ba.size();
2508  }
2509 
2510  ok &= projectFile.error() == QFile::NoError;
2511 
2512  projectFile.close();
2513  }
2514 
2515  tempFile.close();
2516 
2517  if ( !ok )
2518  {
2519  setError( tr( "Unable to save to file %1. Your project "
2520  "may be corrupted on disk. Try clearing some space on the volume and "
2521  "check file permissions before pressing save again." )
2522  .arg( projectFile.fileName() ) );
2523  return false;
2524  }
2525 
2526  setDirty( false ); // reset to pristine state
2527 
2528  emit projectSaved();
2529  return true;
2530 }
2531 
2532 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2533 {
2534  bool propertiesModified;
2535  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2536 
2537  if ( propertiesModified )
2538  setDirty( true );
2539 
2540  return success;
2541 }
2542 
2543 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2544 {
2545  bool propertiesModified;
2546  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2547 
2548  if ( propertiesModified )
2549  setDirty( true );
2550 
2551  return success;
2552 }
2553 
2554 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2555 {
2556  bool propertiesModified;
2557  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2558 
2559  if ( propertiesModified )
2560  setDirty( true );
2561 
2562  return success;
2563 }
2564 
2565 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2566 {
2567  bool propertiesModified;
2568  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2569 
2570  if ( propertiesModified )
2571  setDirty( true );
2572 
2573  return success;
2574 }
2575 
2576 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2577 {
2578  bool propertiesModified;
2579  const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2580 
2581  if ( propertiesModified )
2582  setDirty( true );
2583 
2584  return success;
2585 }
2586 
2587 QStringList QgsProject::readListEntry( const QString &scope,
2588  const QString &key,
2589  const QStringList &def,
2590  bool *ok ) const
2591 {
2592  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2593 
2594  QVariant value;
2595 
2596  if ( property )
2597  {
2598  value = property->value();
2599 
2600  const bool valid = QVariant::StringList == value.type();
2601  if ( ok )
2602  *ok = valid;
2603 
2604  if ( valid )
2605  {
2606  return value.toStringList();
2607  }
2608  }
2609  else if ( ok )
2610  *ok = false;
2611 
2612 
2613  return def;
2614 }
2615 
2616 
2617 QString QgsProject::readEntry( const QString &scope,
2618  const QString &key,
2619  const QString &def,
2620  bool *ok ) const
2621 {
2622  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2623 
2624  QVariant value;
2625 
2626  if ( property )
2627  {
2628  value = property->value();
2629 
2630  const bool valid = value.canConvert( QVariant::String );
2631  if ( ok )
2632  *ok = valid;
2633 
2634  if ( valid )
2635  return value.toString();
2636  }
2637  else if ( ok )
2638  *ok = false;
2639 
2640  return def;
2641 }
2642 
2643 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2644  bool *ok ) const
2645 {
2646  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2647 
2648  QVariant value;
2649 
2650  if ( property )
2651  {
2652  value = property->value();
2653  }
2654 
2655  const bool valid = value.canConvert( QVariant::Int );
2656 
2657  if ( ok )
2658  {
2659  *ok = valid;
2660  }
2661 
2662  if ( valid )
2663  {
2664  return value.toInt();
2665  }
2666 
2667  return def;
2668 }
2669 
2670 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2671  double def,
2672  bool *ok ) const
2673 {
2674  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2675  if ( property )
2676  {
2677  const QVariant value = property->value();
2678 
2679  const bool valid = value.canConvert( QVariant::Double );
2680  if ( ok )
2681  *ok = valid;
2682 
2683  if ( valid )
2684  return value.toDouble();
2685  }
2686  else if ( ok )
2687  *ok = false;
2688 
2689  return def;
2690 }
2691 
2692 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
2693  bool *ok ) const
2694 {
2695  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2696 
2697  if ( property )
2698  {
2699  const QVariant value = property->value();
2700 
2701  const bool valid = value.canConvert( QVariant::Bool );
2702  if ( ok )
2703  *ok = valid;
2704 
2705  if ( valid )
2706  return value.toBool();
2707  }
2708  else if ( ok )
2709  *ok = false;
2710 
2711  return def;
2712 }
2713 
2714 bool QgsProject::removeEntry( const QString &scope, const QString &key )
2715 {
2716  if ( findKey_( scope, key, mProperties ) )
2717  {
2718  removeKey_( scope, key, mProperties );
2719  setDirty( true );
2720  }
2721 
2722  return !findKey_( scope, key, mProperties );
2723 }
2724 
2725 
2726 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
2727 {
2728  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2729 
2730  QStringList entries;
2731 
2732  if ( foundProperty )
2733  {
2734  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2735 
2736  if ( propertyKey )
2737  { propertyKey->entryList( entries ); }
2738  }
2739 
2740  return entries;
2741 }
2742 
2743 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
2744 {
2745  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2746 
2747  QStringList entries;
2748 
2749  if ( foundProperty )
2750  {
2751  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2752 
2753  if ( propertyKey )
2754  { propertyKey->subkeyList( entries ); }
2755  }
2756 
2757  return entries;
2758 }
2759 
2761 {
2762  dump_( mProperties );
2763 }
2764 
2766 {
2767  QString filePath;
2768  switch ( filePathStorage() )
2769  {
2771  break;
2772 
2774  {
2775  // for projects stored in a custom storage, we need to ask to the
2776  // storage for the path, if the storage returns an empty path
2777  // relative paths are not supported
2778  if ( QgsProjectStorage *storage = projectStorage() )
2779  {
2780  filePath = storage->filePath( mFile.fileName() );
2781  }
2782  else
2783  {
2784  filePath = fileName();
2785  }
2786  break;
2787  }
2788  }
2789 
2790  return QgsPathResolver( filePath, mArchive->dir() );
2791 }
2792 
2793 QString QgsProject::readPath( const QString &src ) const
2794 {
2795  return pathResolver().readPath( src );
2796 }
2797 
2798 QString QgsProject::writePath( const QString &src ) const
2799 {
2800  return pathResolver().writePath( src );
2801 }
2802 
2803 void QgsProject::setError( const QString &errorMessage )
2804 {
2805  mErrorMessage = errorMessage;
2806 }
2807 
2808 QString QgsProject::error() const
2809 {
2810  return mErrorMessage;
2811 }
2812 
2813 void QgsProject::clearError()
2814 {
2815  setError( QString() );
2816 }
2817 
2819 {
2820  delete mBadLayerHandler;
2821  mBadLayerHandler = handler;
2822 }
2823 
2824 QString QgsProject::layerIsEmbedded( const QString &id ) const
2825 {
2826  const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
2827  if ( it == mEmbeddedLayers.constEnd() )
2828  {
2829  return QString();
2830  }
2831  return it.value().first;
2832 }
2833 
2834 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
2835  bool saveFlag, QgsProject::ReadFlags flags )
2836 {
2837  QgsDebugCall;
2838 
2839  static QString sPrevProjectFilePath;
2840  static QDateTime sPrevProjectFileTimestamp;
2841  static QDomDocument sProjectDocument;
2842 
2843  QString qgsProjectFile = projectFilePath;
2844  QgsProjectArchive archive;
2845  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
2846  {
2847  archive.unzip( projectFilePath );
2848  qgsProjectFile = archive.projectFile();
2849  }
2850 
2851  const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
2852 
2853  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
2854  {
2855  sPrevProjectFilePath.clear();
2856 
2857  QFile projectFile( qgsProjectFile );
2858  if ( !projectFile.open( QIODevice::ReadOnly ) )
2859  {
2860  return false;
2861  }
2862 
2863  if ( !sProjectDocument.setContent( &projectFile ) )
2864  {
2865  return false;
2866  }
2867 
2868  sPrevProjectFilePath = projectFilePath;
2869  sPrevProjectFileTimestamp = projectFileTimestamp;
2870  }
2871 
2872  // does project store paths absolute or relative?
2873  bool useAbsolutePaths = true;
2874 
2875  const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
2876  if ( !propertiesElem.isNull() )
2877  {
2878  const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
2879  if ( !absElem.isNull() )
2880  {
2881  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
2882  }
2883  }
2884 
2885  QgsReadWriteContext embeddedContext;
2886  if ( !useAbsolutePaths )
2887  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
2888  embeddedContext.setProjectTranslator( this );
2889  embeddedContext.setTransformContext( transformContext() );
2890 
2891  const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
2892  if ( projectLayersElem.isNull() )
2893  {
2894  return false;
2895  }
2896 
2897  QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral( "maplayer" ) );
2898  while ( ! mapLayerElem.isNull() )
2899  {
2900  // get layer id
2901  const QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
2902  if ( id == layerId )
2903  {
2904  // layer can be embedded only once
2905  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
2906  {
2907  return false;
2908  }
2909 
2910  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
2911 
2912  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
2913  {
2914  return true;
2915  }
2916  else
2917  {
2918  mEmbeddedLayers.remove( layerId );
2919  return false;
2920  }
2921  }
2922  mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
2923  }
2924 
2925  return false;
2926 }
2927 
2928 
2929 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags )
2930 {
2931  QString qgsProjectFile = projectFilePath;
2932  QgsProjectArchive archive;
2933  if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
2934  {
2935  archive.unzip( projectFilePath );
2936  qgsProjectFile = archive.projectFile();
2937  }
2938 
2939  // open project file, get layer ids in group, add the layers
2940  QFile projectFile( qgsProjectFile );
2941  if ( !projectFile.open( QIODevice::ReadOnly ) )
2942  {
2943  return nullptr;
2944  }
2945 
2946  QDomDocument projectDocument;
2947  if ( !projectDocument.setContent( &projectFile ) )
2948  {
2949  return nullptr;
2950  }
2951 
2952  QgsReadWriteContext context;
2953  context.setPathResolver( pathResolver() );
2954  context.setProjectTranslator( this );
2956 
2958 
2959  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2960  if ( !layerTreeElem.isNull() )
2961  {
2962  root->readChildrenFromXml( layerTreeElem, context );
2963  }
2964  else
2965  {
2966  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2967  }
2968 
2969  QgsLayerTreeGroup *group = root->findGroup( groupName );
2970  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
2971  {
2972  // embedded groups cannot be embedded again
2973  delete root;
2974  return nullptr;
2975  }
2976 
2977  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
2978  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
2979  delete root;
2980  root = nullptr;
2981 
2982  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2983  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2984 
2985  // set "embedded" to all children + load embedded layers
2986  mLayerTreeRegistryBridge->setEnabled( false );
2987  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
2988  mLayerTreeRegistryBridge->setEnabled( true );
2989 
2990  // consider the layers might be identify disabled in its project
2991  const auto constFindLayerIds = newGroup->findLayerIds();
2992  for ( const QString &layerId : constFindLayerIds )
2993  {
2994  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2995  if ( layer )
2996  {
2997  layer->resolveReferences( this );
2998  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
2999  }
3000  }
3001 
3002  return newGroup;
3003 }
3004 
3005 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
3006 {
3007  const auto constChildren = group->children();
3008  for ( QgsLayerTreeNode *child : constChildren )
3009  {
3010  // all nodes in the subtree will have "embedded" custom property set
3011  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3012 
3013  if ( QgsLayerTree::isGroup( child ) )
3014  {
3015  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
3016  }
3017  else if ( QgsLayerTree::isLayer( child ) )
3018  {
3019  // load the layer into our project
3020  QList<QDomNode> brokenNodes;
3021  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
3022  }
3023  }
3024 }
3025 
3027 {
3028  return mEvaluateDefaultValues;
3029 }
3030 
3031 void QgsProject::setEvaluateDefaultValues( bool evaluateDefaultValues )
3032 {
3033  if ( evaluateDefaultValues == mEvaluateDefaultValues )
3034  return;
3035 
3036  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3037  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
3038  for ( ; layerIt != layers.constEnd(); ++layerIt )
3039  {
3040  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
3041  if ( vl )
3042  {
3044  }
3045  }
3046 
3047  mEvaluateDefaultValues = evaluateDefaultValues;
3048 }
3049 
3051 {
3052  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
3054 }
3055 
3057 {
3058  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
3059 }
3060 
3062 {
3063  const QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
3064  if ( !distanceUnitString.isEmpty() )
3065  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
3066 
3067  //fallback to QGIS default measurement unit
3068  bool ok = false;
3069  const QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
3070  return ok ? type : QgsUnitTypes::DistanceMeters;
3071 }
3072 
3074 {
3075  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
3076 }
3077 
3079 {
3080  const QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
3081  if ( !areaUnitString.isEmpty() )
3082  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
3083 
3084  //fallback to QGIS default area unit
3085  bool ok = false;
3086  const QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
3087  return ok ? type : QgsUnitTypes::AreaSquareMeters;
3088 }
3089 
3091 {
3092  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
3093 }
3094 
3095 QString QgsProject::homePath() const
3096 {
3097  if ( !mCachedHomePath.isEmpty() )
3098  return mCachedHomePath;
3099 
3100  const QFileInfo pfi( fileName() );
3101 
3102  if ( !mHomePath.isEmpty() )
3103  {
3104  const QFileInfo homeInfo( mHomePath );
3105  if ( !homeInfo.isRelative() )
3106  {
3107  mCachedHomePath = mHomePath;
3108  return mHomePath;
3109  }
3110  }
3111  else if ( !fileName().isEmpty() )
3112  {
3113  mCachedHomePath = pfi.path();
3114 
3115  return mCachedHomePath;
3116  }
3117 
3118  if ( !pfi.exists() )
3119  {
3120  mCachedHomePath = mHomePath;
3121  return mHomePath;
3122  }
3123 
3124  if ( !mHomePath.isEmpty() )
3125  {
3126  // path is relative to project file
3127  mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
3128  }
3129  else
3130  {
3131  mCachedHomePath = pfi.canonicalPath();
3132  }
3133  return mCachedHomePath;
3134 }
3135 
3137 {
3138  return mHomePath;
3139 }
3140 
3142 {
3143  return mRelationManager;
3144 }
3145 
3147 {
3148  return mLayoutManager.get();
3149 }
3150 
3152 {
3153  return mLayoutManager.get();
3154 }
3155 
3157 {
3158  return mBookmarkManager;
3159 }
3160 
3162 {
3163  return mBookmarkManager;
3164 }
3165 
3167 {
3168  return mViewSettings;
3169 }
3170 
3172 {
3173  return mViewSettings;
3174 }
3175 
3177 {
3178  return mTimeSettings;
3179 }
3180 
3182 {
3183  return mTimeSettings;
3184 }
3185 
3187 {
3188  return mDisplaySettings;
3189 }
3190 
3192 {
3193  return mDisplaySettings;
3194 }
3195 
3197 {
3198  return mRootGroup;
3199 }
3200 
3202 {
3203  return mMapThemeCollection.get();
3204 }
3205 
3207 {
3208  return mAnnotationManager.get();
3209 }
3210 
3212 {
3213  return mAnnotationManager.get();
3214 }
3215 
3216 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
3217 {
3218  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3219  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3220  {
3221  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3222  continue;
3223 
3224  if ( layers.contains( it.value() ) )
3225  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
3226  else
3227  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
3228  }
3229 
3233 }
3234 
3235 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
3236 {
3237  QList<QgsMapLayer *> nonIdentifiableLayers;
3238  nonIdentifiableLayers.reserve( layerIds.count() );
3239  for ( const QString &layerId : layerIds )
3240  {
3241  QgsMapLayer *layer = mapLayer( layerId );
3242  if ( layer )
3243  nonIdentifiableLayers << layer;
3244  }
3248 }
3249 
3251 {
3252  QStringList nonIdentifiableLayers;
3253 
3254  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3255  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3256  {
3257  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3258  {
3259  nonIdentifiableLayers.append( it.value()->id() );
3260  }
3261  }
3262  return nonIdentifiableLayers;
3263 }
3264 
3266 {
3267  return mAutoTransaction;
3268 }
3269 
3270 void QgsProject::setAutoTransaction( bool autoTransaction )
3271 {
3272  if ( autoTransaction != mAutoTransaction )
3273  {
3274  mAutoTransaction = autoTransaction;
3275 
3276  if ( autoTransaction )
3277  onMapLayersAdded( mapLayers().values() );
3278  else
3279  cleanTransactionGroups( true );
3280  }
3281 }
3282 
3283 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
3284 {
3285  return mTransactionGroups;
3286 }
3287 
3288 
3289 //
3290 // QgsMapLayerStore methods
3291 //
3292 
3293 
3295 {
3296  return mLayerStore->count();
3297 }
3298 
3300 {
3301  return mLayerStore->validCount();
3302 }
3303 
3304 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
3305 {
3306  return mLayerStore->mapLayer( layerId );
3307 }
3308 
3309 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
3310 {
3311  return mLayerStore->mapLayersByName( layerName );
3312 }
3313 
3314 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
3315 {
3316  QList<QgsMapLayer *> layers;
3317  const auto constMapLayers { mLayerStore->mapLayers() };
3318  for ( const auto &l : constMapLayers )
3319  {
3320  if ( ! l->shortName().isEmpty() )
3321  {
3322  if ( l->shortName() == shortName )
3323  layers << l;
3324  }
3325  else if ( l->name() == shortName )
3326  {
3327  layers << l;
3328  }
3329  }
3330  return layers;
3331 }
3332 
3333 bool QgsProject::unzip( const QString &filename, QgsProject::ReadFlags flags )
3334 {
3335  clearError();
3336  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3337 
3338  // unzip the archive
3339  if ( !archive->unzip( filename ) )
3340  {
3341  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
3342  return false;
3343  }
3344 
3345  // test if zip provides a .qgs file
3346  if ( archive->projectFile().isEmpty() )
3347  {
3348  setError( tr( "Zip archive does not provide a project file" ) );
3349  return false;
3350  }
3351 
3352  // Keep the archive
3353  mArchive = std::move( archive );
3354 
3355  // load auxiliary storage
3356  if ( !static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile().isEmpty() )
3357  {
3358  // database file is already a copy as it's been unzipped. So we don't open
3359  // auxiliary storage in copy mode in this case
3360  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile(), false ) );
3361  }
3362  else
3363  {
3364  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
3365  }
3366 
3367  // read the project file
3368  if ( ! readProjectFile( static_cast<QgsProjectArchive *>( mArchive.get() )->projectFile(), flags ) )
3369  {
3370  setError( tr( "Cannot read unzipped qgs project file" ) );
3371  return false;
3372  }
3373 
3374  // Remove the temporary .qgs file
3375  static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3376 
3377  return true;
3378 }
3379 
3380 bool QgsProject::zip( const QString &filename )
3381 {
3382  clearError();
3383 
3384  // save the current project in a temporary .qgs file
3385  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3386  const QString baseName = QFileInfo( filename ).baseName();
3387  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
3388  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
3389 
3390  bool writeOk = false;
3391  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3392  {
3393  writeOk = writeProjectFile( qgsFile.fileName() );
3394  qgsFile.close();
3395  }
3396 
3397  // stop here with an error message
3398  if ( ! writeOk )
3399  {
3400  setError( tr( "Unable to write temporary qgs file" ) );
3401  return false;
3402  }
3403 
3404  // save auxiliary storage
3405  const QFileInfo info( qgsFile );
3406  const QString asExt = QStringLiteral( ".%1" ).arg( QgsAuxiliaryStorage::extension() );
3407  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
3408 
3409  bool auxiliaryStorageSavedOk = true;
3410  if ( ! saveAuxiliaryStorage( asFileName ) )
3411  {
3412  const QString err = mAuxiliaryStorage->errorString();
3413  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 ) );
3414  auxiliaryStorageSavedOk = false;
3415 
3416  // fixes the current archive and keep the previous version of qgd
3417  if ( !mArchive->exists() )
3418  {
3419  mArchive.reset( new QgsProjectArchive() );
3420  mArchive->unzip( mFile.fileName() );
3421  static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3422 
3423  const QString auxiliaryStorageFile = static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile();
3424  if ( ! auxiliaryStorageFile.isEmpty() )
3425  {
3426  archive->addFile( auxiliaryStorageFile );
3427  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( auxiliaryStorageFile, false ) );
3428  }
3429  }
3430  }
3431  else
3432  {
3433  // in this case, an empty filename means that the auxiliary database is
3434  // empty, so we don't want to save it
3435  if ( QFile::exists( asFileName ) )
3436  {
3437  archive->addFile( asFileName );
3438  }
3439  }
3440 
3441  // create the archive
3442  archive->addFile( qgsFile.fileName() );
3443 
3444  // Add all other files
3445  const QStringList &files = mArchive->files();
3446  for ( const QString &file : files )
3447  {
3448  if ( !file.endsWith( ".qgs", Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
3449  {
3450  archive->addFile( file );
3451  }
3452  }
3453 
3454  // zip
3455  bool zipOk = true;
3456  if ( !archive->zip( filename ) )
3457  {
3458  setError( tr( "Unable to perform zip" ) );
3459  zipOk = false;
3460  }
3461 
3462  return auxiliaryStorageSavedOk && zipOk;
3463 }
3464 
3466 {
3467  return QgsZipUtils::isZipFile( mFile.fileName() );
3468 }
3469 
3470 QList<QgsMapLayer *> QgsProject::addMapLayers(
3471  const QList<QgsMapLayer *> &layers,
3472  bool addToLegend,
3473  bool takeOwnership )
3474 {
3475  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
3476  if ( !myResultList.isEmpty() )
3477  {
3478  // Update transform context
3479  for ( auto &l : myResultList )
3480  {
3481  l->setTransformContext( transformContext() );
3482  }
3483  if ( addToLegend )
3484  {
3485  emit legendLayersAdded( myResultList );
3486  }
3487  }
3488 
3489  if ( mAuxiliaryStorage )
3490  {
3491  for ( QgsMapLayer *mlayer : myResultList )
3492  {
3493  if ( mlayer->type() != QgsMapLayerType::VectorLayer )
3494  continue;
3495 
3496  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
3497  if ( vl )
3498  {
3499  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
3500  }
3501  }
3502  }
3503 
3504  mProjectScope.reset();
3505 
3506  return myResultList;
3507 }
3508 
3509 QgsMapLayer *
3511  bool addToLegend,
3512  bool takeOwnership )
3513 {
3514  QList<QgsMapLayer *> addedLayers;
3515  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
3516  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
3517 }
3518 
3519 void QgsProject::removeMapLayers( const QStringList &layerIds )
3520 {
3521  mProjectScope.reset();
3522  mLayerStore->removeMapLayers( layerIds );
3523 }
3524 
3525 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3526 {
3527  mProjectScope.reset();
3528  mLayerStore->removeMapLayers( layers );
3529 }
3530 
3531 void QgsProject::removeMapLayer( const QString &layerId )
3532 {
3533  mProjectScope.reset();
3534  mLayerStore->removeMapLayer( layerId );
3535 }
3536 
3538 {
3539  mProjectScope.reset();
3540  mLayerStore->removeMapLayer( layer );
3541 }
3542 
3544 {
3545  mProjectScope.reset();
3546  return mLayerStore->takeMapLayer( layer );
3547 }
3548 
3550 {
3551  return mMainAnnotationLayer;
3552 }
3553 
3555 {
3556  if ( mLayerStore->count() == 0 )
3557  return;
3558 
3559  ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
3560  mProjectScope.reset();
3561  mLayerStore->removeAllMapLayers();
3562 
3563  snapSingleBlocker.release();
3564  mSnappingConfig.clearIndividualLayerSettings();
3565  if ( !mBlockSnappingUpdates )
3566  emit snappingConfigChanged( mSnappingConfig );
3567 }
3568 
3570 {
3571  const QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3572  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3573  for ( ; it != layers.constEnd(); ++it )
3574  {
3575  it.value()->reload();
3576  }
3577 }
3578 
3579 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3580 {
3581  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3582 }
3583 
3584 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3585 {
3586  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3587 }
3588 
3590 {
3591  QgsCoordinateReferenceSystem defaultCrs;
3592 
3593  // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
3594  // 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)
3595  if ( mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
3596  || mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == QLatin1String( "2" ) )
3597  {
3598  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
3599  defaultCrs = crs();
3600  }
3601  else
3602  {
3603  // global crs
3604  const QString layerDefaultCrs = mSettings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
3605  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
3606  }
3607 
3608  return defaultCrs;
3609 }
3610 
3612 {
3613  mTrustLayerMetadata = trust;
3614 
3615  const auto layers = mapLayers();
3616  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3617  {
3618  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3619  if ( vl )
3620  {
3621  vl->setReadExtentFromXml( trust );
3622  }
3623  }
3624 }
3625 
3626 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
3627 {
3628  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3629  bool empty = true;
3630  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3631  {
3632  if ( it.value()->type() != QgsMapLayerType::VectorLayer )
3633  continue;
3634 
3635  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3636  if ( vl && vl->auxiliaryLayer() )
3637  {
3638  vl->auxiliaryLayer()->save();
3639  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
3640  }
3641  }
3642 
3643  if ( !mAuxiliaryStorage->exists( *this ) && empty )
3644  {
3645  return true; // it's not an error
3646  }
3647  else if ( !filename.isEmpty() )
3648  {
3649  return mAuxiliaryStorage->saveAs( filename );
3650  }
3651  else
3652  {
3653  return mAuxiliaryStorage->saveAs( *this );
3654  }
3655 }
3656 
3657 QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
3658 {
3659  static QgsPropertiesDefinition sPropertyDefinitions
3660  {
3661  {
3662  QgsProject::DataDefinedServerProperty::WMSOnlineResource,
3663  QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
3664  },
3665  };
3666  return sPropertyDefinitions;
3667 }
3668 
3670 {
3671  return mAuxiliaryStorage.get();
3672 }
3673 
3675 {
3676  return mAuxiliaryStorage.get();
3677 }
3678 
3679 QString QgsProject::createAttachedFile( const QString &nameTemplate )
3680 {
3681  const QString fileName = nameTemplate;
3682  const QDir archiveDir( mArchive->dir() );
3683  QTemporaryFile tmpFile( archiveDir.filePath( "XXXXXX_" + nameTemplate ), this );
3684  tmpFile.setAutoRemove( false );
3685  tmpFile.open();
3686  mArchive->addFile( tmpFile.fileName() );
3687  return tmpFile.fileName();
3688 }
3689 
3690 QStringList QgsProject::attachedFiles() const
3691 {
3692  QStringList attachments;
3693  const QString baseName = QFileInfo( fileName() ).baseName();
3694  for ( const QString &file : mArchive->files() )
3695  {
3696  if ( QFileInfo( file ).baseName() != baseName )
3697  {
3698  attachments.append( file );
3699  }
3700  }
3701  return attachments;
3702 }
3703 
3704 bool QgsProject::removeAttachedFile( const QString &path )
3705 {
3706  return mArchive->removeFile( path );
3707 }
3708 
3709 QString QgsProject::attachmentIdentifier( const QString &attachedFile ) const
3710 {
3711  return QStringLiteral( "attachment:///%1" ).arg( QFileInfo( attachedFile ).fileName() );
3712 }
3713 
3714 QString QgsProject::resolveAttachmentIdentifier( const QString &identifier ) const
3715 {
3716  if ( identifier.startsWith( "attachment:///" ) )
3717  {
3718  return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
3719  }
3720  return QString();
3721 }
3722 
3724 {
3725  return mMetadata;
3726 }
3727 
3729 {
3730  if ( metadata == mMetadata )
3731  return;
3732 
3733  mMetadata = metadata;
3734  mProjectScope.reset();
3735 
3736  emit metadataChanged();
3737 
3738  setDirty( true );
3739 }
3740 
3741 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
3742 {
3743  QSet<QgsMapLayer *> requiredLayers;
3744 
3745  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3746  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3747  {
3748  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3749  {
3750  requiredLayers.insert( it.value() );
3751  }
3752  }
3753  return requiredLayers;
3754 }
3755 
3756 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
3757 {
3758  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3759  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3760  {
3761  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3762  continue;
3763 
3764  if ( layers.contains( it.value() ) )
3765  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
3766  else
3767  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
3768  }
3769 }
3770 
3772 {
3773  // save colors to project
3774  QStringList customColors;
3775  QStringList customColorLabels;
3776 
3777  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
3778  for ( ; colorIt != colors.constEnd(); ++colorIt )
3779  {
3780  const QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
3781  const QString label = ( *colorIt ).second;
3782  customColors.append( color );
3783  customColorLabels.append( label );
3784  }
3785  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
3786  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
3787  mProjectScope.reset();
3788  emit projectColorsChanged();
3789 }
3790 
3791 void QgsProject::setBackgroundColor( const QColor &color )
3792 {
3793  if ( mBackgroundColor == color )
3794  return;
3795 
3796  mBackgroundColor = color;
3797  emit backgroundColorChanged();
3798 }
3799 
3801 {
3802  return mBackgroundColor;
3803 }
3804 
3805 void QgsProject::setSelectionColor( const QColor &color )
3806 {
3807  if ( mSelectionColor == color )
3808  return;
3809 
3810  mSelectionColor = color;
3811  emit selectionColorChanged();
3812 }
3813 
3815 {
3816  return mSelectionColor;
3817 }
3818 
3819 void QgsProject::setMapScales( const QVector<double> &scales )
3820 {
3821  mViewSettings->setMapScales( scales );
3822 }
3823 
3824 QVector<double> QgsProject::mapScales() const
3825 {
3826  return mViewSettings->mapScales();
3827 }
3828 
3830 {
3831  mViewSettings->setUseProjectScales( enabled );
3832 }
3833 
3835 {
3836  return mViewSettings->useProjectScales();
3837 }
3838 
3839 void QgsProject::generateTsFile( const QString &locale )
3840 {
3841  QgsTranslationContext translationContext;
3842  translationContext.setProject( this );
3843  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
3844 
3845  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
3846 
3847  translationContext.writeTsFile( locale );
3848 }
3849 
3850 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
3851 {
3852  if ( !mTranslator )
3853  {
3854  return sourceText;
3855  }
3856 
3857  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
3858 
3859  if ( result.isEmpty() )
3860  {
3861  return sourceText;
3862  }
3863  return result;
3864 }
3865 
3867 {
3868  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
3869  if ( !layers.empty() )
3870  {
3871  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3872  {
3873  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
3874  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3875  {
3876  if ( !( ( *it )->accept( visitor ) ) )
3877  return false;
3878 
3879  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3880  return false;
3881  }
3882  }
3883  }
3884 
3885  if ( !mLayoutManager->accept( visitor ) )
3886  return false;
3887 
3888  if ( !mAnnotationManager->accept( visitor ) )
3889  return false;
3890 
3891  return true;
3892 }
3893 
3895 GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
3896  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3897 {
3898  if ( !project )
3899  return;
3900 
3901  //build up color list from project. Do this in advance for speed
3902  QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
3903  const QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
3904 
3905  //generate list from custom colors
3906  int colorIndex = 0;
3907  for ( QStringList::iterator it = colorStrings.begin();
3908  it != colorStrings.end(); ++it )
3909  {
3910  const QColor color = QgsSymbolLayerUtils::decodeColor( *it );
3911  QString label;
3912  if ( colorLabels.length() > colorIndex )
3913  {
3914  label = colorLabels.at( colorIndex );
3915  }
3916 
3917  mColors.insert( label.toLower(), color );
3918  colorIndex++;
3919  }
3920 }
3921 
3922 GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
3923  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3924  , mColors( colors )
3925 {
3926 }
3927 
3928 QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3929 {
3930  const QString colorName = values.at( 0 ).toString().toLower();
3931  if ( mColors.contains( colorName ) )
3932  {
3933  return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
3934  }
3935  else
3936  return QVariant();
3937 }
3938 
3939 QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
3940 {
3941  return new GetNamedProjectColor( mColors );
3942 }
static QString version()
Version string.
Definition: qgis.cpp:285
FilePathType
File path types.
Definition: qgis.h:519
@ Relative
Relative path.
@ Absolute
Absolute path.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
virtual bool writeXml(QDomElement &collectionElem, const QgsPropertiesDefinition &definitions) const
Writes the current state of the property collection into an XML element.
Represents a map layer containing a set of georeferenced annotations, e.g.
void setTransformContext(const QgsCoordinateTransformContext &context) override
Sets the coordinate transform context to transformContext.
void reset()
Resets the annotation layer to a default state, and clears all items from it.
Manages storage of a set of QgsAnnotation annotation objects.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ....
static QString userFullName()
Returns the user's operating system login account full display name.
static QString userLoginName()
Returns the user's operating system login account name.
static const QgsSettingsEntryString settingsLocaleUserLocale
Settings entry locale user locale.
Class allowing to manage the zip/unzip actions.
Definition: qgsarchive.h:36
void addFile(const QString &filename)
Add a new file to this archive.
Definition: qgsarchive.cpp:107
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
This is an abstract base class for any elements of a drag and drop form.
QString name() const
Returns the name of this element.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
bool save()
Commits changes and starts editing then.
Class providing some utility methods to manage auxiliary storage.
static QString extension()
Returns the extension used for auxiliary databases.
Manages storage of a set of bookmarks.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all bookmarks present in the XML document.
void clear()
Removes and deletes all bookmarks from the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString 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 ...
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
QVariantMap config() const
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
An expression node for expression functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QString name
Definition: qgsfield.h:60
QString alias
Definition: qgsfield.h:61
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:572
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
Stores global configuration for labeling engine.
Class used to work with layer dependencies stored in a XML project or layer definition file.
Layer tree group node serves as a container for layers and further groups.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QList< QgsLayerTreeGroup * > findGroups(bool recursive=false) const
Find group layer nodes.
QString name() const override
Returns the group's name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already)
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the childrens, disconnect all the forwarded and external signals and sets their parent to nul...
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer. Properties are stored in a map and saved in project file.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Listens to the updates in map layer registry and does changes in layer tree.
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
static void storeOriginalLayersProperties(QgsLayerTreeGroup *group, const QDomDocument *doc)
Stores in a layer's originalXmlProperties the layer properties information.
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *project)
Updates an embedded group from a project.
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:33
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Manages storage of a set of layouts.
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
static QgsMapLayerType typeFromString(const QString &string, bool &ok)
Returns the map layer type corresponding a string value.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the store.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the store.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the store.
void allLayersRemoved()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the store.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the store.
QgsMapLayer * mapLayer(const QString &id) const
Retrieve a pointer to a layer by layer id.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the store.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
void configChanged()
Emitted whenever the configuration is changed.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
bool isValid
Definition: qgsmaplayer.h:81
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:142
@ Removable
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
Definition: qgsmaplayer.h:143
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:638
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:637
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
Container class that allows storage of map themes consisting of visible map layers and layer styles.
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:807
~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:776
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:791
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.
QString value(const QString &dynamicKeyPart=QString(), bool useDefaultValueOverride=false, const QString &defaultValueOverride=QString()) const
Returns settings value.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
This is a container for configuration of the snapping of the project.
bool addLayers(const QList< QgsMapLayer * > &layers)
Adds the specified layers as individual layers to the configuration with standard configuration.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
void reset()
reset to default values
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
void clearIndividualLayerSettings()
Removes all individual layer snapping settings.
bool removeLayers(const QList< QgsMapLayer * > &layers)
Removes the specified layers from the individual layer configuration.
An interface for classes which can visit style entity (e.g.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
bool isEmpty() const
Returns true if there are no layers in this transaction group.
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
Used for the collecting of strings from projects for translation and creation of ts files.
void registerTranslation(const QString &context, const QString &source)
Registers the source to be translated.
void writeTsFile(const QString &locale)
Writes the Ts-file.
void setFileName(const QString &fileName)
Sets the fileName of the TS file.
void setProject(QgsProject *project)
Sets the project being translated.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE QgsUnitTypes::AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
AreaUnit
Units of area.
Definition: qgsunittypes.h:94
@ AreaSquareMeters
Square meters.
Definition: qgsunittypes.h:95
Represents a vector layer which manages a vector based data sets.
bool loadAuxiliaryLayer(const QgsAuxiliaryStorage &storage, const QString &key=QString())
Loads the auxiliary layer for this vector layer.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
void setReadExtentFromXml(bool readExtentFromXml)
Flag allowing to indicate if the extent has to be read from the XML document when data source has no ...
QgsEditFormConfig editFormConfig
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ PointCloudLayer
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:1660
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:1742
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:1741
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:1654
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:2130
void _getProperties(const QDomDocument &doc, QgsProjectPropertyKey &project_properties)
Restores any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:942
QgsPropertyCollection getDataDefinedServerProperties(const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions)
Returns the data defined server properties collection found in "doc" to "dataDefinedServerProperties"...
Definition: qgsproject.cpp:971
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:908
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.