QGIS API Documentation  3.9.0-Master (224899f119)
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 "qgspluginlayer.h"
28 #include "qgspluginlayerregistry.h"
30 #include "qgssnappingconfig.h"
31 #include "qgspathresolver.h"
32 #include "qgsprojectstorage.h"
34 #include "qgsprojectversion.h"
35 #include "qgsrasterlayer.h"
36 #include "qgsreadwritecontext.h"
37 #include "qgsrectangle.h"
38 #include "qgsrelationmanager.h"
39 #include "qgsannotationmanager.h"
40 #include "qgsvectorlayerjoininfo.h"
41 #include "qgsmapthemecollection.h"
42 #include "qgslayerdefinition.h"
43 #include "qgsunittypes.h"
44 #include "qgstransaction.h"
45 #include "qgstransactiongroup.h"
46 #include "qgsvectordataprovider.h"
48 #include "qgssettings.h"
49 #include "qgsmaplayerlistutils.h"
50 #include "qgsmeshlayer.h"
51 #include "qgslayoutmanager.h"
52 #include "qgsbookmarkmanager.h"
53 #include "qgsmaplayerstore.h"
54 #include "qgsziputils.h"
55 #include "qgsauxiliarystorage.h"
56 #include "qgssymbollayerutils.h"
57 #include "qgsapplication.h"
59 #include "qgsstyleentityvisitor.h"
60 
61 #include <QApplication>
62 #include <QFileInfo>
63 #include <QDomNode>
64 #include <QObject>
65 #include <QTextStream>
66 #include <QTemporaryFile>
67 #include <QDir>
68 #include <QUrl>
69 
70 
71 #ifdef _MSC_VER
72 #include <sys/utime.h>
73 #else
74 #include <utime.h>
75 #endif
76 
77 // canonical project instance
78 QgsProject *QgsProject::sProject = nullptr;
79 
88 QStringList makeKeyTokens_( const QString &scope, const QString &key )
89 {
90  QStringList keyTokens = QStringList( scope );
91  keyTokens += key.split( '/', QString::SkipEmptyParts );
92 
93  // be sure to include the canonical root node
94  keyTokens.push_front( QStringLiteral( "properties" ) );
95 
96  //check validy of keys since an unvalid xml name will will be dropped upon saving the xml file. If not valid, we print a message to the console.
97  for ( int i = 0; i < keyTokens.size(); ++i )
98  {
99  QString keyToken = keyTokens.at( i );
100 
101  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
102  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
103  QString nameCharRegexp = QStringLiteral( "[^:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x2FF\\x370-\\x37D\\x37F-\\x1FFF\\x200C-\\x200D\\x2070-\\x218F\\x2C00-\\x2FEF\\x3001-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFFD\\-\\.0-9\\xB7\\x0300-\\x036F\\x203F-\\x2040]" );
104  QString nameStartCharRegexp = QStringLiteral( "^[^:A-Z_a-z\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\x2FF\\x370-\\x37D\\x37F-\\x1FFF\\x200C-\\x200D\\x2070-\\x218F\\x2C00-\\x2FEF\\x3001-\\xD7FF\\xF900-\\xFDCF\\xFDF0-\\xFFFD]" );
105 
106  if ( keyToken.contains( QRegExp( nameCharRegexp ) ) || keyToken.contains( QRegExp( nameStartCharRegexp ) ) )
107  {
108 
109  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
110  QgsMessageLog::logMessage( errorString, QString(), Qgis::Critical );
111 
112  }
113 
114  }
115 
116  return keyTokens;
117 }
118 
119 
120 
130 QgsProjectProperty *findKey_( const QString &scope,
131  const QString &key,
132  QgsProjectPropertyKey &rootProperty )
133 {
134  QgsProjectPropertyKey *currentProperty = &rootProperty;
135  QgsProjectProperty *nextProperty; // link to next property down hierarchy
136 
137  QStringList keySequence = makeKeyTokens_( scope, key );
138 
139  while ( !keySequence.isEmpty() )
140  {
141  // if the current head of the sequence list matches the property name,
142  // then traverse down the property hierarchy
143  if ( keySequence.first() == currentProperty->name() )
144  {
145  // remove front key since we're traversing down a level
146  keySequence.pop_front();
147 
148  if ( 1 == keySequence.count() )
149  {
150  // if we have only one key name left, then return the key found
151  return currentProperty->find( keySequence.front() );
152  }
153  else if ( keySequence.isEmpty() )
154  {
155  // if we're out of keys then the current property is the one we
156  // want; i.e., we're in the rate case of being at the top-most
157  // property node
158  return currentProperty;
159  }
160  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
161  {
162  if ( nextProperty->isKey() )
163  {
164  currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
165  }
166  else if ( nextProperty->isValue() && 1 == keySequence.count() )
167  {
168  // it may be that this may be one of several property value
169  // nodes keyed by QDict string; if this is the last remaining
170  // key token and the next property is a value node, then
171  // that's the situation, so return the currentProperty
172  return currentProperty;
173  }
174  else
175  {
176  // QgsProjectPropertyValue not Key, so return null
177  return nullptr;
178  }
179  }
180  else
181  {
182  // if the next key down isn't found
183  // then the overall key sequence doesn't exist
184  return nullptr;
185  }
186  }
187  else
188  {
189  return nullptr;
190  }
191  }
192 
193  return nullptr;
194 }
195 
196 
197 
207 QgsProjectProperty *addKey_( const QString &scope,
208  const QString &key,
209  QgsProjectPropertyKey *rootProperty,
210  const QVariant &value,
211  bool &propertiesModified )
212 {
213  QStringList keySequence = makeKeyTokens_( scope, key );
214 
215  // cursor through property key/value hierarchy
216  QgsProjectPropertyKey *currentProperty = rootProperty;
217  QgsProjectProperty *nextProperty; // link to next property down hierarchy
218  QgsProjectPropertyKey *newPropertyKey = nullptr;
219 
220  propertiesModified = false;
221  while ( ! keySequence.isEmpty() )
222  {
223  // if the current head of the sequence list matches the property name,
224  // then traverse down the property hierarchy
225  if ( keySequence.first() == currentProperty->name() )
226  {
227  // remove front key since we're traversing down a level
228  keySequence.pop_front();
229 
230  // if key sequence has one last element, then we use that as the
231  // name to store the value
232  if ( 1 == keySequence.count() )
233  {
234  QgsProjectProperty *property = currentProperty->find( keySequence.front() );
235  if ( !property || property->value() != value )
236  {
237  currentProperty->setValue( keySequence.front(), value );
238  propertiesModified = true;
239  }
240 
241  return currentProperty;
242  }
243  // we're at the top element if popping the keySequence element
244  // will leave it empty; in that case, just add the key
245  else if ( keySequence.isEmpty() )
246  {
247  if ( currentProperty->value() != value )
248  {
249  currentProperty->setValue( value );
250  propertiesModified = true;
251  }
252 
253  return currentProperty;
254  }
255  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
256  {
257  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
258 
259  if ( currentProperty )
260  {
261  continue;
262  }
263  else // QgsProjectPropertyValue not Key, so return null
264  {
265  return nullptr;
266  }
267  }
268  else // the next subkey doesn't exist, so add it
269  {
270  if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
271  {
272  currentProperty = newPropertyKey;
273  }
274  continue;
275  }
276  }
277  else
278  {
279  return nullptr;
280  }
281  }
282 
283  return nullptr;
284 }
285 
294 void removeKey_( const QString &scope,
295  const QString &key,
296  QgsProjectPropertyKey &rootProperty )
297 {
298  QgsProjectPropertyKey *currentProperty = &rootProperty;
299 
300  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
301  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
302 
303  QStringList keySequence = makeKeyTokens_( scope, key );
304 
305  while ( ! keySequence.isEmpty() )
306  {
307  // if the current head of the sequence list matches the property name,
308  // then traverse down the property hierarchy
309  if ( keySequence.first() == currentProperty->name() )
310  {
311  // remove front key since we're traversing down a level
312  keySequence.pop_front();
313 
314  // if we have only one key name left, then try to remove the key
315  // with that name
316  if ( 1 == keySequence.count() )
317  {
318  currentProperty->removeKey( keySequence.front() );
319  }
320  // if we're out of keys then the current property is the one we
321  // want to remove, but we can't delete it directly; we need to
322  // delete it from the parent property key container
323  else if ( keySequence.isEmpty() )
324  {
325  previousQgsPropertyKey->removeKey( currentProperty->name() );
326  }
327  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
328  {
329  previousQgsPropertyKey = currentProperty;
330  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
331 
332  if ( currentProperty )
333  {
334  continue;
335  }
336  else // QgsProjectPropertyValue not Key, so return null
337  {
338  return;
339  }
340  }
341  else // if the next key down isn't found
342  {
343  // then the overall key sequence doesn't exist
344  return;
345  }
346  }
347  else
348  {
349  return;
350  }
351  }
352 }
353 
354 QgsProject::QgsProject( QObject *parent )
355  : QObject( parent )
356  , mLayerStore( new QgsMapLayerStore( this ) )
357  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
358  , mSnappingConfig( this )
359  , mRelationManager( new QgsRelationManager( this ) )
360  , mAnnotationManager( new QgsAnnotationManager( this ) )
361  , mLayoutManager( new QgsLayoutManager( this ) )
362  , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
363  , mRootGroup( new QgsLayerTree )
364  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
365  , mArchive( new QgsProjectArchive() )
366  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
367 {
368  mProperties.setName( QStringLiteral( "properties" ) );
369  clear();
370 
371  // bind the layer tree to the map layer registry.
372  // whenever layers are added to or removed from the registry,
373  // layer tree will be updated
374  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
375  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
376  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
377  connect( this, qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
378 
379  // proxy map layer store signals to this
380  connect( mLayerStore.get(), qgis::overload<const QStringList &>::of( &QgsMapLayerStore::layersWillBeRemoved ),
381  this, qgis::overload< const QStringList &>::of( &QgsProject::layersWillBeRemoved ) );
382  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ),
383  this, qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsProject::layersWillBeRemoved ) );
384  connect( mLayerStore.get(), qgis::overload< const QString & >::of( &QgsMapLayerStore::layerWillBeRemoved ),
385  this, qgis::overload< const QString & >::of( &QgsProject::layerWillBeRemoved ) );
386  connect( mLayerStore.get(), qgis::overload< QgsMapLayer * >::of( &QgsMapLayerStore::layerWillBeRemoved ),
387  this, qgis::overload< QgsMapLayer * >::of( &QgsProject::layerWillBeRemoved ) );
388  connect( mLayerStore.get(), qgis::overload<const QStringList & >::of( &QgsMapLayerStore::layersRemoved ), this, &QgsProject::layersRemoved );
389  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this, &QgsProject::layerRemoved );
390  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this, &QgsProject::removeAll );
391  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this, &QgsProject::layersAdded );
392  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this, &QgsProject::layerWasAdded );
393 
394  if ( QgsApplication::instance() )
395  {
397  }
398 
399  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ), this,
400  [ = ]( const QList<QgsMapLayer *> &layers )
401  {
402  for ( const auto &layer : layers )
403  {
404  disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
405  }
406  }
407  );
408  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersAdded ), this,
409  [ = ]( const QList<QgsMapLayer *> &layers )
410  {
411  for ( const auto &layer : layers )
412  {
413  connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
414  }
415  }
416  );
417 }
418 
419 
421 {
422  mIsBeingDeleted = true;
423 
424  clear();
425  delete mBadLayerHandler;
426  delete mRelationManager;
427  delete mLayerTreeRegistryBridge;
428  delete mRootGroup;
429  if ( this == sProject )
430  {
431  sProject = nullptr;
432  }
433 }
434 
435 void QgsProject::setInstance( QgsProject *project )
436 {
437  sProject = project;
438 }
439 
440 
442 {
443  if ( !sProject )
444  {
445  sProject = new QgsProject;
446  }
447  return sProject;
448 }
449 
450 void QgsProject::setTitle( const QString &title )
451 {
452  if ( title == mMetadata.title() )
453  return;
454 
455  mMetadata.setTitle( title );
456  emit metadataChanged();
457 
458  setDirty( true );
459 }
460 
461 QString QgsProject::title() const
462 {
463  return mMetadata.title();
464 }
465 
467 {
468  return mDirty;
469 }
470 
471 void QgsProject::setDirty( const bool dirty )
472 {
473  if ( dirty && mDirtyBlockCount > 0 )
474  return;
475 
476  if ( mDirty == dirty )
477  return;
478 
479  mDirty = dirty;
480  emit isDirtyChanged( mDirty );
481 }
482 
483 void QgsProject::setPresetHomePath( const QString &path )
484 {
485  if ( path == mHomePath )
486  return;
487 
488  mHomePath = path;
489  emit homePathChanged();
490 
491  setDirty( true );
492 }
493 
494 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
495 {
496  const QList<QgsAttributeEditorElement *> elements = parent->children();
497 
498  for ( QgsAttributeEditorElement *element : elements )
499  {
500  if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
501  {
502  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
503 
504  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
505 
506  if ( !container->children().empty() )
507  registerTranslatableContainers( translationContext, container, layerId );
508  }
509  }
510 }
511 
513 {
514  //register layers
515  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
516 
517  for ( const QgsLayerTreeLayer *layer : layers )
518  {
519  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
520 
521  QgsMapLayer *mapLayer = layer->layer();
522  if ( mapLayer && mapLayer->type() == QgsMapLayerType::VectorLayer )
523  {
524  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
525 
526  //register aliases and fields
527  const QgsFields fields = vlayer->fields();
528  for ( const QgsField &field : fields )
529  {
530  QString fieldName;
531  if ( field.alias().isEmpty() )
532  fieldName = field.name();
533  else
534  fieldName = field.alias();
535 
536  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
537 
538  if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueRelation" ) )
539  {
540  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
541  }
542  }
543 
544  //register formcontainers
545  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
546 
547  }
548  }
549 
550  //register layergroups
551  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
552  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
553  {
554  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
555  }
556 
557  //register relations
558  const QList<QgsRelation> &relations = mRelationManager->relations().values();
559  for ( const QgsRelation &relation : relations )
560  {
561  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
562  }
563 }
564 
565 void QgsProject::setFileName( const QString &name )
566 {
567  if ( name == mFile.fileName() )
568  return;
569 
570  QString oldHomePath = homePath();
571 
572  mFile.setFileName( name );
573  emit fileNameChanged();
574 
575  QString newHomePath = homePath();
576  if ( newHomePath != oldHomePath )
577  emit homePathChanged();
578 
579  setDirty( true );
580 }
581 
582 QString QgsProject::fileName() const
583 {
584  return mFile.fileName();
585 }
586 
587 QFileInfo QgsProject::fileInfo() const
588 {
589  return QFileInfo( mFile );
590 }
591 
593 {
595 }
596 
597 QDateTime QgsProject::lastModified() const
598 {
599  if ( QgsProjectStorage *storage = projectStorage() )
600  {
602  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
603  return metadata.lastModified;
604  }
605  else
606  {
607  return QFileInfo( mFile.fileName() ).lastModified();
608  }
609 }
610 
612 {
613  if ( projectStorage() )
614  return QString();
615 
616  if ( mFile.fileName().isEmpty() )
617  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
618 
619  return QFileInfo( mFile.fileName() ).absolutePath();
620 }
621 
623 {
624  if ( projectStorage() )
625  return QString();
626 
627  if ( mFile.fileName().isEmpty() )
628  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
629 
630  return QFileInfo( mFile.fileName() ).absoluteFilePath();
631 }
632 
633 QString QgsProject::baseName() const
634 {
635  if ( QgsProjectStorage *storage = projectStorage() )
636  {
638  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
639  return metadata.name;
640  }
641  else
642  {
643  return QFileInfo( mFile.fileName() ).completeBaseName();
644  }
645 }
646 
648 {
649  return mCrs;
650 }
651 
652 void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
653 {
654  if ( crs != mCrs )
655  {
656  mCrs = crs;
657  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
658  setDirty( true );
659  emit crsChanged();
660  }
661 
662  if ( adjustEllipsoid )
664 }
665 
666 QString QgsProject::ellipsoid() const
667 {
668  if ( !crs().isValid() )
669  return GEO_NONE;
670 
671  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), GEO_NONE );
672 }
673 
674 void QgsProject::setEllipsoid( const QString &ellipsoid )
675 {
676  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
677  return;
678 
679  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
680  emit ellipsoidChanged( ellipsoid );
681 }
682 
684 {
685  return mTransformContext;
686 }
687 
689 {
690  if ( context == mTransformContext )
691  return;
692 
693  mTransformContext = context;
694  for ( auto &layer : mLayerStore.get()->mapLayers() )
695  {
696  layer->setTransformContext( context );
697  }
699 }
700 
702 {
703  QgsSettings s;
704 
705  mFile.setFileName( QString() );
706  mProperties.clearKeys();
707  mHomePath = QString();
708  mAutoTransaction = false;
709  mEvaluateDefaultValues = false;
710  mDirty = false;
711  mTrustLayerMetadata = false;
712  mCustomVariables.clear();
713  mMetadata = QgsProjectMetadata();
714  if ( !s.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
715  {
716  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
718  }
719  emit metadataChanged();
720 
722  context.readSettings();
723  setTransformContext( context );
724 
725  mEmbeddedLayers.clear();
726  mRelationManager->clear();
727  mAnnotationManager->clear();
728  mLayoutManager->clear();
729  mBookmarkManager->clear();
730  mSnappingConfig.reset();
731  emit snappingConfigChanged( mSnappingConfig );
733 
734  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
736 
737  mLabelingEngineSettings->clear();
738 
739  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
740  mArchive->clear();
741 
743 
744  if ( !mIsBeingDeleted )
745  {
746  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
747  emit projectColorsChanged();
748  }
749 
750  // reset some default project properties
751  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
752  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
753  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
754  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
755 
756  //copy default units to project
757  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
758  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
759 
761  mRootGroup->clear();
762 
763  setDirty( false );
764  emit homePathChanged();
765  emit cleared();
766 }
767 
768 // basically a debugging tool to dump property list values
769 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
770 {
771  QgsDebugMsg( QStringLiteral( "current properties:" ) );
772  topQgsPropertyKey.dump();
773 }
774 
775 
806 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
807 {
808  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
809 
810  if ( propertiesElem.isNull() ) // no properties found, so we're done
811  {
812  return;
813  }
814 
815  QDomNodeList scopes = propertiesElem.childNodes();
816 
817  if ( scopes.count() < 1 )
818  {
819  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
820  return;
821  }
822 
823  if ( ! project_properties.readXml( propertiesElem ) )
824  {
825  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
826  }
827 }
828 
829 
834 static void _getTitle( const QDomDocument &doc, QString &title )
835 {
836  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
837 
838  title.clear(); // by default the title will be empty
839 
840  if ( !nl.count() )
841  {
842  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
843  return;
844  }
845 
846  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element OK
847 
848  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
849  {
850  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
851  return;
852  }
853 
854  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
855 
856  if ( !titleTextNode.isText() )
857  {
858  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
859  return;
860  }
861 
862  QDomText titleText = titleTextNode.toText();
863 
864  title = titleText.data();
865 
866 }
867 
868 QgsProjectVersion getVersion( const QDomDocument &doc )
869 {
870  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
871 
872  if ( !nl.count() )
873  {
874  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
875  return QgsProjectVersion( 0, 0, 0, QString() );
876  }
877 
878  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
879 
880  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
881  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
882  return projectVersion;
883 }
884 
885 
887 {
888  return mSnappingConfig;
889 }
890 
892 {
893  if ( mSnappingConfig == snappingConfig )
894  return;
895 
896  mSnappingConfig = snappingConfig;
897  setDirty( true );
898  emit snappingConfigChanged( mSnappingConfig );
899 }
900 
901 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, QgsProject::ReadFlags flags )
902 {
903  // Layer order is set by the restoring the legend settings from project file.
904  // This is done on the 'readProject( ... )' signal
905 
906  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
907 
908  // process the map layer nodes
909 
910  if ( 0 == nl.count() ) // if we have no layers to process, bail
911  {
912  return true; // Decided to return "true" since it's
913  // possible for there to be a project with no
914  // layers; but also, more imporantly, this
915  // would cause the tests/qgsproject to fail
916  // since the test suite doesn't currently
917  // support test layers
918  }
919 
920  bool returnStatus = true;
921 
922  emit layerLoaded( 0, nl.count() );
923 
924  // order layers based on their dependencies
925  QgsLayerDefinition::DependencySorter depSorter( doc );
926  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
927  return false;
928 
929  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
930 
931  int i = 0;
932  for ( const QDomNode &node : sortedLayerNodes )
933  {
934  const QDomElement element = node.toElement();
935 
936  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
937  if ( !name.isNull() )
938  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
939 
940  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
941  {
942  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, flags );
943  }
944  else
945  {
946  QgsReadWriteContext context;
947  context.setPathResolver( pathResolver() );
948  context.setProjectTranslator( this );
950 
951  if ( !addLayer( element, brokenNodes, context, flags ) )
952  {
953  returnStatus = false;
954  }
955  const auto messages = context.takeMessages();
956  if ( !messages.isEmpty() )
957  {
958  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
959  }
960  }
961  emit layerLoaded( i + 1, nl.count() );
962  i++;
963  }
964 
965  return returnStatus;
966 }
967 
968 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, QgsProject::ReadFlags flags )
969 {
970  QString type = layerElem.attribute( QStringLiteral( "type" ) );
971  QgsDebugMsgLevel( "Layer type is " + type, 4 );
972  std::unique_ptr<QgsMapLayer> mapLayer;
973 
974  if ( type == QLatin1String( "vector" ) )
975  {
976  mapLayer = qgis::make_unique<QgsVectorLayer>();
977  // apply specific settings to vector layer
978  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
979  {
980  vl->setReadExtentFromXml( mTrustLayerMetadata );
981  }
982  }
983  else if ( type == QLatin1String( "raster" ) )
984  {
985  mapLayer = qgis::make_unique<QgsRasterLayer>();
986  }
987  else if ( type == QLatin1String( "mesh" ) )
988  {
989  mapLayer = qgis::make_unique<QgsMeshLayer>();
990  }
991  else if ( type == QLatin1String( "plugin" ) )
992  {
993  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
994  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
995  }
996 
997  if ( !mapLayer )
998  {
999  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1000  return false;
1001  }
1002 
1003  Q_CHECK_PTR( mapLayer ); // NOLINT
1004 
1005  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1006  // because if it was, the newly created layer will not be added to the store and it would leak.
1007  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1008  Q_ASSERT( ! layerId.isEmpty() );
1009  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1010 
1011  // have the layer restore state that is stored in Dom node
1012  QgsMapLayer::ReadFlags layerFlags = nullptr;
1013  if ( flags & QgsProject::FlagDontResolveLayers )
1014  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1015  bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1016  QList<QgsMapLayer *> newLayers;
1017  newLayers << mapLayer.get();
1018  if ( layerIsValid || flags & QgsProject::FlagDontResolveLayers )
1019  {
1020  emit readMapLayer( mapLayer.get(), layerElem );
1021  addMapLayers( newLayers );
1022  }
1023  else
1024  {
1025  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1026  addMapLayers( newLayers, false );
1027  newLayers.first();
1028  QgsDebugMsg( "Unable to load " + type + " layer" );
1029  brokenNodes.push_back( layerElem );
1030  }
1031 
1032  // It should be safe to delete the layer now if layer was stored, because all the store
1033  // had to to was to reset the data source in case the validity changed.
1034  if ( ! layerWasStored )
1035  {
1036  mapLayer.release();
1037  }
1038 
1039  return layerIsValid;
1040 }
1041 
1042 bool QgsProject::read( const QString &filename, QgsProject::ReadFlags flags )
1043 {
1044  mFile.setFileName( filename );
1045 
1046  return read( flags );
1047 }
1048 
1049 bool QgsProject::read( QgsProject::ReadFlags flags )
1050 {
1051  QString filename = mFile.fileName();
1052  bool returnValue;
1053 
1054  if ( QgsProjectStorage *storage = projectStorage() )
1055  {
1056  QTemporaryFile inDevice;
1057  if ( !inDevice.open() )
1058  {
1059  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1060  return false;
1061  }
1062 
1063  QgsReadWriteContext context;
1064  context.setProjectTranslator( this );
1065  if ( !storage->readProject( filename, &inDevice, context ) )
1066  {
1067  QString err = tr( "Unable to open %1" ).arg( filename );
1068  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1069  if ( !messages.isEmpty() )
1070  err += QStringLiteral( "\n\n" ) + messages.last().message();
1071  setError( err );
1072  return false;
1073  }
1074  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1075  }
1076  else
1077  {
1078  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1079  {
1080  returnValue = unzip( mFile.fileName(), flags );
1081  }
1082  else
1083  {
1084  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1085  returnValue = readProjectFile( mFile.fileName(), flags );
1086  }
1087 
1088  //on translation we should not change the filename back
1089  if ( !mTranslator )
1090  {
1091  mFile.setFileName( filename );
1092  }
1093  else
1094  {
1095  //but delete the translator
1096  mTranslator.reset( nullptr );
1097  }
1098  }
1099  emit homePathChanged();
1100  return returnValue;
1101 }
1102 
1103 bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags flags )
1104 {
1105  QFile projectFile( filename );
1106  clearError();
1107 
1108  QgsSettings settings;
1109 
1110  QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), settings.value( QStringLiteral( "locale/userLocale" ), QString() ).toString() );
1111 
1112  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1113  {
1114  mTranslator.reset( new QTranslator() );
1115  mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1116  }
1117 
1118  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1119 
1120  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1121  {
1122  projectFile.close();
1123 
1124  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1125 
1126  return false;
1127  }
1128 
1129  // location of problem associated with errorMsg
1130  int line, column;
1131  QString errorMsg;
1132 
1133  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1134  {
1135  // want to make this class as GUI independent as possible; so commented out
1136 #if 0
1137  QMessageBox::critical( 0, tr( "Read Project File" ),
1138  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
1139 #endif
1140 
1141  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1142  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1143 
1144  QgsDebugMsg( errorString );
1145 
1146  projectFile.close();
1147 
1148  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1149 
1150  return false;
1151  }
1152 
1153  projectFile.close();
1154 
1155  QgsDebugMsg( "Opened document " + projectFile.fileName() );
1156 
1157  // get project version string, if any
1158  QgsProjectVersion fileVersion = getVersion( *doc );
1159  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
1160 
1161  if ( thisVersion > fileVersion )
1162  {
1163  QgsLogger::warning( "Loading a file that was saved with an older "
1164  "version of qgis (saved in " + fileVersion.text() +
1165  ", loaded in " + Qgis::QGIS_VERSION +
1166  "). Problems may occur." );
1167 
1168  QgsProjectFileTransform projectFile( *doc, fileVersion );
1169 
1170  // Shows a warning when an old project file is read.
1171  emit oldProjectVersionWarning( fileVersion.text() );
1172  QgsDebugMsg( QStringLiteral( "Emitting oldProjectVersionWarning(oldVersion)." ) );
1173 
1174  projectFile.updateRevision( thisVersion );
1175  }
1176 
1177  // start new project, just keep the file name and auxiliary storage
1178  QString fileName = mFile.fileName();
1179  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1180  clear();
1181  mAuxiliaryStorage = std::move( aStorage );
1182  mFile.setFileName( fileName );
1183 
1184  // now get any properties
1185  _getProperties( *doc, mProperties );
1186 
1187  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
1188 
1189  dump_( mProperties );
1190 
1191  // get older style project title
1192  QString oldTitle;
1193  _getTitle( *doc, oldTitle );
1194 
1195  QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1196  if ( homePathNl.count() > 0 )
1197  {
1198  QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1199  QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1200  if ( !homePath.isEmpty() )
1201  setPresetHomePath( homePath );
1202  }
1203  else
1204  {
1205  emit homePathChanged();
1206  }
1207 
1208  QgsReadWriteContext context;
1209  context.setPathResolver( pathResolver() );
1210  context.setProjectTranslator( this );
1211 
1212  //crs
1213  QgsCoordinateReferenceSystem projectCrs;
1214  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1215  {
1216  // first preference - dedicated projectCrs node
1217  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1218  if ( !srsNode.isNull() )
1219  {
1220  projectCrs.readXml( srsNode );
1221  }
1222 
1223  if ( !projectCrs.isValid() )
1224  {
1225  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1226  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1227 
1228  // try the CRS
1229  if ( currentCRS >= 0 )
1230  {
1231  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1232  }
1233 
1234  // if that didn't produce a match, try the proj.4 string
1235  if ( !projCrsString.isEmpty() && ( !projectCrs.isValid() || projectCrs.toProj4() != projCrsString ) )
1236  {
1237  projectCrs = QgsCoordinateReferenceSystem::fromProj4( projCrsString );
1238  }
1239 
1240  // last just take the given id
1241  if ( !projectCrs.isValid() )
1242  {
1243  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1244  }
1245  }
1246  }
1247  mCrs = projectCrs;
1248 
1249  QStringList datumErrors;
1250  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1251  {
1252  emit missingDatumTransforms( datumErrors );
1253  }
1254  emit transformContextChanged();
1255 
1256  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "projectMetadata" ) );
1257  if ( !nl.isEmpty() )
1258  {
1259  QDomElement metadataElement = nl.at( 0 ).toElement();
1260  mMetadata.readMetadataXml( metadataElement );
1261  }
1262  else
1263  {
1264  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1265  mMetadata = QgsProjectMetadata();
1266  }
1267  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1268  {
1269  // upgrade older title storage to storing within project metadata.
1270  mMetadata.setTitle( oldTitle );
1271  }
1272  emit metadataChanged();
1273 
1274  nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
1275  if ( nl.count() )
1276  {
1277  QDomElement transactionElement = nl.at( 0 ).toElement();
1278  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1279  mAutoTransaction = true;
1280  }
1281 
1282  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
1283  if ( nl.count() )
1284  {
1285  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
1286  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1287  mEvaluateDefaultValues = true;
1288  }
1289 
1290  nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
1291  if ( nl.count() )
1292  {
1293  QDomElement trustElement = nl.at( 0 ).toElement();
1294  if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1295  mTrustLayerMetadata = true;
1296  }
1297 
1298  // read the layer tree from project file
1299 
1300  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1301 
1302  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1303  if ( !layerTreeElem.isNull() )
1304  {
1305  mRootGroup->readChildrenFromXml( layerTreeElem, context );
1306  }
1307  else
1308  {
1309  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1310  }
1311 
1312  mLayerTreeRegistryBridge->setEnabled( false );
1313 
1314  // get the map layers
1315  QList<QDomNode> brokenNodes;
1316  bool clean = _getMapLayers( *doc, brokenNodes, flags );
1317 
1318  // review the integrity of the retrieved map layers
1319  if ( !clean )
1320  {
1321  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1322 
1323  if ( !brokenNodes.isEmpty() )
1324  {
1325  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1326  }
1327 
1328  // we let a custom handler decide what to do with missing layers
1329  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1330  mBadLayerHandler->handleBadLayers( brokenNodes );
1331  }
1332 
1333  // Resolve references to other layers
1334  // Needs to be done here once all dependent layers are loaded
1335  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1336  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
1337  {
1338  it.value()->resolveReferences( this );
1339  }
1340 
1341  mLayerTreeRegistryBridge->setEnabled( true );
1342 
1343  // load embedded groups and layers
1344  loadEmbeddedNodes( mRootGroup, flags );
1345 
1346  // now that layers are loaded, we can resolve layer tree's references to the layers
1347  mRootGroup->resolveReferences( this );
1348 
1349 
1350  if ( !layerTreeElem.isNull() )
1351  {
1352  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1353  }
1354 
1355  // Load pre 3.0 configuration
1356  QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1357  if ( !layerTreeCanvasElem.isNull( ) )
1358  {
1359  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1360  }
1361 
1362  // Convert pre 3.4 to create layers flags
1363  if ( QgsProjectVersion( 3, 4, 0 ) > fileVersion )
1364  {
1365  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1366  for ( const QString &layerId : requiredLayerIds )
1367  {
1368  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1369  {
1370  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1371  }
1372  }
1373  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1374  for ( const QString &layerId : disabledLayerIds )
1375  {
1376  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1377  {
1378  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1379  }
1380  }
1381  }
1382 
1383  // After bad layer handling we might still have invalid layers,
1384  // store them in case the user wanted to handle them later
1385  // or wanted to pass them through when saving
1386  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1387 
1388  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1389 
1390  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1392  mMapThemeCollection->readXml( *doc );
1393 
1394  mLabelingEngineSettings->readSettingsFromProject( this );
1396 
1397  mAnnotationManager->readXml( doc->documentElement(), context );
1398  mLayoutManager->readXml( doc->documentElement(), *doc );
1399  mBookmarkManager->readXml( doc->documentElement(), *doc );
1400 
1401  // reassign change dependencies now that all layers are loaded
1402  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1403  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1404  {
1405  it.value()->setDependencies( it.value()->dependencies() );
1406  }
1407 
1408  mSnappingConfig.readProject( *doc );
1409  //add variables defined in project file
1410  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1411  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1412 
1413  mCustomVariables.clear();
1414  if ( variableNames.length() == variableValues.length() )
1415  {
1416  for ( int i = 0; i < variableNames.length(); ++i )
1417  {
1418  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1419  }
1420  }
1421  else
1422  {
1423  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1424  }
1425  emit customVariablesChanged();
1426  emit crsChanged();
1427  emit ellipsoidChanged( ellipsoid() );
1428 
1429  // read the project: used by map canvas and legend
1430  emit readProject( *doc );
1431  emit readProjectWithContext( *doc, context );
1432  emit snappingConfigChanged( mSnappingConfig );
1434  emit projectColorsChanged();
1435 
1436  // if all went well, we're allegedly in pristine state
1437  if ( clean )
1438  setDirty( false );
1439 
1443 
1444  if ( mTranslator )
1445  {
1446  //project possibly translated -> rename it with locale postfix
1447  QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
1448  setFileName( newFileName );
1449 
1450  if ( write() )
1451  {
1452  setTitle( localeFileName );
1453  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Success );
1454  }
1455  else
1456  {
1457  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Critical );
1458  }
1459  }
1460  return true;
1461 }
1462 
1463 
1464 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
1465 {
1466 
1467  const auto constChildren = group->children();
1468  for ( QgsLayerTreeNode *child : constChildren )
1469  {
1470  if ( QgsLayerTree::isGroup( child ) )
1471  {
1472  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1473  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1474  {
1475  // make sure to convert the path from relative to absolute
1476  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1477  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1478  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
1479  if ( newGroup )
1480  {
1481  QList<QgsLayerTreeNode *> clonedChildren;
1482  const auto constChildren = newGroup->children();
1483  for ( QgsLayerTreeNode *newGroupChild : constChildren )
1484  clonedChildren << newGroupChild->clone();
1485  delete newGroup;
1486 
1487  childGroup->insertChildNodes( 0, clonedChildren );
1488  }
1489  }
1490  else
1491  {
1492  loadEmbeddedNodes( childGroup, flags );
1493  }
1494  }
1495  else if ( QgsLayerTree::isLayer( child ) )
1496  {
1497  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1498  {
1499  QList<QDomNode> brokenNodes;
1500  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes, flags );
1501  }
1502  }
1503 
1504  }
1505 }
1506 
1507 QVariantMap QgsProject::customVariables() const
1508 {
1509  return mCustomVariables;
1510 }
1511 
1512 void QgsProject::setCustomVariables( const QVariantMap &variables )
1513 {
1514  if ( variables == mCustomVariables )
1515  return;
1516 
1517  //write variable to project
1518  QStringList variableNames;
1519  QStringList variableValues;
1520 
1521  QVariantMap::const_iterator it = variables.constBegin();
1522  for ( ; it != variables.constEnd(); ++it )
1523  {
1524  variableNames << it.key();
1525  variableValues << it.value().toString();
1526  }
1527 
1528  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1529  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1530 
1531  mCustomVariables = variables;
1532 
1533  emit customVariablesChanged();
1534 }
1535 
1537 {
1538  *mLabelingEngineSettings = settings;
1540 }
1541 
1543 {
1544  return *mLabelingEngineSettings;
1545 }
1546 
1548 {
1549  return mLayerStore.get();
1550 }
1551 
1553 {
1554  return mLayerStore.get();
1555 }
1556 
1557 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1558 {
1559  QList<QgsVectorLayer *> layers;
1560  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1561  const auto constLayerIds = layerIds;
1562  for ( const QString &layerId : constLayerIds )
1563  {
1564  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1565  layers << vlayer;
1566  }
1567  return layers;
1568 }
1569 
1570 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1571 {
1572  QStringList list;
1573  const auto constLayers = layers;
1574  for ( QgsVectorLayer *layer : constLayers )
1575  list << layer->id();
1576  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1578 }
1579 
1581 {
1582  QgsExpressionContext context;
1583 
1586 
1587  return context;
1588 }
1589 
1590 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1591 {
1592  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1593 
1594  bool tgChanged = false;
1595 
1596  const auto constLayers = layers;
1597  for ( QgsMapLayer *layer : constLayers )
1598  {
1599  if ( layer->isValid() )
1600  {
1601  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1602  if ( vlayer )
1603  {
1604  if ( autoTransaction() )
1605  {
1606  if ( QgsTransaction::supportsTransaction( vlayer ) )
1607  {
1608  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1609  QString key = vlayer->providerType();
1610 
1611  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1612 
1613  if ( !tg )
1614  {
1615  tg = new QgsTransactionGroup();
1616  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1617  tgChanged = true;
1618  }
1619  tg->addLayer( vlayer );
1620  }
1621  }
1623  }
1624 
1625  if ( tgChanged )
1626  emit transactionGroupsChanged();
1627 
1628  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1629 
1630  // check if we have to update connections for layers with dependencies
1631  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1632  {
1633  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1634  if ( deps.contains( layer->id() ) )
1635  {
1636  // reconnect to change signals
1637  it.value()->setDependencies( deps );
1638  }
1639  }
1640  }
1641  }
1642 
1643  if ( mSnappingConfig.addLayers( layers ) )
1644  emit snappingConfigChanged( mSnappingConfig );
1645 }
1646 
1647 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1648 {
1649  if ( mSnappingConfig.removeLayers( layers ) )
1650  emit snappingConfigChanged( mSnappingConfig );
1651 }
1652 
1653 void QgsProject::cleanTransactionGroups( bool force )
1654 {
1655  bool changed = false;
1656  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1657  {
1658  if ( tg.value()->isEmpty() || force )
1659  {
1660  delete tg.value();
1661  tg = mTransactionGroups.erase( tg );
1662  changed = true;
1663  }
1664  else
1665  {
1666  ++tg;
1667  }
1668  }
1669  if ( changed )
1670  emit transactionGroupsChanged();
1671 }
1672 
1673 bool QgsProject::readLayer( const QDomNode &layerNode )
1674 {
1675  QgsReadWriteContext context;
1676  context.setPathResolver( pathResolver() );
1677  context.setProjectTranslator( this );
1679  QList<QDomNode> brokenNodes;
1680  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
1681  {
1682  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1683  // added layer for joins
1684  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1685  const auto constVectorLayers = vectorLayers;
1686  for ( QgsVectorLayer *layer : constVectorLayers )
1687  {
1688  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1689  layer->resolveReferences( this );
1690  }
1691 
1692  return true;
1693  }
1694  return false;
1695 }
1696 
1697 bool QgsProject::write( const QString &filename )
1698 {
1699  mFile.setFileName( filename );
1700 
1701  return write();
1702 }
1703 
1705 {
1706  if ( QgsProjectStorage *storage = projectStorage() )
1707  {
1708  QgsReadWriteContext context;
1709  // for projects stored in a custom storage, we have to check for the support
1710  // of relative paths since the storage most likely will not be in a file system
1711  QString storageFilePath { storage->filePath( mFile.fileName() ) };
1712  if ( storageFilePath.isEmpty() )
1713  {
1714  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
1715  }
1716  context.setPathResolver( pathResolver() );
1717 
1718  QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
1719  QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
1720 
1721  if ( !zip( tmpZipFilename ) )
1722  return false; // zip() already calls setError() when returning false
1723 
1724  QFile tmpZipFile( tmpZipFilename );
1725  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
1726  {
1727  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
1728  return false;
1729  }
1730 
1732  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
1733  {
1734  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
1735  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1736  if ( !messages.isEmpty() )
1737  err += QStringLiteral( "\n\n" ) + messages.last().message();
1738  setError( err );
1739  return false;
1740  }
1741 
1742  tmpZipFile.close();
1743  QFile::remove( tmpZipFilename );
1744 
1745  return true;
1746  }
1747 
1748  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1749  {
1750  return zip( mFile.fileName() );
1751  }
1752  else
1753  {
1754  // write project file even if the auxiliary storage is not correctly
1755  // saved
1756  const bool asOk = saveAuxiliaryStorage();
1757  const bool writeOk = writeProjectFile( mFile.fileName() );
1758 
1759  // errors raised during writing project file are more important
1760  if ( !asOk && writeOk )
1761  {
1762  const QString err = mAuxiliaryStorage->errorString();
1763  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
1764  }
1765 
1766  return asOk && writeOk;
1767  }
1768 }
1769 
1770 bool QgsProject::writeProjectFile( const QString &filename )
1771 {
1772  QFile projectFile( filename );
1773  clearError();
1774 
1775  // if we have problems creating or otherwise writing to the project file,
1776  // let's find out up front before we go through all the hand-waving
1777  // necessary to create all the Dom objects
1778  QFileInfo myFileInfo( projectFile );
1779  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1780  {
1781  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1782  .arg( projectFile.fileName() ) );
1783  return false;
1784  }
1785 
1786  QgsReadWriteContext context;
1787  context.setPathResolver( pathResolver() );
1789 
1790  QDomImplementation DomImplementation;
1791  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1792 
1793  QDomDocumentType documentType =
1794  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1795  QStringLiteral( "SYSTEM" ) );
1796  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1797 
1798  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1799  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1800  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1801 
1802  doc->appendChild( qgisNode );
1803 
1804  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
1805  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
1806  qgisNode.appendChild( homePathNode );
1807 
1808  // title
1809  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1810  qgisNode.appendChild( titleNode );
1811 
1812  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1813  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? 1 : 0 );
1814  qgisNode.appendChild( transactionNode );
1815 
1816  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1817  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? 1 : 0 );
1818  qgisNode.appendChild( evaluateDefaultValuesNode );
1819 
1820  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
1821  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? 1 : 0 );
1822  qgisNode.appendChild( trustNode );
1823 
1824  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1825  titleNode.appendChild( titleText );
1826 
1827  // write project CRS
1828  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
1829  mCrs.writeXml( srsNode, *doc );
1830  qgisNode.appendChild( srsNode );
1831 
1832  // write layer tree - make sure it is without embedded subgroups
1833  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1835  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
1836 
1837  clonedRoot->writeXml( qgisNode, context );
1838  delete clonedRoot;
1839 
1840  mSnappingConfig.writeProject( *doc );
1841 
1842  // let map canvas and legend write their information
1843  emit writeProject( *doc );
1844 
1845  // within top level node save list of layers
1846  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
1847 
1848  // Iterate over layers in zOrder
1849  // Call writeXml() on each
1850  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1851 
1852  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
1853  while ( li != layers.end() )
1854  {
1855  QgsMapLayer *ml = li.value();
1856 
1857  if ( ml )
1858  {
1859  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1860  if ( emIt == mEmbeddedLayers.constEnd() )
1861  {
1862  QDomElement maplayerElem;
1863  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
1864  // not available, just write what we DO have
1865  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
1866  {
1867  // general layer metadata
1868  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1869  ml->writeLayerXml( maplayerElem, *doc, context );
1870  }
1871  else if ( ! ml->originalXmlProperties().isEmpty() )
1872  {
1873  QDomDocument document;
1874  if ( document.setContent( ml->originalXmlProperties() ) )
1875  {
1876  maplayerElem = document.firstChildElement();
1877  }
1878  else
1879  {
1880  QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
1881  }
1882  }
1883 
1884  emit writeMapLayer( ml, maplayerElem, *doc );
1885 
1886  projectLayersNode.appendChild( maplayerElem );
1887  }
1888  else
1889  {
1890  // layer defined in an external project file
1891  // only save embedded layer if not managed by a legend group
1892  if ( emIt.value().second )
1893  {
1894  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1895  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
1896  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
1897  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
1898  projectLayersNode.appendChild( mapLayerElem );
1899  }
1900  }
1901  }
1902  li++;
1903  }
1904 
1905  qgisNode.appendChild( projectLayersNode );
1906 
1907  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
1908  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
1909  for ( QgsMapLayer *layer : constCustomLayerOrder )
1910  {
1911  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
1912  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
1913  layerOrderNode.appendChild( mapLayerElem );
1914  }
1915  qgisNode.appendChild( layerOrderNode );
1916 
1917  mLabelingEngineSettings->writeSettingsToProject( this );
1918 
1919  // now add the optional extra properties
1920 
1921  dump_( mProperties );
1922 
1923  QgsDebugMsg( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
1924 
1925  if ( !mProperties.isEmpty() ) // only worry about properties if we
1926  // actually have any properties
1927  {
1928  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
1929  }
1930 
1931  mMapThemeCollection->writeXml( *doc );
1932 
1933  mTransformContext.writeXml( qgisNode, context );
1934 
1935  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
1936  mMetadata.writeMetadataXml( metadataElem, *doc );
1937  qgisNode.appendChild( metadataElem );
1938 
1939  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
1940  qgisNode.appendChild( annotationsElem );
1941 
1942  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
1943  qgisNode.appendChild( layoutElem );
1944 
1945  QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
1946  qgisNode.appendChild( bookmarkElem );
1947 
1948  // now wrap it up and ship it to the project file
1949  doc->normalize(); // XXX I'm not entirely sure what this does
1950 
1951  // Create backup file
1952  if ( QFile::exists( fileName() ) )
1953  {
1954  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
1955  bool ok = true;
1956  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1957  ok &= projectFile.open( QIODevice::ReadOnly );
1958 
1959  QByteArray ba;
1960  while ( ok && !projectFile.atEnd() )
1961  {
1962  ba = projectFile.read( 10240 );
1963  ok &= backupFile.write( ba ) == ba.size();
1964  }
1965 
1966  projectFile.close();
1967  backupFile.close();
1968 
1969  if ( !ok )
1970  {
1971  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1972  return false;
1973  }
1974 
1975  QFileInfo fi( fileName() );
1976  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
1977  utime( backupFile.fileName().toUtf8().constData(), &tb );
1978  }
1979 
1980  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1981  {
1982  projectFile.close(); // even though we got an error, let's make
1983  // sure it's closed anyway
1984 
1985  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
1986  return false;
1987  }
1988 
1989  QTemporaryFile tempFile;
1990  bool ok = tempFile.open();
1991  if ( ok )
1992  {
1993  QTextStream projectFileStream( &tempFile );
1994  doc->save( projectFileStream, 2 ); // save as utf-8
1995  ok &= projectFileStream.pos() > -1;
1996 
1997  ok &= tempFile.seek( 0 );
1998 
1999  QByteArray ba;
2000  while ( ok && !tempFile.atEnd() )
2001  {
2002  ba = tempFile.read( 10240 );
2003  ok &= projectFile.write( ba ) == ba.size();
2004  }
2005 
2006  ok &= projectFile.error() == QFile::NoError;
2007 
2008  projectFile.close();
2009  }
2010 
2011  tempFile.close();
2012 
2013  if ( !ok )
2014  {
2015  setError( tr( "Unable to save to file %1. Your project "
2016  "may be corrupted on disk. Try clearing some space on the volume and "
2017  "check file permissions before pressing save again." )
2018  .arg( projectFile.fileName() ) );
2019  return false;
2020  }
2021 
2022  setDirty( false ); // reset to pristine state
2023 
2024  emit projectSaved();
2025 
2026  return true;
2027 }
2028 
2029 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2030 {
2031  bool propertiesModified;
2032  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2033 
2034  if ( propertiesModified )
2035  setDirty( true );
2036 
2037  return success;
2038 }
2039 
2040 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2041 {
2042  bool propertiesModified;
2043  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2044 
2045  if ( propertiesModified )
2046  setDirty( true );
2047 
2048  return success;
2049 }
2050 
2051 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2052 {
2053  bool propertiesModified;
2054  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2055 
2056  if ( propertiesModified )
2057  setDirty( true );
2058 
2059  return success;
2060 }
2061 
2062 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2063 {
2064  bool propertiesModified;
2065  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2066 
2067  if ( propertiesModified )
2068  setDirty( true );
2069 
2070  return success;
2071 }
2072 
2073 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2074 {
2075  bool propertiesModified;
2076  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2077 
2078  if ( propertiesModified )
2079  setDirty( true );
2080 
2081  return success;
2082 }
2083 
2084 QStringList QgsProject::readListEntry( const QString &scope,
2085  const QString &key,
2086  const QStringList &def,
2087  bool *ok ) const
2088 {
2089  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2090 
2091  QVariant value;
2092 
2093  if ( property )
2094  {
2095  value = property->value();
2096 
2097  bool valid = QVariant::StringList == value.type();
2098  if ( ok )
2099  *ok = valid;
2100 
2101  if ( valid )
2102  {
2103  return value.toStringList();
2104  }
2105  }
2106 
2107  return def;
2108 }
2109 
2110 
2111 QString QgsProject::readEntry( const QString &scope,
2112  const QString &key,
2113  const QString &def,
2114  bool *ok ) const
2115 {
2116  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2117 
2118  QVariant value;
2119 
2120  if ( property )
2121  {
2122  value = property->value();
2123 
2124  bool valid = value.canConvert( QVariant::String );
2125  if ( ok )
2126  *ok = valid;
2127 
2128  if ( valid )
2129  return value.toString();
2130  }
2131 
2132  return def;
2133 }
2134 
2135 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2136  bool *ok ) const
2137 {
2138  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2139 
2140  QVariant value;
2141 
2142  if ( property )
2143  {
2144  value = property->value();
2145  }
2146 
2147  bool valid = value.canConvert( QVariant::Int );
2148 
2149  if ( ok )
2150  {
2151  *ok = valid;
2152  }
2153 
2154  if ( valid )
2155  {
2156  return value.toInt();
2157  }
2158 
2159  return def;
2160 }
2161 
2162 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2163  double def,
2164  bool *ok ) const
2165 {
2166  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2167  if ( property )
2168  {
2169  QVariant value = property->value();
2170 
2171  bool valid = value.canConvert( QVariant::Double );
2172  if ( ok )
2173  *ok = valid;
2174 
2175  if ( valid )
2176  return value.toDouble();
2177  }
2178 
2179  return def;
2180 }
2181 
2182 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
2183  bool *ok ) const
2184 {
2185  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2186 
2187  if ( property )
2188  {
2189  QVariant value = property->value();
2190 
2191  bool valid = value.canConvert( QVariant::Bool );
2192  if ( ok )
2193  *ok = valid;
2194 
2195  if ( valid )
2196  return value.toBool();
2197  }
2198 
2199  return def;
2200 }
2201 
2202 
2203 bool QgsProject::removeEntry( const QString &scope, const QString &key )
2204 {
2205  if ( findKey_( scope, key, mProperties ) )
2206  {
2207  removeKey_( scope, key, mProperties );
2208  setDirty( true );
2209  }
2210 
2211  return !findKey_( scope, key, mProperties );
2212 }
2213 
2214 
2215 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
2216 {
2217  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2218 
2219  QStringList entries;
2220 
2221  if ( foundProperty )
2222  {
2223  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2224 
2225  if ( propertyKey )
2226  { propertyKey->entryList( entries ); }
2227  }
2228 
2229  return entries;
2230 }
2231 
2232 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
2233 {
2234  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2235 
2236  QStringList entries;
2237 
2238  if ( foundProperty )
2239  {
2240  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2241 
2242  if ( propertyKey )
2243  { propertyKey->subkeyList( entries ); }
2244  }
2245 
2246  return entries;
2247 }
2248 
2250 {
2251  dump_( mProperties );
2252 }
2253 
2255 {
2256  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
2257  QString filePath;
2258  if ( ! absolutePaths )
2259  {
2260  // for projects stored in a custom storage, we need to ask to the
2261  // storage for the path, if the storage returns an empty path
2262  // relative paths are not supported
2263  if ( QgsProjectStorage *storage = projectStorage() )
2264  {
2265  filePath = storage->filePath( mFile.fileName() );
2266  }
2267  else
2268  {
2269  filePath = fileName();
2270  }
2271  }
2272  return QgsPathResolver( filePath );
2273 }
2274 
2275 QString QgsProject::readPath( const QString &src ) const
2276 {
2277  return pathResolver().readPath( src );
2278 }
2279 
2280 QString QgsProject::writePath( const QString &src ) const
2281 {
2282  return pathResolver().writePath( src );
2283 }
2284 
2285 void QgsProject::setError( const QString &errorMessage )
2286 {
2287  mErrorMessage = errorMessage;
2288 }
2289 
2290 QString QgsProject::error() const
2291 {
2292  return mErrorMessage;
2293 }
2294 
2295 void QgsProject::clearError()
2296 {
2297  setError( QString() );
2298 }
2299 
2301 {
2302  delete mBadLayerHandler;
2303  mBadLayerHandler = handler;
2304 }
2305 
2306 QString QgsProject::layerIsEmbedded( const QString &id ) const
2307 {
2308  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
2309  if ( it == mEmbeddedLayers.constEnd() )
2310  {
2311  return QString();
2312  }
2313  return it.value().first;
2314 }
2315 
2316 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
2317  bool saveFlag, QgsProject::ReadFlags flags )
2318 {
2319  QgsDebugCall;
2320 
2321  static QString sPrevProjectFilePath;
2322  static QDateTime sPrevProjectFileTimestamp;
2323  static QDomDocument sProjectDocument;
2324 
2325  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
2326 
2327  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
2328  {
2329  sPrevProjectFilePath.clear();
2330 
2331  QFile projectFile( projectFilePath );
2332  if ( !projectFile.open( QIODevice::ReadOnly ) )
2333  {
2334  return false;
2335  }
2336 
2337  if ( !sProjectDocument.setContent( &projectFile ) )
2338  {
2339  return false;
2340  }
2341 
2342  sPrevProjectFilePath = projectFilePath;
2343  sPrevProjectFileTimestamp = projectFileTimestamp;
2344  }
2345 
2346  // does project store paths absolute or relative?
2347  bool useAbsolutePaths = true;
2348 
2349  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
2350  if ( !propertiesElem.isNull() )
2351  {
2352  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
2353  if ( !absElem.isNull() )
2354  {
2355  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
2356  }
2357  }
2358 
2359  QgsReadWriteContext embeddedContext;
2360  if ( !useAbsolutePaths )
2361  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
2362  embeddedContext.setProjectTranslator( this );
2363  embeddedContext.setTransformContext( transformContext() );
2364 
2365  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
2366  if ( projectLayersElem.isNull() )
2367  {
2368  return false;
2369  }
2370 
2371  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
2372  for ( int i = 0; i < mapLayerNodes.size(); ++i )
2373  {
2374  // get layer id
2375  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
2376  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
2377  if ( id == layerId )
2378  {
2379  // layer can be embedded only once
2380  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
2381  {
2382  return false;
2383  }
2384 
2385  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
2386 
2387  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
2388  {
2389  return true;
2390  }
2391  else
2392  {
2393  mEmbeddedLayers.remove( layerId );
2394  return false;
2395  }
2396  }
2397  }
2398 
2399  return false;
2400 }
2401 
2402 
2403 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags )
2404 {
2405  // open project file, get layer ids in group, add the layers
2406  QFile projectFile( projectFilePath );
2407  if ( !projectFile.open( QIODevice::ReadOnly ) )
2408  {
2409  return nullptr;
2410  }
2411 
2412  QDomDocument projectDocument;
2413  if ( !projectDocument.setContent( &projectFile ) )
2414  {
2415  return nullptr;
2416  }
2417 
2418  QgsReadWriteContext context;
2419  context.setPathResolver( pathResolver() );
2420  context.setProjectTranslator( this );
2422 
2424 
2425  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2426  if ( !layerTreeElem.isNull() )
2427  {
2428  root->readChildrenFromXml( layerTreeElem, context );
2429  }
2430  else
2431  {
2432  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2433  }
2434 
2435  QgsLayerTreeGroup *group = root->findGroup( groupName );
2436  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
2437  {
2438  // embedded groups cannot be embedded again
2439  delete root;
2440  return nullptr;
2441  }
2442 
2443  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
2444  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
2445  delete root;
2446  root = nullptr;
2447 
2448  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2449  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2450 
2451  // set "embedded" to all children + load embedded layers
2452  mLayerTreeRegistryBridge->setEnabled( false );
2453  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
2454  mLayerTreeRegistryBridge->setEnabled( true );
2455 
2456  // consider the layers might be identify disabled in its project
2457  const auto constFindLayerIds = newGroup->findLayerIds();
2458  for ( const QString &layerId : constFindLayerIds )
2459  {
2460  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2461  if ( layer )
2462  {
2463  layer->resolveReferences( this );
2464  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
2465  }
2466  }
2467 
2468  return newGroup;
2469 }
2470 
2471 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
2472 {
2473  const auto constChildren = group->children();
2474  for ( QgsLayerTreeNode *child : constChildren )
2475  {
2476  // all nodes in the subtree will have "embedded" custom property set
2477  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2478 
2479  if ( QgsLayerTree::isGroup( child ) )
2480  {
2481  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
2482  }
2483  else if ( QgsLayerTree::isLayer( child ) )
2484  {
2485  // load the layer into our project
2486  QList<QDomNode> brokenNodes;
2487  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
2488  }
2489  }
2490 }
2491 
2493 {
2494  return mEvaluateDefaultValues;
2495 }
2496 
2498 {
2499  if ( evaluateDefaultValues == mEvaluateDefaultValues )
2500  return;
2501 
2502  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2503  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
2504  for ( ; layerIt != layers.constEnd(); ++layerIt )
2505  {
2506  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
2507  if ( vl )
2508  {
2510  }
2511  }
2512 
2513  mEvaluateDefaultValues = evaluateDefaultValues;
2514 }
2515 
2517 {
2518  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2520 }
2521 
2523 {
2524  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2525 }
2526 
2528 {
2529  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2530  if ( !distanceUnitString.isEmpty() )
2531  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2532 
2533  //fallback to QGIS default measurement unit
2534  QgsSettings s;
2535  bool ok = false;
2536  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2537  return ok ? type : QgsUnitTypes::DistanceMeters;
2538 }
2539 
2541 {
2542  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2543 }
2544 
2546 {
2547  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2548  if ( !areaUnitString.isEmpty() )
2549  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2550 
2551  //fallback to QGIS default area unit
2552  QgsSettings s;
2553  bool ok = false;
2554  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2555  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2556 }
2557 
2559 {
2560  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2561 }
2562 
2563 QString QgsProject::homePath() const
2564 {
2565  if ( !mHomePath.isEmpty() )
2566  {
2567  QFileInfo homeInfo( mHomePath );
2568  if ( !homeInfo.isRelative() )
2569  return mHomePath;
2570  }
2571 
2572  QFileInfo pfi( fileName() );
2573  if ( !pfi.exists() )
2574  return mHomePath;
2575 
2576  if ( !mHomePath.isEmpty() )
2577  {
2578  // path is relative to project file
2579  return QDir::cleanPath( pfi.path() + '/' + mHomePath );
2580  }
2581  else
2582  {
2583  return pfi.canonicalPath();
2584  }
2585 }
2586 
2588 {
2589  return mHomePath;
2590 }
2591 
2593 {
2594  return mRelationManager;
2595 }
2596 
2598 {
2599  return mLayoutManager.get();
2600 }
2601 
2603 {
2604  return mLayoutManager.get();
2605 }
2606 
2608 {
2609  return mBookmarkManager;
2610 }
2611 
2613 {
2614  return mBookmarkManager;
2615 }
2616 
2618 {
2619  return mRootGroup;
2620 }
2621 
2623 {
2624  return mMapThemeCollection.get();
2625 }
2626 
2628 {
2629  return mAnnotationManager.get();
2630 }
2631 
2633 {
2634  return mAnnotationManager.get();
2635 }
2636 
2637 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2638 {
2639  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
2640  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
2641  {
2642  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2643  continue;
2644 
2645  if ( layers.contains( it.value() ) )
2646  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
2647  else
2648  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
2649  }
2650 
2654 }
2655 
2656 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2657 {
2658  QList<QgsMapLayer *> nonIdentifiableLayers;
2659  nonIdentifiableLayers.reserve( layerIds.count() );
2660  for ( const QString &layerId : layerIds )
2661  {
2662  QgsMapLayer *layer = mapLayer( layerId );
2663  if ( layer )
2664  nonIdentifiableLayers << layer;
2665  }
2667  setNonIdentifiableLayers( nonIdentifiableLayers );
2669 }
2670 
2671 QStringList QgsProject::nonIdentifiableLayers() const
2672 {
2673  QStringList nonIdentifiableLayers;
2674 
2675  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2676  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
2677  {
2678  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2679  {
2680  nonIdentifiableLayers.append( it.value()->id() );
2681  }
2682  }
2683  return nonIdentifiableLayers;
2684 }
2685 
2687 {
2688  return mAutoTransaction;
2689 }
2690 
2692 {
2693  if ( autoTransaction != mAutoTransaction )
2694  {
2695  mAutoTransaction = autoTransaction;
2696 
2697  if ( autoTransaction )
2698  onMapLayersAdded( mapLayers().values() );
2699  else
2700  cleanTransactionGroups( true );
2701  }
2702 }
2703 
2704 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2705 {
2706  return mTransactionGroups;
2707 }
2708 
2709 
2710 //
2711 // QgsMapLayerStore methods
2712 //
2713 
2714 
2716 {
2717  return mLayerStore->count();
2718 }
2719 
2721 {
2722  return mLayerStore->validCount();
2723 }
2724 
2725 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
2726 {
2727  return mLayerStore->mapLayer( layerId );
2728 }
2729 
2730 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
2731 {
2732  return mLayerStore->mapLayersByName( layerName );
2733 }
2734 
2735 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
2736 {
2737  QList<QgsMapLayer *> layers;
2738  const auto constMapLayers { mLayerStore->mapLayers() };
2739  for ( const auto &l : constMapLayers )
2740  {
2741  if ( ! l->shortName().isEmpty() )
2742  {
2743  if ( l->shortName() == shortName )
2744  layers << l;
2745  }
2746  else if ( l->name() == shortName )
2747  {
2748  layers << l;
2749  }
2750  }
2751  return layers;
2752 }
2753 
2754 bool QgsProject::unzip( const QString &filename, QgsProject::ReadFlags flags )
2755 {
2756  clearError();
2757  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2758 
2759  // unzip the archive
2760  if ( !archive->unzip( filename ) )
2761  {
2762  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
2763  return false;
2764  }
2765 
2766  // test if zip provides a .qgs file
2767  if ( archive->projectFile().isEmpty() )
2768  {
2769  setError( tr( "Zip archive does not provide a project file" ) );
2770  return false;
2771  }
2772 
2773  // load auxiliary storage
2774  if ( !archive->auxiliaryStorageFile().isEmpty() )
2775  {
2776  // database file is already a copy as it's been unzipped. So we don't open
2777  // auxiliary storage in copy mode in this case
2778  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
2779  }
2780  else
2781  {
2782  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
2783  }
2784 
2785  // read the project file
2786  if ( ! readProjectFile( archive->projectFile(), flags ) )
2787  {
2788  setError( tr( "Cannot read unzipped qgs project file" ) );
2789  return false;
2790  }
2791 
2792  // keep the archive and remove the temporary .qgs file
2793  mArchive = std::move( archive );
2794  mArchive->clearProjectFile();
2795 
2796  return true;
2797 }
2798 
2799 bool QgsProject::zip( const QString &filename )
2800 {
2801  clearError();
2802 
2803  // save the current project in a temporary .qgs file
2804  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2805  const QString baseName = QFileInfo( filename ).baseName();
2806  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
2807  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
2808 
2809  bool writeOk = false;
2810  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2811  {
2812  writeOk = writeProjectFile( qgsFile.fileName() );
2813  qgsFile.close();
2814  }
2815 
2816  // stop here with an error message
2817  if ( ! writeOk )
2818  {
2819  setError( tr( "Unable to write temporary qgs file" ) );
2820  return false;
2821  }
2822 
2823  // save auxiliary storage
2824  const QFileInfo info( qgsFile );
2825  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
2826 
2827  if ( ! saveAuxiliaryStorage( asFileName ) )
2828  {
2829  const QString err = mAuxiliaryStorage->errorString();
2830  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2831  return false;
2832  }
2833 
2834  // create the archive
2835  archive->addFile( qgsFile.fileName() );
2836  archive->addFile( asFileName );
2837 
2838  // zip
2839  if ( !archive->zip( filename ) )
2840  {
2841  setError( tr( "Unable to perform zip" ) );
2842  return false;
2843  }
2844 
2845  return true;
2846 }
2847 
2849 {
2850  return QgsZipUtils::isZipFile( mFile.fileName() );
2851 }
2852 
2853 QList<QgsMapLayer *> QgsProject::addMapLayers(
2854  const QList<QgsMapLayer *> &layers,
2855  bool addToLegend,
2856  bool takeOwnership )
2857 {
2858  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
2859  if ( !myResultList.isEmpty() )
2860  {
2861  // Update transform context
2862  for ( auto &l : myResultList )
2863  {
2864  l->setTransformContext( transformContext() );
2865  }
2866  if ( addToLegend )
2867  {
2868  emit legendLayersAdded( myResultList );
2869  }
2870  }
2871 
2872  if ( mAuxiliaryStorage )
2873  {
2874  for ( QgsMapLayer *mlayer : myResultList )
2875  {
2876  if ( mlayer->type() != QgsMapLayerType::VectorLayer )
2877  continue;
2878 
2879  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
2880  if ( vl )
2881  {
2882  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
2883  }
2884  }
2885  }
2886 
2887  return myResultList;
2888 }
2889 
2890 QgsMapLayer *
2892  bool addToLegend,
2893  bool takeOwnership )
2894 {
2895  QList<QgsMapLayer *> addedLayers;
2896  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
2897  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
2898 }
2899 
2900 void QgsProject::removeMapLayers( const QStringList &layerIds )
2901 {
2902  mLayerStore->removeMapLayers( layerIds );
2903 }
2904 
2905 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
2906 {
2907  mLayerStore->removeMapLayers( layers );
2908 }
2909 
2910 void QgsProject::removeMapLayer( const QString &layerId )
2911 {
2912  mLayerStore->removeMapLayer( layerId );
2913 }
2914 
2916 {
2917  mLayerStore->removeMapLayer( layer );
2918 }
2919 
2921 {
2922  return mLayerStore->takeMapLayer( layer );
2923 }
2924 
2926 {
2927  mLayerStore->removeAllMapLayers();
2928 }
2929 
2931 {
2932  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2933  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
2934  for ( ; it != layers.constEnd(); ++it )
2935  {
2936  it.value()->reload();
2937  }
2938 }
2939 
2940 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
2941 {
2942  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
2943 }
2944 
2945 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
2946 {
2947  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
2948 }
2949 
2951 {
2952  QgsSettings settings;
2953  QgsCoordinateReferenceSystem defaultCrs;
2954  if ( settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "useProject" )
2955  || settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "prompt" ) )
2956  {
2957  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
2958  // (since "prompt" has no meaning here - the prompt will always be shown, it's just deciding on the default choice in the prompt!)
2959  defaultCrs = crs();
2960  }
2961  else
2962  {
2963  // global crs
2964  QString layerDefaultCrs = settings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), GEO_EPSG_CRS_AUTHID ).toString();
2965  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
2966  }
2967 
2968  return defaultCrs;
2969 }
2970 
2972 {
2973  mTrustLayerMetadata = trust;
2974 
2975  auto layers = mapLayers();
2976  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
2977  {
2978  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
2979  if ( vl )
2980  {
2981  vl->setReadExtentFromXml( trust );
2982  }
2983  }
2984 }
2985 
2986 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
2987 {
2988  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2989  bool empty = true;
2990  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
2991  {
2992  if ( it.value()->type() != QgsMapLayerType::VectorLayer )
2993  continue;
2994 
2995  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
2996  if ( vl && vl->auxiliaryLayer() )
2997  {
2998  vl->auxiliaryLayer()->save();
2999  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
3000  }
3001  }
3002 
3003  if ( !mAuxiliaryStorage->exists( *this ) && filename.isEmpty() && empty )
3004  {
3005  return true; // it's not an error
3006  }
3007  else if ( !filename.isEmpty() )
3008  {
3009  return mAuxiliaryStorage->saveAs( filename );
3010  }
3011  else
3012  {
3013  return mAuxiliaryStorage->saveAs( *this );
3014  }
3015 }
3016 
3018 {
3019  return mAuxiliaryStorage.get();
3020 }
3021 
3023 {
3024  return mAuxiliaryStorage.get();
3025 }
3026 
3028 {
3029  return mMetadata;
3030 }
3031 
3033 {
3034  if ( metadata == mMetadata )
3035  return;
3036 
3037  mMetadata = metadata;
3038  emit metadataChanged();
3039 
3040  setDirty( true );
3041 }
3042 
3043 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
3044 {
3045  QSet<QgsMapLayer *> requiredLayers;
3046 
3047  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3048  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3049  {
3050  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3051  {
3052  requiredLayers.insert( it.value() );
3053  }
3054  }
3055  return requiredLayers;
3056 }
3057 
3058 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
3059 {
3060  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3061  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3062  {
3063  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3064  continue;
3065 
3066  if ( layers.contains( it.value() ) )
3067  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
3068  else
3069  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
3070  }
3071 }
3072 
3074 {
3075  // save colors to project
3076  QStringList customColors;
3077  QStringList customColorLabels;
3078 
3079  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
3080  for ( ; colorIt != colors.constEnd(); ++colorIt )
3081  {
3082  QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
3083  QString label = ( *colorIt ).second;
3084  customColors.append( color );
3085  customColorLabels.append( label );
3086  }
3087  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
3088  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
3089  emit projectColorsChanged();
3090 }
3091 
3092 void QgsProject::generateTsFile( const QString &locale )
3093 {
3094  QgsTranslationContext translationContext;
3095  translationContext.setProject( this );
3096  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
3097 
3098  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
3099 
3100  translationContext.writeTsFile( locale );
3101 }
3102 
3103 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
3104 {
3105  if ( !mTranslator )
3106  {
3107  return sourceText;
3108  }
3109 
3110  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
3111 
3112  if ( result.isEmpty() )
3113  {
3114  return sourceText;
3115  }
3116  return result;
3117 }
3118 
3120 {
3121  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
3122  if ( !layers.empty() )
3123  {
3124  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3125  {
3126  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
3127  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3128  {
3129  if ( !( ( *it )->accept( visitor ) ) )
3130  return false;
3131 
3132  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3133  return false;
3134  }
3135  }
3136  }
3137 
3138  if ( !mLayoutManager->accept( visitor ) )
3139  return false;
3140 
3141  if ( !mAnnotationManager->accept( visitor ) )
3142  return false;
3143 
3144  return true;
3145 }
Q_DECL_DEPRECATED QStringList nonIdentifiableLayers() const
Gets the list of layers which currently should not be taken into account on map identification.
Layer tree group node serves as a container for layers and further groups.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:471
void setTrustLayerMetadata(bool trust)
Sets the trust option allowing to indicate if the extent has to be read from the XML document when da...
The class is used as a container of context for various read/write operations on other objects...
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QString error() const
Returns error message from previous read/write.
QString name
Name of the project - equivalent to a file&#39;s base name (i.e. without path and extension).
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:466
static Q_INVOKABLE AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer *> &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Base class for all map layer types.
Definition: qgsmaplayer.h:79
void layersAdded(const QList< QgsMapLayer *> &layers)
Emitted when one or more layers were added to the registry.
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:597
static QgsCoordinateReferenceSystem fromProj4(const QString &proj4)
Creates a CRS from a proj4 style formatted string.
static const QString QGIS_VERSION
Version string.
Definition: qgis.h:51
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsRelationManager * relationManager() const
const QgsAuxiliaryStorage * auxiliaryStorage() const
Returns the current const auxiliary storage.
This is an abstract base class for any elements of a drag and drop form.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:43
QgsMapLayerType type() const
Returns the type of the layer.
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer *> &layers)
A list of layers with which intersections should be avoided.
void isDirtyChanged(bool dirty)
Emitted when the project dirty status changes.
QgsTransactionGroup * transactionGroup(const QString &providerKey, const QString &connString)
Returns the matching transaction group from a provider key and connection string. ...
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the store.
bool save()
Commits changes and starts editing then.
Manages storage of a set of QgsAnnotation annotation objects.
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
Definition: qgsproject.cpp:769
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Don&#39;t resolve layer paths (i.e. don&#39;t load any layer content). Dramatically improves project read tim...
Definition: qgsproject.h:276
const QgsProjectMetadata & metadata() const
Returns a reference to the project&#39;s metadata store.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ...
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
Definition: qgsproject.cpp:891
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
Class used to work with layer dependencies stored in a XML project or layer definition file...
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
QString providerType() const
Returns the provider type (provider key) for this layer.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
void setCreationDateTime(const QDateTime &creationDateTime)
Sets the project&#39;s creation date/timestamp.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:634
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
QString toProj4() const
Returns a Proj4 string representation of this CRS.
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:565
Class providing some utility methods to manage auxiliary storage.
void crsChanged()
Emitted when the CRS of the project has changed.
void layersAdded(const QList< QgsMapLayer *> &layers)
Emitted when one or more layers were added to the store.
void generateTsFile(const QString &locale)
Triggers the collection strings of .qgs to be included in ts file and calls writeTsFile() ...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the store.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
void configChanged()
Emitted whenever the configuration is changed.
Container of fields for a vector layer.
Definition: qgsfields.h:42
void labelingEngineSettingsChanged()
Emitted when global configuration of the labeling engine changes.
void projectColorsChanged()
Emitted whenever the project&#39;s color scheme has been changed.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application&#39;s plugin layer registry, used for managing plugin layer types.
void topologicalEditingChanged()
Emitted when the topological editing flag has changed.
int count() const
Returns the number of registered layers.
void metadataChanged()
Emitted when the project&#39;s metadata is changed.
Manages storage of a set of bookmarks.
QDateTime lastModified
Date and local time when the file was last modified.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
void updateRelationsStatus()
Updates relations status.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
void reloadAllLayers()
Reload all registered layer&#39;s provider data caches, synchronising the layer with any changes in the d...
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
void legendLayersAdded(const QList< QgsMapLayer *> &layers)
Emitted, when a layer was added to the registry and the legend.
bool isValid() const
Returns the status of the layer.
An interface for classes which can visit style entity (e.g.
void removeKey(const QString &keyName)
Removes the specified key.
void projectSaved()
Emitted when the project file has been written and closed.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the store.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
void reset()
reset to default values
void allLayersRemoved()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results...
QgsMapLayerStore * layerStore()
Returns a pointer to the project&#39;s internal layer store.
const QgsBookmarkManager * bookmarkManager() const
Returns the project&#39;s bookmark manager, which manages bookmarks within the project.
QgsCoordinateTransformContext transformContext() const
Returns a copy of the project&#39;s coordinate transform context, which stores various information regard...
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
void clear()
Remove any relation managed by this class.
QgsProject(QObject *parent=nullptr)
Create a new QgsProject.
Definition: qgsproject.cpp:354
static QString extension()
Returns the extension used for auxiliary databases.
static QString userFullName()
Returns the user&#39;s operating system login account full display name.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
QgsProjectVersion getVersion(const QDomDocument &doc)
Returns the version string found in the given DOM document.
Definition: qgsproject.cpp:868
const QString GEO_NONE
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.cpp:72
void fileNameChanged()
Emitted when the file name of the project changes.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
static QString encodeColor(const QColor &color)
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:115
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context&#39;s state from a DOM element.
bool readMetadataXml(const QDomElement &metadataElement) override
Sets state from DOM document.
QgsProjectProperty * find(const QString &propertyName) const
Attempts to find a property with a matching sub-key name.
Q_DECL_DEPRECATED void setNonIdentifiableLayers(const QList< QgsMapLayer *> &layers)
Set a list of layers which should not be taken into account on map identification.
void setAuthor(const QString &author)
Sets the project author string.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
QString presetHomePath() const
Returns any manual project home path setting, or an empty string if not set.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean entry to the project file.
QgsProjectPropertyValue * setValue(const QString &name, const QVariant &value)
Sets the value associated with this key.
void readSettings()
Reads the context&#39;s state from application settings.
void missingDatumTransforms(const QStringList &missingTransforms)
Emitted when datum transforms stored in the project are not available locally.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
void readProjectWithContext(const QDomDocument &, QgsReadWriteContext &context)
Emitted when a project is being read.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
QString id() const
Returns the layer&#39;s unique ID, which is used to access this layer from QgsProject.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
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...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QString name() const
The name of the property is used as identifier.
Namespace with helper functions for layer tree operations.
Definition: qgslayertree.h:32
QVariant value() const override
If this key has a value, it will be stored by its name in its properties.
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager&#39;s state from a DOM element, restoring all bookmarks present in the XML document...
bool writeMetadataXml(QDomElement &metadataElement, QDomDocument &document) const override
Stores state in a DOM node.
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the default area measurement units for the project.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets data coordinate transform context to transformContext.
const QString & typeName
A class to describe the version of a project.
QString layerIsEmbedded(const QString &id) const
Returns project file path if layer is embedded from other project file. Returns empty string if layer...
int count() const
Returns the number of sub-keys contained by this property.
virtual void clearKeys()
Deletes any sub-nodes from the property.
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the store.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const
Returns the default CRS for new layers based on the settings and the current project CRS...
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
void dumpProperties() const
Dump out current project properties to stderr.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
void readProject(const QDomDocument &)
Emitted when a project is being read.
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.
Listens to the updates in map layer registry and does changes in layer tree.
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
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...
QStringList subkeyList(const QString &scope, const QString &key) const
Returns keys with keys – do not return keys that contain only values.
void clear()
Clear any information from this layer tree.
Don&#39;t resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:531
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
void setCrs(const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid=false)
Sets the project&#39;s native coordinate reference system.
Definition: qgsproject.cpp:652
QList< QgsLayerTreeGroup * > findGroups() const
Find all group layer nodes.
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
void layerLoaded(int i, int n)
Emitted when a layer from a projects was read.
Contains information relating to a node (i.e.
void dump(int tabs=0) const override
Dumps out the keys and values.
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the default distance measurement units for the project.
Metadata associated with a project.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:53
This class is a base class for nodes in a layer tree.
void dataSourceChanged()
Emitted whenever the layer&#39;s data source has been changed.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
Reads and writes project states.
Definition: qgsproject.h:90
const QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:70
void setEllipsoid(const QString &ellipsoid)
Sets the project&#39;s ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:674
~QgsProject() override
Definition: qgsproject.cpp:420
void setProjectColors(const QgsNamedColorList &colors)
Sets the colors for the project&#39;s color scheme (see QgsProjectColorScheme).
void insertChildNodes(int index, const QList< QgsLayerTreeNode *> &nodes)
Insert existing nodes at specified position.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, bool saveFlag=true, QgsProject::ReadFlags flags=nullptr)
Creates a maplayer instance defined in an arbitrary project file.
QList< QgsReadWriteContext::ReadWriteMessage > takeMessages()
Returns the stored messages and remove them.
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted when a layer is being saved.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
void removeAllMapLayers()
Removes all registered layers.
void clear()
Removes and deletes all bookmarks from the manager.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file...
virtual bool isValue() const =0
Returns true if the property is a QgsProjectPropertyValue.
Contains information about the context in which a coordinate transform is executed.
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Remove a given key.
Definition: qgsproject.cpp:294
QgsEditFormConfig editFormConfig
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
Manages storage of a set of layouts.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the store.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
void setProviderProperty(ProviderProperty property, const QVariant &value)
Allows setting arbitrary properties on the provider.
QgsProjectStorage * projectStorage() const
Returns pointer to project storage implementation that handles read/write of the project file...
Definition: qgsproject.cpp:592
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
bool write()
Writes the project to its current associated file (see fileName() ).
Used for the collecting of strings from projects for translation and creation of ts files...
QgsAnnotationManager * annotationManager()
Returns pointer to the project&#39;s annotation manager.
#define QgsDebugCall
Definition: qgslogger.h:37
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
const QgsLayoutManager * layoutManager() const
Returns the project&#39;s layout manager, which manages compositions within the project.
QStringList makeKeyTokens_(const QString &scope, const QString &key)
Take the given scope and key and convert them to a string list of key tokens that will be used to nav...
Definition: qgsproject.cpp:88
bool loadAuxiliaryLayer(const QgsAuxiliaryStorage &storage, const QString &key=QString())
Loads the auxiliary layer for this vector layer.
bool removeLayers(const QList< QgsMapLayer *> &layers)
Removes the specified layers from the individual layer configuration.
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
Definition: qgsproject.cpp:512
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
QString absolutePath() const
Returns full absolute path to the project folder if the project is stored in a file system - derived ...
Definition: qgsproject.cpp:611
void setMetadata(const QgsProjectMetadata &metadata)
Sets the project&#39;s metadata store.
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
bool topologicalEditing() const
Convenience function to query topological editing status.
bool readLayer(const QDomNode &layerNode)
Reads the layer described in the associated DOM node.
QString homePath() const
Returns the project&#39;s home path.
bool isEmpty() const
Returns true if this property contains no sub-keys.
QgsMapThemeCollection * mapThemeCollection()
Returns pointer to the project&#39;s map theme collection.
QString fileName() const
Returns the project&#39;s file name.
Project property key node.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:54
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project&#39;s layer tree.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context&#39;s state to a DOM element.
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed...
void setPresetHomePath(const QString &path)
Sets the project&#39;s home path.
Definition: qgsproject.cpp:483
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
void writeTsFile(const QString &locale)
Writes the Ts-file.
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:635
void setFileName(const QString &fileName)
Sets the fileName of the TS file.
void registerTranslation(const QString &context, const QString &source)
Registers the source to be translated.
void writeProject(QDomDocument &)
Emitted when the project is being written.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:819
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
void oldProjectVersionWarning(const QString &)
Emitted when an old project file is read.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project&#39;s associated file.
Definition: qgsproject.cpp:587
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the registry.
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already) ...
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
QString source() const
Returns the source for the layer.
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
void setProject(QgsProject *project)
Sets the project being translated.
void registerTranslatableContainers(QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId)
Registers the containers that require translation into the translationContext.
Definition: qgsproject.cpp:494
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed...
This class manages a set of relations between layers.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QgsSnappingConfig snappingConfig() const
The snapping configuration for this project.
Stores global configuration for labeling engine.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:441
void removeMapLayer(const QString &layerId)
Remove a layer from the registry by layer ID.
CORE_EXPORT bool isZipFile(const QString &filename)
Returns true if the file name is a zipped file ( i.e with a &#39;.qgz&#39; extension, false otherwise...
Definition: qgsziputils.cpp:29
This class represents a coordinate reference system (CRS).
static void storeOriginalLayersProperties(QgsLayerTreeGroup *group, const QDomDocument *doc)
Stores in a layer&#39;s originalXmlProperties the layer properties information.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project&#39;s coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:688
bool read(const QString &filename, QgsProject::ReadFlags flags=nullptr)
Reads given project file from the given file.
void setTitle(const QString &title)
Sets the project&#39;s title.
Definition: qgsproject.cpp:450
This is a container for attribute editors, used to group them visually in the attribute form if it is...
void loadingLayer(const QString &layerName)
Emitted when a layer is loaded.
QgsCoordinateReferenceSystem crs() const
Returns the project&#39;s native coordinate reference system.
bool isEmpty() const
Checks whether the container is empty.
Definition: qgsfields.cpp:128
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
Abstract interface for project storage - to be implemented by various backends and registered in QgsP...
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
Definition: qgsmaplayer.h:138
void loadingLayerMessageReceived(const QString &layerName, const QList< QgsReadWriteContext::ReadWriteMessage > &messages)
Emitted when loading layers has produced some messages.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
An Abstract Base Class for QGIS project property hierarchys.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
void homePathChanged()
Emitted when the home path of the project changes.
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
QString name() const override
Returns the group&#39;s name.
void avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
Container class that allows storage of map themes consisting of visible map layers and layer styles...
void transformContextChanged()
Emitted when the project transformContext() is changed.
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:461
This is a container for configuration of the snapping of the project.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer&#39;s data provider, it may be nullptr.
QgsUnitTypes::DistanceUnit distanceUnits() const
Convenience function to query default distance measurement units for project.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
QVariantMap customVariables() const
A map of custom project variables.
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted after the basic initialization of a layer from the project file is done.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QString ellipsoid() const
Returns a proj string representing the project&#39;s ellipsoid setting, e.g., "WGS84".
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
QString baseName() const
Returns the base name of the project file without the path and without extension - derived from fileN...
Definition: qgsproject.cpp:633
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
Resolves relative paths into absolute paths and vice versa.
bool addLayers(const QList< QgsMapLayer *> &layers)
Adds the specified layers as individual layers to the configuration with standard configuration...
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
Class for storing the component parts of a RDBMS data source URI (e.g.
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
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:622
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:130
void setEvaluateDefaultValues(bool evaluateDefaultValues)
Defines if default values should be evaluated on provider side when requested and not when committed...
Represents a vector layer which manages a vector based data sets.
QgsMapLayer * mapLayer(const QString &id) const
Retrieve a pointer to a layer by layer id.
QString name() const
Returns the name of this element.
QList< QgsVectorLayer * > avoidIntersectionsLayers() const
A list of layers with which intersections should be avoided.
QgsProjectStorage * projectStorageFromUri(const QString &uri)
Returns storage implementation if the URI matches one. Returns nullptr otherwise (it is a normal file...
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
void setName(const QString &name)
The name of the property is used as identifier.
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value, bool &propertiesModified)
Add the given key and value.
Definition: qgsproject.cpp:207
AreaUnit
Units of area.
Definition: qgsunittypes.h:80
If the layer is identifiable using the identify map tool and as a WMS layer.
Definition: qgsmaplayer.h:137
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QStringList entryList(const QString &scope, const QString &key) const
Returns keys with values – do not return keys that contain other keys.
void removeAll()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void _getProperties(const QDomDocument &doc, QgsProjectPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:806
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets project&#39;s global labeling engine settings.
void clear()
Clears the project, removing all settings and resetting it back to an empty, default state...
Definition: qgsproject.cpp:701
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...
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *project)
Updates an embedded group from a project.
Interface for classes that handle missing layer files when reading project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Q_DECL_DEPRECATED void nonIdentifiableLayersChanged(QStringList nonIdentifiableLayers)
Emitted when the list of layer which are excluded from map identification changes.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the registry.
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...
Layer tree node points to a map layer.
void setReadExtentFromXml(bool readExtentFromXml)
Flag allowing to indicate if the extent has to be read from the XML document when data source has no ...
A structured metadata store for a map layer.
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project&#39;s global labeling engine settings.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
int validCount() const
Returns the number of registered valid layers.
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags=nullptr)
Create layer group instance defined in an arbitrary project file.