QGIS API Documentation  3.9.0-Master (635e245143)
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  mProjectScope.reset();
457  emit metadataChanged();
458 
459  setDirty( true );
460 }
461 
462 QString QgsProject::title() const
463 {
464  return mMetadata.title();
465 }
466 
468 {
469  return mDirty;
470 }
471 
472 void QgsProject::setDirty( const bool dirty )
473 {
474  if ( dirty && mDirtyBlockCount > 0 )
475  return;
476 
477  if ( mDirty == dirty )
478  return;
479 
480  mDirty = dirty;
481  emit isDirtyChanged( mDirty );
482 }
483 
484 void QgsProject::setPresetHomePath( const QString &path )
485 {
486  if ( path == mHomePath )
487  return;
488 
489  mHomePath = path;
490  mCachedHomePath.clear();
491  mProjectScope.reset();
492 
493  emit homePathChanged();
494 
495  setDirty( true );
496 }
497 
498 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
499 {
500  const QList<QgsAttributeEditorElement *> elements = parent->children();
501 
502  for ( QgsAttributeEditorElement *element : elements )
503  {
504  if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
505  {
506  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
507 
508  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
509 
510  if ( !container->children().empty() )
511  registerTranslatableContainers( translationContext, container, layerId );
512  }
513  }
514 }
515 
517 {
518  //register layers
519  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
520 
521  for ( const QgsLayerTreeLayer *layer : layers )
522  {
523  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
524 
525  QgsMapLayer *mapLayer = layer->layer();
526  if ( mapLayer && mapLayer->type() == QgsMapLayerType::VectorLayer )
527  {
528  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
529 
530  //register aliases and fields
531  const QgsFields fields = vlayer->fields();
532  for ( const QgsField &field : fields )
533  {
534  QString fieldName;
535  if ( field.alias().isEmpty() )
536  fieldName = field.name();
537  else
538  fieldName = field.alias();
539 
540  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
541 
542  if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueRelation" ) )
543  {
544  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
545  }
546  }
547 
548  //register formcontainers
549  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
550 
551  }
552  }
553 
554  //register layergroups
555  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
556  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
557  {
558  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
559  }
560 
561  //register relations
562  const QList<QgsRelation> &relations = mRelationManager->relations().values();
563  for ( const QgsRelation &relation : relations )
564  {
565  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
566  }
567 }
568 
569 void QgsProject::setFileName( const QString &name )
570 {
571  if ( name == mFile.fileName() )
572  return;
573 
574  QString oldHomePath = homePath();
575 
576  mFile.setFileName( name );
577  mCachedHomePath.clear();
578  mProjectScope.reset();
579 
580  emit fileNameChanged();
581 
582  QString newHomePath = homePath();
583  if ( newHomePath != oldHomePath )
584  emit homePathChanged();
585 
586  setDirty( true );
587 }
588 
589 QString QgsProject::fileName() const
590 {
591  return mFile.fileName();
592 }
593 
594 QFileInfo QgsProject::fileInfo() const
595 {
596  return QFileInfo( mFile );
597 }
598 
600 {
602 }
603 
604 QDateTime QgsProject::lastModified() const
605 {
606  if ( QgsProjectStorage *storage = projectStorage() )
607  {
609  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
610  return metadata.lastModified;
611  }
612  else
613  {
614  return QFileInfo( mFile.fileName() ).lastModified();
615  }
616 }
617 
619 {
620  if ( projectStorage() )
621  return QString();
622 
623  if ( mFile.fileName().isEmpty() )
624  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
625 
626  return QFileInfo( mFile.fileName() ).absolutePath();
627 }
628 
630 {
631  if ( projectStorage() )
632  return QString();
633 
634  if ( mFile.fileName().isEmpty() )
635  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
636 
637  return QFileInfo( mFile.fileName() ).absoluteFilePath();
638 }
639 
640 QString QgsProject::baseName() const
641 {
642  if ( QgsProjectStorage *storage = projectStorage() )
643  {
645  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
646  return metadata.name;
647  }
648  else
649  {
650  return QFileInfo( mFile.fileName() ).completeBaseName();
651  }
652 }
653 
655 {
656  return mCrs;
657 }
658 
659 void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
660 {
661  if ( crs != mCrs )
662  {
663  mCrs = crs;
664  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
665  mProjectScope.reset();
666  setDirty( true );
667  emit crsChanged();
668  }
669 
670  if ( adjustEllipsoid )
672 }
673 
674 QString QgsProject::ellipsoid() const
675 {
676  if ( !crs().isValid() )
677  return GEO_NONE;
678 
679  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), GEO_NONE );
680 }
681 
682 void QgsProject::setEllipsoid( const QString &ellipsoid )
683 {
684  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
685  return;
686 
687  mProjectScope.reset();
688  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
689  emit ellipsoidChanged( ellipsoid );
690 }
691 
693 {
694  return mTransformContext;
695 }
696 
698 {
699  if ( context == mTransformContext )
700  return;
701 
702  mTransformContext = context;
703  mProjectScope.reset();
704 
705  for ( auto &layer : mLayerStore.get()->mapLayers() )
706  {
707  layer->setTransformContext( context );
708  }
710 }
711 
713 {
714  QgsSettings s;
715 
716  mProjectScope.reset();
717  mFile.setFileName( QString() );
718  mProperties.clearKeys();
719  mHomePath.clear();
720  mCachedHomePath.clear();
721  mAutoTransaction = false;
722  mEvaluateDefaultValues = false;
723  mDirty = false;
724  mTrustLayerMetadata = false;
725  mCustomVariables.clear();
726  mMetadata = QgsProjectMetadata();
727  if ( !s.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
728  {
729  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
731  }
732  emit metadataChanged();
733 
735  context.readSettings();
736  setTransformContext( context );
737 
738  mEmbeddedLayers.clear();
739  mRelationManager->clear();
740  mAnnotationManager->clear();
741  mLayoutManager->clear();
742  mBookmarkManager->clear();
743  mSnappingConfig.reset();
744  emit snappingConfigChanged( mSnappingConfig );
746 
747  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
749 
750  mLabelingEngineSettings->clear();
751 
752  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
753  mArchive->clear();
754 
756 
757  if ( !mIsBeingDeleted )
758  {
759  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
760  emit projectColorsChanged();
761  }
762 
763  // reset some default project properties
764  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
765  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
766  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
767  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
768 
769  //copy default units to project
770  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
771  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
772 
774  mRootGroup->clear();
775 
776  setDirty( false );
777  emit homePathChanged();
778  emit cleared();
779 }
780 
781 // basically a debugging tool to dump property list values
782 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
783 {
784  QgsDebugMsg( QStringLiteral( "current properties:" ) );
785  topQgsPropertyKey.dump();
786 }
787 
788 
819 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
820 {
821  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
822 
823  if ( propertiesElem.isNull() ) // no properties found, so we're done
824  {
825  return;
826  }
827 
828  QDomNodeList scopes = propertiesElem.childNodes();
829 
830  if ( scopes.count() < 1 )
831  {
832  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
833  return;
834  }
835 
836  if ( ! project_properties.readXml( propertiesElem ) )
837  {
838  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
839  }
840 }
841 
842 
847 static void _getTitle( const QDomDocument &doc, QString &title )
848 {
849  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
850 
851  title.clear(); // by default the title will be empty
852 
853  if ( !nl.count() )
854  {
855  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
856  return;
857  }
858 
859  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element OK
860 
861  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
862  {
863  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
864  return;
865  }
866 
867  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
868 
869  if ( !titleTextNode.isText() )
870  {
871  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
872  return;
873  }
874 
875  QDomText titleText = titleTextNode.toText();
876 
877  title = titleText.data();
878 
879 }
880 
881 QgsProjectVersion getVersion( const QDomDocument &doc )
882 {
883  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
884 
885  if ( !nl.count() )
886  {
887  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
888  return QgsProjectVersion( 0, 0, 0, QString() );
889  }
890 
891  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
892 
893  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
894  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
895  return projectVersion;
896 }
897 
898 
900 {
901  return mSnappingConfig;
902 }
903 
905 {
906  if ( mSnappingConfig == snappingConfig )
907  return;
908 
909  mSnappingConfig = snappingConfig;
910  setDirty( true );
911  emit snappingConfigChanged( mSnappingConfig );
912 }
913 
914 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, QgsProject::ReadFlags flags )
915 {
916  // Layer order is set by the restoring the legend settings from project file.
917  // This is done on the 'readProject( ... )' signal
918 
919  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
920 
921  // process the map layer nodes
922 
923  if ( 0 == nl.count() ) // if we have no layers to process, bail
924  {
925  return true; // Decided to return "true" since it's
926  // possible for there to be a project with no
927  // layers; but also, more imporantly, this
928  // would cause the tests/qgsproject to fail
929  // since the test suite doesn't currently
930  // support test layers
931  }
932 
933  bool returnStatus = true;
934 
935  emit layerLoaded( 0, nl.count() );
936 
937  // order layers based on their dependencies
938  QgsLayerDefinition::DependencySorter depSorter( doc );
939  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
940  return false;
941 
942  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
943 
944  int i = 0;
945  for ( const QDomNode &node : sortedLayerNodes )
946  {
947  const QDomElement element = node.toElement();
948 
949  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
950  if ( !name.isNull() )
951  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
952 
953  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
954  {
955  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, flags );
956  }
957  else
958  {
959  QgsReadWriteContext context;
960  context.setPathResolver( pathResolver() );
961  context.setProjectTranslator( this );
963 
964  if ( !addLayer( element, brokenNodes, context, flags ) )
965  {
966  returnStatus = false;
967  }
968  const auto messages = context.takeMessages();
969  if ( !messages.isEmpty() )
970  {
971  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
972  }
973  }
974  emit layerLoaded( i + 1, nl.count() );
975  i++;
976  }
977 
978  return returnStatus;
979 }
980 
981 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, QgsProject::ReadFlags flags )
982 {
983  QString type = layerElem.attribute( QStringLiteral( "type" ) );
984  QgsDebugMsgLevel( "Layer type is " + type, 4 );
985  std::unique_ptr<QgsMapLayer> mapLayer;
986 
987  if ( type == QLatin1String( "vector" ) )
988  {
989  mapLayer = qgis::make_unique<QgsVectorLayer>();
990  // apply specific settings to vector layer
991  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
992  {
993  vl->setReadExtentFromXml( mTrustLayerMetadata );
994  }
995  }
996  else if ( type == QLatin1String( "raster" ) )
997  {
998  mapLayer = qgis::make_unique<QgsRasterLayer>();
999  }
1000  else if ( type == QLatin1String( "mesh" ) )
1001  {
1002  mapLayer = qgis::make_unique<QgsMeshLayer>();
1003  }
1004  else if ( type == QLatin1String( "plugin" ) )
1005  {
1006  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1007  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1008  }
1009 
1010  if ( !mapLayer )
1011  {
1012  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1013  return false;
1014  }
1015 
1016  Q_CHECK_PTR( mapLayer ); // NOLINT
1017 
1018  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1019  // because if it was, the newly created layer will not be added to the store and it would leak.
1020  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1021  Q_ASSERT( ! layerId.isEmpty() );
1022  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1023 
1024  // have the layer restore state that is stored in Dom node
1025  QgsMapLayer::ReadFlags layerFlags = nullptr;
1026  if ( flags & QgsProject::FlagDontResolveLayers )
1027  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1028  bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1029  QList<QgsMapLayer *> newLayers;
1030  newLayers << mapLayer.get();
1031  if ( layerIsValid || flags & QgsProject::FlagDontResolveLayers )
1032  {
1033  emit readMapLayer( mapLayer.get(), layerElem );
1034  addMapLayers( newLayers );
1035  }
1036  else
1037  {
1038  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1039  addMapLayers( newLayers, false );
1040  newLayers.first();
1041  QgsDebugMsg( "Unable to load " + type + " layer" );
1042  brokenNodes.push_back( layerElem );
1043  }
1044 
1045  // It should be safe to delete the layer now if layer was stored, because all the store
1046  // had to to was to reset the data source in case the validity changed.
1047  if ( ! layerWasStored )
1048  {
1049  mapLayer.release();
1050  }
1051 
1052  return layerIsValid;
1053 }
1054 
1055 bool QgsProject::read( const QString &filename, QgsProject::ReadFlags flags )
1056 {
1057  mFile.setFileName( filename );
1058  mCachedHomePath.clear();
1059  mProjectScope.reset();
1060 
1061  return read( flags );
1062 }
1063 
1064 bool QgsProject::read( QgsProject::ReadFlags flags )
1065 {
1066  QString filename = mFile.fileName();
1067  bool returnValue;
1068 
1069  if ( QgsProjectStorage *storage = projectStorage() )
1070  {
1071  QTemporaryFile inDevice;
1072  if ( !inDevice.open() )
1073  {
1074  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1075  return false;
1076  }
1077 
1078  QgsReadWriteContext context;
1079  context.setProjectTranslator( this );
1080  if ( !storage->readProject( filename, &inDevice, context ) )
1081  {
1082  QString err = tr( "Unable to open %1" ).arg( filename );
1083  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1084  if ( !messages.isEmpty() )
1085  err += QStringLiteral( "\n\n" ) + messages.last().message();
1086  setError( err );
1087  return false;
1088  }
1089  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1090  }
1091  else
1092  {
1093  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1094  {
1095  returnValue = unzip( mFile.fileName(), flags );
1096  }
1097  else
1098  {
1099  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1100  returnValue = readProjectFile( mFile.fileName(), flags );
1101  }
1102 
1103  //on translation we should not change the filename back
1104  if ( !mTranslator )
1105  {
1106  mFile.setFileName( filename );
1107  mCachedHomePath.clear();
1108  mProjectScope.reset();
1109  }
1110  else
1111  {
1112  //but delete the translator
1113  mTranslator.reset( nullptr );
1114  }
1115  }
1116  emit homePathChanged();
1117  return returnValue;
1118 }
1119 
1120 bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags flags )
1121 {
1122  QFile projectFile( filename );
1123  clearError();
1124 
1125  QgsSettings settings;
1126 
1127  QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), settings.value( QStringLiteral( "locale/userLocale" ), QString() ).toString() );
1128 
1129  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1130  {
1131  mTranslator.reset( new QTranslator() );
1132  mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1133  }
1134 
1135  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1136 
1137  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1138  {
1139  projectFile.close();
1140 
1141  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1142 
1143  return false;
1144  }
1145 
1146  // location of problem associated with errorMsg
1147  int line, column;
1148  QString errorMsg;
1149 
1150  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1151  {
1152  // want to make this class as GUI independent as possible; so commented out
1153 #if 0
1154  QMessageBox::critical( 0, tr( "Read Project File" ),
1155  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
1156 #endif
1157 
1158  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1159  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1160 
1161  QgsDebugMsg( errorString );
1162 
1163  projectFile.close();
1164 
1165  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1166 
1167  return false;
1168  }
1169 
1170  projectFile.close();
1171 
1172  QgsDebugMsg( "Opened document " + projectFile.fileName() );
1173 
1174  // get project version string, if any
1175  QgsProjectVersion fileVersion = getVersion( *doc );
1176  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
1177 
1178  if ( thisVersion > fileVersion )
1179  {
1180  QgsLogger::warning( "Loading a file that was saved with an older "
1181  "version of qgis (saved in " + fileVersion.text() +
1182  ", loaded in " + Qgis::QGIS_VERSION +
1183  "). Problems may occur." );
1184 
1185  QgsProjectFileTransform projectFile( *doc, fileVersion );
1186 
1187  // Shows a warning when an old project file is read.
1188  emit oldProjectVersionWarning( fileVersion.text() );
1189 
1190  projectFile.updateRevision( thisVersion );
1191  }
1192 
1193  // start new project, just keep the file name and auxiliary storage
1194  QString fileName = mFile.fileName();
1195  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1196  clear();
1197  mAuxiliaryStorage = std::move( aStorage );
1198  mFile.setFileName( fileName );
1199  mCachedHomePath.clear();
1200  mProjectScope.reset();
1201 
1202  // now get any properties
1203  _getProperties( *doc, mProperties );
1204 
1205  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
1206 
1207 #if 0
1208  dump_( mProperties );
1209 #endif
1210 
1211  // get older style project title
1212  QString oldTitle;
1213  _getTitle( *doc, oldTitle );
1214 
1215  QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1216  if ( homePathNl.count() > 0 )
1217  {
1218  QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1219  QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1220  if ( !homePath.isEmpty() )
1221  setPresetHomePath( homePath );
1222  }
1223  else
1224  {
1225  emit homePathChanged();
1226  }
1227 
1228  QgsReadWriteContext context;
1229  context.setPathResolver( pathResolver() );
1230  context.setProjectTranslator( this );
1231 
1232  //crs
1233  QgsCoordinateReferenceSystem projectCrs;
1234  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1235  {
1236  // first preference - dedicated projectCrs node
1237  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1238  if ( !srsNode.isNull() )
1239  {
1240  projectCrs.readXml( srsNode );
1241  }
1242 
1243  if ( !projectCrs.isValid() )
1244  {
1245  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1246  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1247 
1248  // try the CRS
1249  if ( currentCRS >= 0 )
1250  {
1251  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1252  }
1253 
1254  // if that didn't produce a match, try the proj.4 string
1255  if ( !projCrsString.isEmpty() && ( !projectCrs.isValid() || projectCrs.toProj4() != projCrsString ) )
1256  {
1257  projectCrs = QgsCoordinateReferenceSystem::fromProj4( projCrsString );
1258  }
1259 
1260  // last just take the given id
1261  if ( !projectCrs.isValid() )
1262  {
1263  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1264  }
1265  }
1266  }
1267  mCrs = projectCrs;
1268 
1269  QStringList datumErrors;
1270  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1271  {
1272  emit missingDatumTransforms( datumErrors );
1273  }
1274  emit transformContextChanged();
1275 
1276  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "projectMetadata" ) );
1277  if ( !nl.isEmpty() )
1278  {
1279  QDomElement metadataElement = nl.at( 0 ).toElement();
1280  mMetadata.readMetadataXml( metadataElement );
1281  }
1282  else
1283  {
1284  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1285  mMetadata = QgsProjectMetadata();
1286  }
1287  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1288  {
1289  // upgrade older title storage to storing within project metadata.
1290  mMetadata.setTitle( oldTitle );
1291  }
1292  emit metadataChanged();
1293 
1294  nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
1295  if ( nl.count() )
1296  {
1297  QDomElement transactionElement = nl.at( 0 ).toElement();
1298  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1299  mAutoTransaction = true;
1300  }
1301 
1302  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
1303  if ( nl.count() )
1304  {
1305  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
1306  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1307  mEvaluateDefaultValues = true;
1308  }
1309 
1310  nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
1311  if ( nl.count() )
1312  {
1313  QDomElement trustElement = nl.at( 0 ).toElement();
1314  if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1315  mTrustLayerMetadata = true;
1316  }
1317 
1318  // read the layer tree from project file
1319 
1320  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1321 
1322  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1323  if ( !layerTreeElem.isNull() )
1324  {
1325  mRootGroup->readChildrenFromXml( layerTreeElem, context );
1326  }
1327  else
1328  {
1329  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1330  }
1331 
1332  mLayerTreeRegistryBridge->setEnabled( false );
1333 
1334  // get the map layers
1335  QList<QDomNode> brokenNodes;
1336  bool clean = _getMapLayers( *doc, brokenNodes, flags );
1337 
1338  // review the integrity of the retrieved map layers
1339  if ( !clean )
1340  {
1341  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1342 
1343  if ( !brokenNodes.isEmpty() )
1344  {
1345  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1346  }
1347 
1348  // we let a custom handler decide what to do with missing layers
1349  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1350  mBadLayerHandler->handleBadLayers( brokenNodes );
1351  }
1352 
1353  // Resolve references to other layers
1354  // Needs to be done here once all dependent layers are loaded
1355  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1356  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
1357  {
1358  it.value()->resolveReferences( this );
1359  }
1360 
1361  mLayerTreeRegistryBridge->setEnabled( true );
1362 
1363  // load embedded groups and layers
1364  loadEmbeddedNodes( mRootGroup, flags );
1365 
1366  // now that layers are loaded, we can resolve layer tree's references to the layers
1367  mRootGroup->resolveReferences( this );
1368 
1369 
1370  if ( !layerTreeElem.isNull() )
1371  {
1372  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1373  }
1374 
1375  // Load pre 3.0 configuration
1376  QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1377  if ( !layerTreeCanvasElem.isNull( ) )
1378  {
1379  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1380  }
1381 
1382  // Convert pre 3.4 to create layers flags
1383  if ( QgsProjectVersion( 3, 4, 0 ) > fileVersion )
1384  {
1385  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1386  for ( const QString &layerId : requiredLayerIds )
1387  {
1388  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1389  {
1390  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1391  }
1392  }
1393  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1394  for ( const QString &layerId : disabledLayerIds )
1395  {
1396  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1397  {
1398  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1399  }
1400  }
1401  }
1402 
1403  // After bad layer handling we might still have invalid layers,
1404  // store them in case the user wanted to handle them later
1405  // or wanted to pass them through when saving
1406  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1407 
1408  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1409 
1410  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1412  mMapThemeCollection->readXml( *doc );
1413 
1414  mLabelingEngineSettings->readSettingsFromProject( this );
1416 
1417  mAnnotationManager->readXml( doc->documentElement(), context );
1418  mLayoutManager->readXml( doc->documentElement(), *doc );
1419  mBookmarkManager->readXml( doc->documentElement(), *doc );
1420 
1421  // reassign change dependencies now that all layers are loaded
1422  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1423  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1424  {
1425  it.value()->setDependencies( it.value()->dependencies() );
1426  }
1427 
1428  mSnappingConfig.readProject( *doc );
1429  //add variables defined in project file
1430  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1431  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1432 
1433  mCustomVariables.clear();
1434  if ( variableNames.length() == variableValues.length() )
1435  {
1436  for ( int i = 0; i < variableNames.length(); ++i )
1437  {
1438  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1439  }
1440  }
1441  else
1442  {
1443  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1444  }
1445  emit customVariablesChanged();
1446  emit crsChanged();
1447  emit ellipsoidChanged( ellipsoid() );
1448 
1449  // read the project: used by map canvas and legend
1450  emit readProject( *doc );
1451  emit readProjectWithContext( *doc, context );
1452  emit snappingConfigChanged( mSnappingConfig );
1454  emit projectColorsChanged();
1455 
1456  // if all went well, we're allegedly in pristine state
1457  if ( clean )
1458  setDirty( false );
1459 
1463 
1464  if ( mTranslator )
1465  {
1466  //project possibly translated -> rename it with locale postfix
1467  QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
1468  setFileName( newFileName );
1469 
1470  if ( write() )
1471  {
1472  setTitle( localeFileName );
1473  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Success );
1474  }
1475  else
1476  {
1477  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Critical );
1478  }
1479  }
1480  return true;
1481 }
1482 
1483 
1484 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
1485 {
1486 
1487  const auto constChildren = group->children();
1488  for ( QgsLayerTreeNode *child : constChildren )
1489  {
1490  if ( QgsLayerTree::isGroup( child ) )
1491  {
1492  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1493  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1494  {
1495  // make sure to convert the path from relative to absolute
1496  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1497  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1498  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
1499  if ( newGroup )
1500  {
1501  QList<QgsLayerTreeNode *> clonedChildren;
1502  const auto constChildren = newGroup->children();
1503  for ( QgsLayerTreeNode *newGroupChild : constChildren )
1504  clonedChildren << newGroupChild->clone();
1505  delete newGroup;
1506 
1507  childGroup->insertChildNodes( 0, clonedChildren );
1508  }
1509  }
1510  else
1511  {
1512  loadEmbeddedNodes( childGroup, flags );
1513  }
1514  }
1515  else if ( QgsLayerTree::isLayer( child ) )
1516  {
1517  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1518  {
1519  QList<QDomNode> brokenNodes;
1520  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes, flags );
1521  }
1522  }
1523 
1524  }
1525 }
1526 
1527 QVariantMap QgsProject::customVariables() const
1528 {
1529  return mCustomVariables;
1530 }
1531 
1532 void QgsProject::setCustomVariables( const QVariantMap &variables )
1533 {
1534  if ( variables == mCustomVariables )
1535  return;
1536 
1537  //write variable to project
1538  QStringList variableNames;
1539  QStringList variableValues;
1540 
1541  QVariantMap::const_iterator it = variables.constBegin();
1542  for ( ; it != variables.constEnd(); ++it )
1543  {
1544  variableNames << it.key();
1545  variableValues << it.value().toString();
1546  }
1547 
1548  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1549  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1550 
1551  mCustomVariables = variables;
1552  mProjectScope.reset();
1553 
1554  emit customVariablesChanged();
1555 }
1556 
1558 {
1559  *mLabelingEngineSettings = settings;
1561 }
1562 
1564 {
1565  return *mLabelingEngineSettings;
1566 }
1567 
1569 {
1570  return mLayerStore.get();
1571 }
1572 
1574 {
1575  return mLayerStore.get();
1576 }
1577 
1578 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1579 {
1580  QList<QgsVectorLayer *> layers;
1581  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1582  const auto constLayerIds = layerIds;
1583  for ( const QString &layerId : constLayerIds )
1584  {
1585  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1586  layers << vlayer;
1587  }
1588  return layers;
1589 }
1590 
1591 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1592 {
1593  QStringList list;
1594  const auto constLayers = layers;
1595  for ( QgsVectorLayer *layer : constLayers )
1596  list << layer->id();
1597  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1599 }
1600 
1602 {
1603  QgsExpressionContext context;
1604 
1607 
1608  return context;
1609 }
1610 
1612 {
1613  // MUCH cheaper to clone than build
1614  if ( mProjectScope )
1615  {
1616  std::unique_ptr< QgsExpressionContextScope > projectScope = qgis::make_unique< QgsExpressionContextScope >( *mProjectScope );
1617  // we can't cache these
1618  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
1619  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
1620  return projectScope.release();
1621  }
1622 
1623  mProjectScope = qgis::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
1624 
1625  const QVariantMap vars = customVariables();
1626 
1627  QVariantMap::const_iterator it = vars.constBegin();
1628 
1629  for ( ; it != vars.constEnd(); ++it )
1630  {
1631  mProjectScope->setVariable( it.key(), it.value(), true );
1632  }
1633 
1634  QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
1635  QString projectFolder = QFileInfo( projectPath ).path();
1636  QString projectFilename = QFileInfo( projectPath ).fileName();
1637  QString projectBasename = baseName();
1638 
1639  //add other known project variables
1640  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
1641  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
1642  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
1643  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
1644  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
1645  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
1646  QgsCoordinateReferenceSystem projectCrs = crs();
1647  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
1648  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj4(), true, true ) );
1649  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
1650  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
1651 
1652  // metadata
1653  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
1654  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
1655  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
1656  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
1657 
1658  // keywords
1659  QVariantMap keywords;
1660  QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
1661  for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
1662  {
1663  keywords.insert( it.key(), it.value() );
1664  }
1665  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
1666 
1667  mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
1668 
1670 }
1671 
1672 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1673 {
1674  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1675 
1676  bool tgChanged = false;
1677 
1678  const auto constLayers = layers;
1679  for ( QgsMapLayer *layer : constLayers )
1680  {
1681  if ( layer->isValid() )
1682  {
1683  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1684  if ( vlayer )
1685  {
1686  if ( autoTransaction() )
1687  {
1688  if ( QgsTransaction::supportsTransaction( vlayer ) )
1689  {
1690  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1691  QString key = vlayer->providerType();
1692 
1693  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1694 
1695  if ( !tg )
1696  {
1697  tg = new QgsTransactionGroup();
1698  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1699  tgChanged = true;
1700  }
1701  tg->addLayer( vlayer );
1702  }
1703  }
1705  }
1706 
1707  if ( tgChanged )
1708  emit transactionGroupsChanged();
1709 
1710  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1711 
1712  // check if we have to update connections for layers with dependencies
1713  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1714  {
1715  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1716  if ( deps.contains( layer->id() ) )
1717  {
1718  // reconnect to change signals
1719  it.value()->setDependencies( deps );
1720  }
1721  }
1722  }
1723  }
1724 
1725  if ( mSnappingConfig.addLayers( layers ) )
1726  emit snappingConfigChanged( mSnappingConfig );
1727 }
1728 
1729 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1730 {
1731  if ( mSnappingConfig.removeLayers( layers ) )
1732  emit snappingConfigChanged( mSnappingConfig );
1733 }
1734 
1735 void QgsProject::cleanTransactionGroups( bool force )
1736 {
1737  bool changed = false;
1738  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1739  {
1740  if ( tg.value()->isEmpty() || force )
1741  {
1742  delete tg.value();
1743  tg = mTransactionGroups.erase( tg );
1744  changed = true;
1745  }
1746  else
1747  {
1748  ++tg;
1749  }
1750  }
1751  if ( changed )
1752  emit transactionGroupsChanged();
1753 }
1754 
1755 bool QgsProject::readLayer( const QDomNode &layerNode )
1756 {
1757  QgsReadWriteContext context;
1758  context.setPathResolver( pathResolver() );
1759  context.setProjectTranslator( this );
1761  QList<QDomNode> brokenNodes;
1762  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
1763  {
1764  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1765  // added layer for joins
1766  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1767  const auto constVectorLayers = vectorLayers;
1768  for ( QgsVectorLayer *layer : constVectorLayers )
1769  {
1770  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1771  layer->resolveReferences( this );
1772  }
1773 
1774  return true;
1775  }
1776  return false;
1777 }
1778 
1779 bool QgsProject::write( const QString &filename )
1780 {
1781  mFile.setFileName( filename );
1782  mCachedHomePath.clear();
1783  mProjectScope.reset();
1784 
1785  return write();
1786 }
1787 
1789 {
1790  if ( QgsProjectStorage *storage = projectStorage() )
1791  {
1792  QgsReadWriteContext context;
1793  // for projects stored in a custom storage, we have to check for the support
1794  // of relative paths since the storage most likely will not be in a file system
1795  QString storageFilePath { storage->filePath( mFile.fileName() ) };
1796  if ( storageFilePath.isEmpty() )
1797  {
1798  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
1799  }
1800  context.setPathResolver( pathResolver() );
1801 
1802  QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
1803  QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
1804 
1805  if ( !zip( tmpZipFilename ) )
1806  return false; // zip() already calls setError() when returning false
1807 
1808  QFile tmpZipFile( tmpZipFilename );
1809  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
1810  {
1811  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
1812  return false;
1813  }
1814 
1816  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
1817  {
1818  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
1819  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1820  if ( !messages.isEmpty() )
1821  err += QStringLiteral( "\n\n" ) + messages.last().message();
1822  setError( err );
1823  return false;
1824  }
1825 
1826  tmpZipFile.close();
1827  QFile::remove( tmpZipFilename );
1828 
1829  return true;
1830  }
1831 
1832  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1833  {
1834  return zip( mFile.fileName() );
1835  }
1836  else
1837  {
1838  // write project file even if the auxiliary storage is not correctly
1839  // saved
1840  const bool asOk = saveAuxiliaryStorage();
1841  const bool writeOk = writeProjectFile( mFile.fileName() );
1842 
1843  // errors raised during writing project file are more important
1844  if ( !asOk && writeOk )
1845  {
1846  const QString err = mAuxiliaryStorage->errorString();
1847  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
1848  }
1849 
1850  return asOk && writeOk;
1851  }
1852 }
1853 
1854 bool QgsProject::writeProjectFile( const QString &filename )
1855 {
1856  QFile projectFile( filename );
1857  clearError();
1858 
1859  // if we have problems creating or otherwise writing to the project file,
1860  // let's find out up front before we go through all the hand-waving
1861  // necessary to create all the Dom objects
1862  QFileInfo myFileInfo( projectFile );
1863  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1864  {
1865  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1866  .arg( projectFile.fileName() ) );
1867  return false;
1868  }
1869 
1870  QgsReadWriteContext context;
1871  context.setPathResolver( pathResolver() );
1873 
1874  QDomImplementation DomImplementation;
1875  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1876 
1877  QDomDocumentType documentType =
1878  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1879  QStringLiteral( "SYSTEM" ) );
1880  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1881 
1882  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1883  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1884  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1885 
1886  doc->appendChild( qgisNode );
1887 
1888  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
1889  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
1890  qgisNode.appendChild( homePathNode );
1891 
1892  // title
1893  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1894  qgisNode.appendChild( titleNode );
1895 
1896  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1897  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? 1 : 0 );
1898  qgisNode.appendChild( transactionNode );
1899 
1900  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1901  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? 1 : 0 );
1902  qgisNode.appendChild( evaluateDefaultValuesNode );
1903 
1904  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
1905  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? 1 : 0 );
1906  qgisNode.appendChild( trustNode );
1907 
1908  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1909  titleNode.appendChild( titleText );
1910 
1911  // write project CRS
1912  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
1913  mCrs.writeXml( srsNode, *doc );
1914  qgisNode.appendChild( srsNode );
1915 
1916  // write layer tree - make sure it is without embedded subgroups
1917  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1919  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
1920 
1921  clonedRoot->writeXml( qgisNode, context );
1922  delete clonedRoot;
1923 
1924  mSnappingConfig.writeProject( *doc );
1925 
1926  // let map canvas and legend write their information
1927  emit writeProject( *doc );
1928 
1929  // within top level node save list of layers
1930  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
1931 
1932  // Iterate over layers in zOrder
1933  // Call writeXml() on each
1934  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1935 
1936  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
1937  while ( li != layers.end() )
1938  {
1939  QgsMapLayer *ml = li.value();
1940 
1941  if ( ml )
1942  {
1943  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1944  if ( emIt == mEmbeddedLayers.constEnd() )
1945  {
1946  QDomElement maplayerElem;
1947  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
1948  // not available, just write what we DO have
1949  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
1950  {
1951  // general layer metadata
1952  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1953  ml->writeLayerXml( maplayerElem, *doc, context );
1954  }
1955  else if ( ! ml->originalXmlProperties().isEmpty() )
1956  {
1957  QDomDocument document;
1958  if ( document.setContent( ml->originalXmlProperties() ) )
1959  {
1960  maplayerElem = document.firstChildElement();
1961  }
1962  else
1963  {
1964  QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
1965  }
1966  }
1967 
1968  emit writeMapLayer( ml, maplayerElem, *doc );
1969 
1970  projectLayersNode.appendChild( maplayerElem );
1971  }
1972  else
1973  {
1974  // layer defined in an external project file
1975  // only save embedded layer if not managed by a legend group
1976  if ( emIt.value().second )
1977  {
1978  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1979  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
1980  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
1981  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
1982  projectLayersNode.appendChild( mapLayerElem );
1983  }
1984  }
1985  }
1986  li++;
1987  }
1988 
1989  qgisNode.appendChild( projectLayersNode );
1990 
1991  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
1992  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
1993  for ( QgsMapLayer *layer : constCustomLayerOrder )
1994  {
1995  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
1996  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
1997  layerOrderNode.appendChild( mapLayerElem );
1998  }
1999  qgisNode.appendChild( layerOrderNode );
2000 
2001  mLabelingEngineSettings->writeSettingsToProject( this );
2002 
2003  // now add the optional extra properties
2004 #if 0
2005  dump_( mProperties );
2006 #endif
2007 
2008  QgsDebugMsg( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
2009 
2010  if ( !mProperties.isEmpty() ) // only worry about properties if we
2011  // actually have any properties
2012  {
2013  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2014  }
2015 
2016  mMapThemeCollection->writeXml( *doc );
2017 
2018  mTransformContext.writeXml( qgisNode, context );
2019 
2020  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2021  mMetadata.writeMetadataXml( metadataElem, *doc );
2022  qgisNode.appendChild( metadataElem );
2023 
2024  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2025  qgisNode.appendChild( annotationsElem );
2026 
2027  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2028  qgisNode.appendChild( layoutElem );
2029 
2030  QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2031  qgisNode.appendChild( bookmarkElem );
2032 
2033  // now wrap it up and ship it to the project file
2034  doc->normalize(); // XXX I'm not entirely sure what this does
2035 
2036  // Create backup file
2037  if ( QFile::exists( fileName() ) )
2038  {
2039  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2040  bool ok = true;
2041  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2042  ok &= projectFile.open( QIODevice::ReadOnly );
2043 
2044  QByteArray ba;
2045  while ( ok && !projectFile.atEnd() )
2046  {
2047  ba = projectFile.read( 10240 );
2048  ok &= backupFile.write( ba ) == ba.size();
2049  }
2050 
2051  projectFile.close();
2052  backupFile.close();
2053 
2054  if ( !ok )
2055  {
2056  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2057  return false;
2058  }
2059 
2060  QFileInfo fi( fileName() );
2061  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2062  utime( backupFile.fileName().toUtf8().constData(), &tb );
2063  }
2064 
2065  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2066  {
2067  projectFile.close(); // even though we got an error, let's make
2068  // sure it's closed anyway
2069 
2070  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2071  return false;
2072  }
2073 
2074  QTemporaryFile tempFile;
2075  bool ok = tempFile.open();
2076  if ( ok )
2077  {
2078  QTextStream projectFileStream( &tempFile );
2079  doc->save( projectFileStream, 2 ); // save as utf-8
2080  ok &= projectFileStream.pos() > -1;
2081 
2082  ok &= tempFile.seek( 0 );
2083 
2084  QByteArray ba;
2085  while ( ok && !tempFile.atEnd() )
2086  {
2087  ba = tempFile.read( 10240 );
2088  ok &= projectFile.write( ba ) == ba.size();
2089  }
2090 
2091  ok &= projectFile.error() == QFile::NoError;
2092 
2093  projectFile.close();
2094  }
2095 
2096  tempFile.close();
2097 
2098  if ( !ok )
2099  {
2100  setError( tr( "Unable to save to file %1. Your project "
2101  "may be corrupted on disk. Try clearing some space on the volume and "
2102  "check file permissions before pressing save again." )
2103  .arg( projectFile.fileName() ) );
2104  return false;
2105  }
2106 
2107  setDirty( false ); // reset to pristine state
2108 
2109  emit projectSaved();
2110 
2111  return true;
2112 }
2113 
2114 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2115 {
2116  bool propertiesModified;
2117  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2118 
2119  if ( propertiesModified )
2120  setDirty( true );
2121 
2122  return success;
2123 }
2124 
2125 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2126 {
2127  bool propertiesModified;
2128  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2129 
2130  if ( propertiesModified )
2131  setDirty( true );
2132 
2133  return success;
2134 }
2135 
2136 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2137 {
2138  bool propertiesModified;
2139  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2140 
2141  if ( propertiesModified )
2142  setDirty( true );
2143 
2144  return success;
2145 }
2146 
2147 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2148 {
2149  bool propertiesModified;
2150  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2151 
2152  if ( propertiesModified )
2153  setDirty( true );
2154 
2155  return success;
2156 }
2157 
2158 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2159 {
2160  bool propertiesModified;
2161  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2162 
2163  if ( propertiesModified )
2164  setDirty( true );
2165 
2166  return success;
2167 }
2168 
2169 QStringList QgsProject::readListEntry( const QString &scope,
2170  const QString &key,
2171  const QStringList &def,
2172  bool *ok ) const
2173 {
2174  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2175 
2176  QVariant value;
2177 
2178  if ( property )
2179  {
2180  value = property->value();
2181 
2182  bool valid = QVariant::StringList == value.type();
2183  if ( ok )
2184  *ok = valid;
2185 
2186  if ( valid )
2187  {
2188  return value.toStringList();
2189  }
2190  }
2191 
2192  return def;
2193 }
2194 
2195 
2196 QString QgsProject::readEntry( const QString &scope,
2197  const QString &key,
2198  const QString &def,
2199  bool *ok ) const
2200 {
2201  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2202 
2203  QVariant value;
2204 
2205  if ( property )
2206  {
2207  value = property->value();
2208 
2209  bool valid = value.canConvert( QVariant::String );
2210  if ( ok )
2211  *ok = valid;
2212 
2213  if ( valid )
2214  return value.toString();
2215  }
2216 
2217  return def;
2218 }
2219 
2220 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2221  bool *ok ) const
2222 {
2223  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2224 
2225  QVariant value;
2226 
2227  if ( property )
2228  {
2229  value = property->value();
2230  }
2231 
2232  bool valid = value.canConvert( QVariant::Int );
2233 
2234  if ( ok )
2235  {
2236  *ok = valid;
2237  }
2238 
2239  if ( valid )
2240  {
2241  return value.toInt();
2242  }
2243 
2244  return def;
2245 }
2246 
2247 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2248  double def,
2249  bool *ok ) const
2250 {
2251  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2252  if ( property )
2253  {
2254  QVariant value = property->value();
2255 
2256  bool valid = value.canConvert( QVariant::Double );
2257  if ( ok )
2258  *ok = valid;
2259 
2260  if ( valid )
2261  return value.toDouble();
2262  }
2263 
2264  return def;
2265 }
2266 
2267 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
2268  bool *ok ) const
2269 {
2270  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2271 
2272  if ( property )
2273  {
2274  QVariant value = property->value();
2275 
2276  bool valid = value.canConvert( QVariant::Bool );
2277  if ( ok )
2278  *ok = valid;
2279 
2280  if ( valid )
2281  return value.toBool();
2282  }
2283 
2284  return def;
2285 }
2286 
2287 
2288 bool QgsProject::removeEntry( const QString &scope, const QString &key )
2289 {
2290  if ( findKey_( scope, key, mProperties ) )
2291  {
2292  removeKey_( scope, key, mProperties );
2293  setDirty( true );
2294  }
2295 
2296  return !findKey_( scope, key, mProperties );
2297 }
2298 
2299 
2300 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
2301 {
2302  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2303 
2304  QStringList entries;
2305 
2306  if ( foundProperty )
2307  {
2308  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2309 
2310  if ( propertyKey )
2311  { propertyKey->entryList( entries ); }
2312  }
2313 
2314  return entries;
2315 }
2316 
2317 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
2318 {
2319  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2320 
2321  QStringList entries;
2322 
2323  if ( foundProperty )
2324  {
2325  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2326 
2327  if ( propertyKey )
2328  { propertyKey->subkeyList( entries ); }
2329  }
2330 
2331  return entries;
2332 }
2333 
2335 {
2336  dump_( mProperties );
2337 }
2338 
2340 {
2341  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
2342  QString filePath;
2343  if ( ! absolutePaths )
2344  {
2345  // for projects stored in a custom storage, we need to ask to the
2346  // storage for the path, if the storage returns an empty path
2347  // relative paths are not supported
2348  if ( QgsProjectStorage *storage = projectStorage() )
2349  {
2350  filePath = storage->filePath( mFile.fileName() );
2351  }
2352  else
2353  {
2354  filePath = fileName();
2355  }
2356  }
2357  return QgsPathResolver( filePath );
2358 }
2359 
2360 QString QgsProject::readPath( const QString &src ) const
2361 {
2362  return pathResolver().readPath( src );
2363 }
2364 
2365 QString QgsProject::writePath( const QString &src ) const
2366 {
2367  return pathResolver().writePath( src );
2368 }
2369 
2370 void QgsProject::setError( const QString &errorMessage )
2371 {
2372  mErrorMessage = errorMessage;
2373 }
2374 
2375 QString QgsProject::error() const
2376 {
2377  return mErrorMessage;
2378 }
2379 
2380 void QgsProject::clearError()
2381 {
2382  setError( QString() );
2383 }
2384 
2386 {
2387  delete mBadLayerHandler;
2388  mBadLayerHandler = handler;
2389 }
2390 
2391 QString QgsProject::layerIsEmbedded( const QString &id ) const
2392 {
2393  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
2394  if ( it == mEmbeddedLayers.constEnd() )
2395  {
2396  return QString();
2397  }
2398  return it.value().first;
2399 }
2400 
2401 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
2402  bool saveFlag, QgsProject::ReadFlags flags )
2403 {
2404  QgsDebugCall;
2405 
2406  static QString sPrevProjectFilePath;
2407  static QDateTime sPrevProjectFileTimestamp;
2408  static QDomDocument sProjectDocument;
2409 
2410  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
2411 
2412  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
2413  {
2414  sPrevProjectFilePath.clear();
2415 
2416  QFile projectFile( projectFilePath );
2417  if ( !projectFile.open( QIODevice::ReadOnly ) )
2418  {
2419  return false;
2420  }
2421 
2422  if ( !sProjectDocument.setContent( &projectFile ) )
2423  {
2424  return false;
2425  }
2426 
2427  sPrevProjectFilePath = projectFilePath;
2428  sPrevProjectFileTimestamp = projectFileTimestamp;
2429  }
2430 
2431  // does project store paths absolute or relative?
2432  bool useAbsolutePaths = true;
2433 
2434  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
2435  if ( !propertiesElem.isNull() )
2436  {
2437  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
2438  if ( !absElem.isNull() )
2439  {
2440  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
2441  }
2442  }
2443 
2444  QgsReadWriteContext embeddedContext;
2445  if ( !useAbsolutePaths )
2446  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
2447  embeddedContext.setProjectTranslator( this );
2448  embeddedContext.setTransformContext( transformContext() );
2449 
2450  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
2451  if ( projectLayersElem.isNull() )
2452  {
2453  return false;
2454  }
2455 
2456  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
2457  for ( int i = 0; i < mapLayerNodes.size(); ++i )
2458  {
2459  // get layer id
2460  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
2461  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
2462  if ( id == layerId )
2463  {
2464  // layer can be embedded only once
2465  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
2466  {
2467  return false;
2468  }
2469 
2470  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
2471 
2472  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
2473  {
2474  return true;
2475  }
2476  else
2477  {
2478  mEmbeddedLayers.remove( layerId );
2479  return false;
2480  }
2481  }
2482  }
2483 
2484  return false;
2485 }
2486 
2487 
2488 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags )
2489 {
2490  // open project file, get layer ids in group, add the layers
2491  QFile projectFile( projectFilePath );
2492  if ( !projectFile.open( QIODevice::ReadOnly ) )
2493  {
2494  return nullptr;
2495  }
2496 
2497  QDomDocument projectDocument;
2498  if ( !projectDocument.setContent( &projectFile ) )
2499  {
2500  return nullptr;
2501  }
2502 
2503  QgsReadWriteContext context;
2504  context.setPathResolver( pathResolver() );
2505  context.setProjectTranslator( this );
2507 
2509 
2510  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2511  if ( !layerTreeElem.isNull() )
2512  {
2513  root->readChildrenFromXml( layerTreeElem, context );
2514  }
2515  else
2516  {
2517  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2518  }
2519 
2520  QgsLayerTreeGroup *group = root->findGroup( groupName );
2521  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
2522  {
2523  // embedded groups cannot be embedded again
2524  delete root;
2525  return nullptr;
2526  }
2527 
2528  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
2529  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
2530  delete root;
2531  root = nullptr;
2532 
2533  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2534  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2535 
2536  // set "embedded" to all children + load embedded layers
2537  mLayerTreeRegistryBridge->setEnabled( false );
2538  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
2539  mLayerTreeRegistryBridge->setEnabled( true );
2540 
2541  // consider the layers might be identify disabled in its project
2542  const auto constFindLayerIds = newGroup->findLayerIds();
2543  for ( const QString &layerId : constFindLayerIds )
2544  {
2545  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2546  if ( layer )
2547  {
2548  layer->resolveReferences( this );
2549  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
2550  }
2551  }
2552 
2553  return newGroup;
2554 }
2555 
2556 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
2557 {
2558  const auto constChildren = group->children();
2559  for ( QgsLayerTreeNode *child : constChildren )
2560  {
2561  // all nodes in the subtree will have "embedded" custom property set
2562  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2563 
2564  if ( QgsLayerTree::isGroup( child ) )
2565  {
2566  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
2567  }
2568  else if ( QgsLayerTree::isLayer( child ) )
2569  {
2570  // load the layer into our project
2571  QList<QDomNode> brokenNodes;
2572  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
2573  }
2574  }
2575 }
2576 
2578 {
2579  return mEvaluateDefaultValues;
2580 }
2581 
2583 {
2584  if ( evaluateDefaultValues == mEvaluateDefaultValues )
2585  return;
2586 
2587  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2588  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
2589  for ( ; layerIt != layers.constEnd(); ++layerIt )
2590  {
2591  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
2592  if ( vl )
2593  {
2595  }
2596  }
2597 
2598  mEvaluateDefaultValues = evaluateDefaultValues;
2599 }
2600 
2602 {
2603  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2605 }
2606 
2608 {
2609  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2610 }
2611 
2613 {
2614  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2615  if ( !distanceUnitString.isEmpty() )
2616  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2617 
2618  //fallback to QGIS default measurement unit
2619  QgsSettings s;
2620  bool ok = false;
2621  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2622  return ok ? type : QgsUnitTypes::DistanceMeters;
2623 }
2624 
2626 {
2627  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2628 }
2629 
2631 {
2632  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2633  if ( !areaUnitString.isEmpty() )
2634  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2635 
2636  //fallback to QGIS default area unit
2637  QgsSettings s;
2638  bool ok = false;
2639  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2640  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2641 }
2642 
2644 {
2645  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2646 }
2647 
2648 QString QgsProject::homePath() const
2649 {
2650  if ( !mCachedHomePath.isEmpty() )
2651  return mCachedHomePath;
2652 
2653  if ( !mHomePath.isEmpty() )
2654  {
2655  QFileInfo homeInfo( mHomePath );
2656  if ( !homeInfo.isRelative() )
2657  {
2658  mCachedHomePath = mHomePath;
2659  return mHomePath;
2660  }
2661  }
2662 
2663  QFileInfo pfi( fileName() );
2664  if ( !pfi.exists() )
2665  {
2666  mCachedHomePath = mHomePath;
2667  return mHomePath;
2668  }
2669 
2670  if ( !mHomePath.isEmpty() )
2671  {
2672  // path is relative to project file
2673  mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
2674  }
2675  else
2676  {
2677  mCachedHomePath = pfi.canonicalPath();
2678  }
2679  return mCachedHomePath;
2680 }
2681 
2683 {
2684  return mHomePath;
2685 }
2686 
2688 {
2689  return mRelationManager;
2690 }
2691 
2693 {
2694  return mLayoutManager.get();
2695 }
2696 
2698 {
2699  return mLayoutManager.get();
2700 }
2701 
2703 {
2704  return mBookmarkManager;
2705 }
2706 
2708 {
2709  return mBookmarkManager;
2710 }
2711 
2713 {
2714  return mRootGroup;
2715 }
2716 
2718 {
2719  return mMapThemeCollection.get();
2720 }
2721 
2723 {
2724  return mAnnotationManager.get();
2725 }
2726 
2728 {
2729  return mAnnotationManager.get();
2730 }
2731 
2732 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2733 {
2734  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
2735  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
2736  {
2737  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2738  continue;
2739 
2740  if ( layers.contains( it.value() ) )
2741  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
2742  else
2743  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
2744  }
2745 
2749 }
2750 
2751 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2752 {
2753  QList<QgsMapLayer *> nonIdentifiableLayers;
2754  nonIdentifiableLayers.reserve( layerIds.count() );
2755  for ( const QString &layerId : layerIds )
2756  {
2757  QgsMapLayer *layer = mapLayer( layerId );
2758  if ( layer )
2759  nonIdentifiableLayers << layer;
2760  }
2762  setNonIdentifiableLayers( nonIdentifiableLayers );
2764 }
2765 
2766 QStringList QgsProject::nonIdentifiableLayers() const
2767 {
2768  QStringList nonIdentifiableLayers;
2769 
2770  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2771  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
2772  {
2773  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2774  {
2775  nonIdentifiableLayers.append( it.value()->id() );
2776  }
2777  }
2778  return nonIdentifiableLayers;
2779 }
2780 
2782 {
2783  return mAutoTransaction;
2784 }
2785 
2787 {
2788  if ( autoTransaction != mAutoTransaction )
2789  {
2790  mAutoTransaction = autoTransaction;
2791 
2792  if ( autoTransaction )
2793  onMapLayersAdded( mapLayers().values() );
2794  else
2795  cleanTransactionGroups( true );
2796  }
2797 }
2798 
2799 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2800 {
2801  return mTransactionGroups;
2802 }
2803 
2804 
2805 //
2806 // QgsMapLayerStore methods
2807 //
2808 
2809 
2811 {
2812  return mLayerStore->count();
2813 }
2814 
2816 {
2817  return mLayerStore->validCount();
2818 }
2819 
2820 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
2821 {
2822  return mLayerStore->mapLayer( layerId );
2823 }
2824 
2825 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
2826 {
2827  return mLayerStore->mapLayersByName( layerName );
2828 }
2829 
2830 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
2831 {
2832  QList<QgsMapLayer *> layers;
2833  const auto constMapLayers { mLayerStore->mapLayers() };
2834  for ( const auto &l : constMapLayers )
2835  {
2836  if ( ! l->shortName().isEmpty() )
2837  {
2838  if ( l->shortName() == shortName )
2839  layers << l;
2840  }
2841  else if ( l->name() == shortName )
2842  {
2843  layers << l;
2844  }
2845  }
2846  return layers;
2847 }
2848 
2849 bool QgsProject::unzip( const QString &filename, QgsProject::ReadFlags flags )
2850 {
2851  clearError();
2852  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2853 
2854  // unzip the archive
2855  if ( !archive->unzip( filename ) )
2856  {
2857  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
2858  return false;
2859  }
2860 
2861  // test if zip provides a .qgs file
2862  if ( archive->projectFile().isEmpty() )
2863  {
2864  setError( tr( "Zip archive does not provide a project file" ) );
2865  return false;
2866  }
2867 
2868  // load auxiliary storage
2869  if ( !archive->auxiliaryStorageFile().isEmpty() )
2870  {
2871  // database file is already a copy as it's been unzipped. So we don't open
2872  // auxiliary storage in copy mode in this case
2873  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
2874  }
2875  else
2876  {
2877  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
2878  }
2879 
2880  // read the project file
2881  if ( ! readProjectFile( archive->projectFile(), flags ) )
2882  {
2883  setError( tr( "Cannot read unzipped qgs project file" ) );
2884  return false;
2885  }
2886 
2887  // keep the archive and remove the temporary .qgs file
2888  mArchive = std::move( archive );
2889  mArchive->clearProjectFile();
2890 
2891  return true;
2892 }
2893 
2894 bool QgsProject::zip( const QString &filename )
2895 {
2896  clearError();
2897 
2898  // save the current project in a temporary .qgs file
2899  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2900  const QString baseName = QFileInfo( filename ).baseName();
2901  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
2902  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
2903 
2904  bool writeOk = false;
2905  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2906  {
2907  writeOk = writeProjectFile( qgsFile.fileName() );
2908  qgsFile.close();
2909  }
2910 
2911  // stop here with an error message
2912  if ( ! writeOk )
2913  {
2914  setError( tr( "Unable to write temporary qgs file" ) );
2915  return false;
2916  }
2917 
2918  // save auxiliary storage
2919  const QFileInfo info( qgsFile );
2920  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
2921 
2922  if ( ! saveAuxiliaryStorage( asFileName ) )
2923  {
2924  const QString err = mAuxiliaryStorage->errorString();
2925  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2926  return false;
2927  }
2928 
2929  // create the archive
2930  archive->addFile( qgsFile.fileName() );
2931  archive->addFile( asFileName );
2932 
2933  // zip
2934  if ( !archive->zip( filename ) )
2935  {
2936  setError( tr( "Unable to perform zip" ) );
2937  return false;
2938  }
2939 
2940  return true;
2941 }
2942 
2944 {
2945  return QgsZipUtils::isZipFile( mFile.fileName() );
2946 }
2947 
2948 QList<QgsMapLayer *> QgsProject::addMapLayers(
2949  const QList<QgsMapLayer *> &layers,
2950  bool addToLegend,
2951  bool takeOwnership )
2952 {
2953  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
2954  if ( !myResultList.isEmpty() )
2955  {
2956  // Update transform context
2957  for ( auto &l : myResultList )
2958  {
2959  l->setTransformContext( transformContext() );
2960  }
2961  if ( addToLegend )
2962  {
2963  emit legendLayersAdded( myResultList );
2964  }
2965  }
2966 
2967  if ( mAuxiliaryStorage )
2968  {
2969  for ( QgsMapLayer *mlayer : myResultList )
2970  {
2971  if ( mlayer->type() != QgsMapLayerType::VectorLayer )
2972  continue;
2973 
2974  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
2975  if ( vl )
2976  {
2977  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
2978  }
2979  }
2980  }
2981 
2982  return myResultList;
2983 }
2984 
2985 QgsMapLayer *
2987  bool addToLegend,
2988  bool takeOwnership )
2989 {
2990  QList<QgsMapLayer *> addedLayers;
2991  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
2992  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
2993 }
2994 
2995 void QgsProject::removeMapLayers( const QStringList &layerIds )
2996 {
2997  mLayerStore->removeMapLayers( layerIds );
2998 }
2999 
3000 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3001 {
3002  mLayerStore->removeMapLayers( layers );
3003 }
3004 
3005 void QgsProject::removeMapLayer( const QString &layerId )
3006 {
3007  mLayerStore->removeMapLayer( layerId );
3008 }
3009 
3011 {
3012  mLayerStore->removeMapLayer( layer );
3013 }
3014 
3016 {
3017  return mLayerStore->takeMapLayer( layer );
3018 }
3019 
3021 {
3022  mLayerStore->removeAllMapLayers();
3023 }
3024 
3026 {
3027  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3028  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3029  for ( ; it != layers.constEnd(); ++it )
3030  {
3031  it.value()->reload();
3032  }
3033 }
3034 
3035 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3036 {
3037  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3038 }
3039 
3040 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3041 {
3042  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3043 }
3044 
3046 {
3047  QgsSettings settings;
3048  QgsCoordinateReferenceSystem defaultCrs;
3049  if ( settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "useProject" )
3050  || settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "prompt" ) )
3051  {
3052  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
3053  // (since "prompt" has no meaning here - the prompt will always be shown, it's just deciding on the default choice in the prompt!)
3054  defaultCrs = crs();
3055  }
3056  else
3057  {
3058  // global crs
3059  QString layerDefaultCrs = settings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), GEO_EPSG_CRS_AUTHID ).toString();
3060  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
3061  }
3062 
3063  return defaultCrs;
3064 }
3065 
3067 {
3068  mTrustLayerMetadata = trust;
3069 
3070  auto layers = mapLayers();
3071  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3072  {
3073  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3074  if ( vl )
3075  {
3076  vl->setReadExtentFromXml( trust );
3077  }
3078  }
3079 }
3080 
3081 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
3082 {
3083  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3084  bool empty = true;
3085  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3086  {
3087  if ( it.value()->type() != QgsMapLayerType::VectorLayer )
3088  continue;
3089 
3090  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3091  if ( vl && vl->auxiliaryLayer() )
3092  {
3093  vl->auxiliaryLayer()->save();
3094  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
3095  }
3096  }
3097 
3098  if ( !mAuxiliaryStorage->exists( *this ) && filename.isEmpty() && empty )
3099  {
3100  return true; // it's not an error
3101  }
3102  else if ( !filename.isEmpty() )
3103  {
3104  return mAuxiliaryStorage->saveAs( filename );
3105  }
3106  else
3107  {
3108  return mAuxiliaryStorage->saveAs( *this );
3109  }
3110 }
3111 
3113 {
3114  return mAuxiliaryStorage.get();
3115 }
3116 
3118 {
3119  return mAuxiliaryStorage.get();
3120 }
3121 
3123 {
3124  return mMetadata;
3125 }
3126 
3128 {
3129  if ( metadata == mMetadata )
3130  return;
3131 
3132  mMetadata = metadata;
3133  mProjectScope.reset();
3134 
3135  emit metadataChanged();
3136 
3137  setDirty( true );
3138 }
3139 
3140 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
3141 {
3142  QSet<QgsMapLayer *> requiredLayers;
3143 
3144  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3145  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3146  {
3147  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3148  {
3149  requiredLayers.insert( it.value() );
3150  }
3151  }
3152  return requiredLayers;
3153 }
3154 
3155 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
3156 {
3157  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3158  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3159  {
3160  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3161  continue;
3162 
3163  if ( layers.contains( it.value() ) )
3164  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
3165  else
3166  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
3167  }
3168 }
3169 
3171 {
3172  // save colors to project
3173  QStringList customColors;
3174  QStringList customColorLabels;
3175 
3176  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
3177  for ( ; colorIt != colors.constEnd(); ++colorIt )
3178  {
3179  QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
3180  QString label = ( *colorIt ).second;
3181  customColors.append( color );
3182  customColorLabels.append( label );
3183  }
3184  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
3185  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
3186  mProjectScope.reset();
3187  emit projectColorsChanged();
3188 }
3189 
3190 void QgsProject::generateTsFile( const QString &locale )
3191 {
3192  QgsTranslationContext translationContext;
3193  translationContext.setProject( this );
3194  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
3195 
3196  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
3197 
3198  translationContext.writeTsFile( locale );
3199 }
3200 
3201 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
3202 {
3203  if ( !mTranslator )
3204  {
3205  return sourceText;
3206  }
3207 
3208  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
3209 
3210  if ( result.isEmpty() )
3211  {
3212  return sourceText;
3213  }
3214  return result;
3215 }
3216 
3218 {
3219  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
3220  if ( !layers.empty() )
3221  {
3222  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3223  {
3224  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
3225  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3226  {
3227  if ( !( ( *it )->accept( visitor ) ) )
3228  return false;
3229 
3230  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3231  return false;
3232  }
3233  }
3234  }
3235 
3236  if ( !mLayoutManager->accept( visitor ) )
3237  return false;
3238 
3239  if ( !mAnnotationManager->accept( visitor ) )
3240  return false;
3241 
3242  return true;
3243 }
3244 
3246 GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
3247  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3248 {
3249  if ( !project )
3250  return;
3251 
3252  //build up color list from project. Do this in advance for speed
3253  QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
3254  QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
3255 
3256  //generate list from custom colors
3257  int colorIndex = 0;
3258  for ( QStringList::iterator it = colorStrings.begin();
3259  it != colorStrings.end(); ++it )
3260  {
3261  QColor color = QgsSymbolLayerUtils::decodeColor( *it );
3262  QString label;
3263  if ( colorLabels.length() > colorIndex )
3264  {
3265  label = colorLabels.at( colorIndex );
3266  }
3267 
3268  mColors.insert( label.toLower(), color );
3269  colorIndex++;
3270  }
3271 }
3272 
3273 GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
3274  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3275  , mColors( colors )
3276 {
3277 }
3278 
3279 QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3280 {
3281  QString colorName = values.at( 0 ).toString().toLower();
3282  if ( mColors.contains( colorName ) )
3283  {
3284  return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
3285  }
3286  else
3287  return QVariant();
3288 }
3289 
3290 QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
3291 {
3292  return new GetNamedProjectColor( mColors );
3293 }
Q_DECL_DEPRECATED QStringList nonIdentifiableLayers() const
Gets the list of layers which currently should not be taken into account on map identification.
Class for parsing and evaluation of expressions (formerly called "search strings").
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:472
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.
Single variable definition for use within a QgsExpressionContextScope.
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:467
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:604
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.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource...
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.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
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:782
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:277
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:904
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:569
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 addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
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:881
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:659
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:91
const QString GEO_EPSG_CRS_AUTHID
Geographic coord sys from EPSG authority.
Definition: qgis.cpp:70
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void setEllipsoid(const QString &ellipsoid)
Sets the project&#39;s ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:682
~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.
Single scope for storing variables and functions for use within a QgsExpressionContext.
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...
An expression node for expression functions.
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:599
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:516
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:618
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:66
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:484
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:821
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:594
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:498
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.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
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:697
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:462
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:640
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:629
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:92
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:819
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
Expression function for use within a QgsExpressionContextScope.
QString authid() const
Returns the authority identifier for the CRS.
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:712
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.
static QColor decodeColor(const QString &str)
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.