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