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