QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 <algorithm>
62 #include <QApplication>
63 #include <QFileInfo>
64 #include <QDomNode>
65 #include <QObject>
66 #include <QTextStream>
67 #include <QTemporaryFile>
68 #include <QDir>
69 #include <QUrl>
70 
71 
72 #ifdef _MSC_VER
73 #include <sys/utime.h>
74 #else
75 #include <utime.h>
76 #endif
77 
78 // canonical project instance
79 QgsProject *QgsProject::sProject = nullptr;
80 
89 QStringList makeKeyTokens_( const QString &scope, const QString &key )
90 {
91  QStringList keyTokens = QStringList( scope );
92  keyTokens += key.split( '/', QString::SkipEmptyParts );
93 
94  // be sure to include the canonical root node
95  keyTokens.push_front( QStringLiteral( "properties" ) );
96 
97  //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.
98  for ( int i = 0; i < keyTokens.size(); ++i )
99  {
100  QString keyToken = keyTokens.at( i );
101 
102  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
103  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
104  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]" );
105  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]" );
106 
107  if ( keyToken.contains( QRegExp( nameCharRegexp ) ) || keyToken.contains( QRegExp( nameStartCharRegexp ) ) )
108  {
109 
110  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
111  QgsMessageLog::logMessage( errorString, QString(), Qgis::Critical );
112 
113  }
114 
115  }
116 
117  return keyTokens;
118 }
119 
120 
121 
131 QgsProjectProperty *findKey_( const QString &scope,
132  const QString &key,
133  QgsProjectPropertyKey &rootProperty )
134 {
135  QgsProjectPropertyKey *currentProperty = &rootProperty;
136  QgsProjectProperty *nextProperty; // link to next property down hierarchy
137 
138  QStringList keySequence = makeKeyTokens_( scope, key );
139 
140  while ( !keySequence.isEmpty() )
141  {
142  // if the current head of the sequence list matches the property name,
143  // then traverse down the property hierarchy
144  if ( keySequence.first() == currentProperty->name() )
145  {
146  // remove front key since we're traversing down a level
147  keySequence.pop_front();
148 
149  if ( 1 == keySequence.count() )
150  {
151  // if we have only one key name left, then return the key found
152  return currentProperty->find( keySequence.front() );
153  }
154  else if ( keySequence.isEmpty() )
155  {
156  // if we're out of keys then the current property is the one we
157  // want; i.e., we're in the rate case of being at the top-most
158  // property node
159  return currentProperty;
160  }
161  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
162  {
163  if ( nextProperty->isKey() )
164  {
165  currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
166  }
167  else if ( nextProperty->isValue() && 1 == keySequence.count() )
168  {
169  // it may be that this may be one of several property value
170  // nodes keyed by QDict string; if this is the last remaining
171  // key token and the next property is a value node, then
172  // that's the situation, so return the currentProperty
173  return currentProperty;
174  }
175  else
176  {
177  // QgsProjectPropertyValue not Key, so return null
178  return nullptr;
179  }
180  }
181  else
182  {
183  // if the next key down isn't found
184  // then the overall key sequence doesn't exist
185  return nullptr;
186  }
187  }
188  else
189  {
190  return nullptr;
191  }
192  }
193 
194  return nullptr;
195 }
196 
197 
198 
208 QgsProjectProperty *addKey_( const QString &scope,
209  const QString &key,
210  QgsProjectPropertyKey *rootProperty,
211  const QVariant &value,
212  bool &propertiesModified )
213 {
214  QStringList keySequence = makeKeyTokens_( scope, key );
215 
216  // cursor through property key/value hierarchy
217  QgsProjectPropertyKey *currentProperty = rootProperty;
218  QgsProjectProperty *nextProperty; // link to next property down hierarchy
219  QgsProjectPropertyKey *newPropertyKey = nullptr;
220 
221  propertiesModified = false;
222  while ( ! keySequence.isEmpty() )
223  {
224  // if the current head of the sequence list matches the property name,
225  // then traverse down the property hierarchy
226  if ( keySequence.first() == currentProperty->name() )
227  {
228  // remove front key since we're traversing down a level
229  keySequence.pop_front();
230 
231  // if key sequence has one last element, then we use that as the
232  // name to store the value
233  if ( 1 == keySequence.count() )
234  {
235  QgsProjectProperty *property = currentProperty->find( keySequence.front() );
236  if ( !property || property->value() != value )
237  {
238  currentProperty->setValue( keySequence.front(), value );
239  propertiesModified = true;
240  }
241 
242  return currentProperty;
243  }
244  // we're at the top element if popping the keySequence element
245  // will leave it empty; in that case, just add the key
246  else if ( keySequence.isEmpty() )
247  {
248  if ( currentProperty->value() != value )
249  {
250  currentProperty->setValue( value );
251  propertiesModified = true;
252  }
253 
254  return currentProperty;
255  }
256  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
257  {
258  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
259 
260  if ( currentProperty )
261  {
262  continue;
263  }
264  else // QgsProjectPropertyValue not Key, so return null
265  {
266  return nullptr;
267  }
268  }
269  else // the next subkey doesn't exist, so add it
270  {
271  if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
272  {
273  currentProperty = newPropertyKey;
274  }
275  continue;
276  }
277  }
278  else
279  {
280  return nullptr;
281  }
282  }
283 
284  return nullptr;
285 }
286 
295 void removeKey_( const QString &scope,
296  const QString &key,
297  QgsProjectPropertyKey &rootProperty )
298 {
299  QgsProjectPropertyKey *currentProperty = &rootProperty;
300 
301  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
302  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
303 
304  QStringList keySequence = makeKeyTokens_( scope, key );
305 
306  while ( ! keySequence.isEmpty() )
307  {
308  // if the current head of the sequence list matches the property name,
309  // then traverse down the property hierarchy
310  if ( keySequence.first() == currentProperty->name() )
311  {
312  // remove front key since we're traversing down a level
313  keySequence.pop_front();
314 
315  // if we have only one key name left, then try to remove the key
316  // with that name
317  if ( 1 == keySequence.count() )
318  {
319  currentProperty->removeKey( keySequence.front() );
320  }
321  // if we're out of keys then the current property is the one we
322  // want to remove, but we can't delete it directly; we need to
323  // delete it from the parent property key container
324  else if ( keySequence.isEmpty() )
325  {
326  previousQgsPropertyKey->removeKey( currentProperty->name() );
327  }
328  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
329  {
330  previousQgsPropertyKey = currentProperty;
331  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
332 
333  if ( currentProperty )
334  {
335  continue;
336  }
337  else // QgsProjectPropertyValue not Key, so return null
338  {
339  return;
340  }
341  }
342  else // if the next key down isn't found
343  {
344  // then the overall key sequence doesn't exist
345  return;
346  }
347  }
348  else
349  {
350  return;
351  }
352  }
353 }
354 
355 QgsProject::QgsProject( QObject *parent )
356  : QObject( parent )
357  , mLayerStore( new QgsMapLayerStore( this ) )
358  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
359  , mSnappingConfig( this )
360  , mRelationManager( new QgsRelationManager( this ) )
361  , mAnnotationManager( new QgsAnnotationManager( this ) )
362  , mLayoutManager( new QgsLayoutManager( this ) )
363  , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
364  , mRootGroup( new QgsLayerTree )
365  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
366  , mArchive( new QgsProjectArchive() )
367  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
368 {
369  mProperties.setName( QStringLiteral( "properties" ) );
370  clear();
371 
372  // bind the layer tree to the map layer registry.
373  // whenever layers are added to or removed from the registry,
374  // layer tree will be updated
375  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
376  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
377  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
378  connect( this, qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
379 
380  // proxy map layer store signals to this
381  connect( mLayerStore.get(), qgis::overload<const QStringList &>::of( &QgsMapLayerStore::layersWillBeRemoved ),
382  this, qgis::overload< const QStringList &>::of( &QgsProject::layersWillBeRemoved ) );
383  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ),
384  this, qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsProject::layersWillBeRemoved ) );
385  connect( mLayerStore.get(), qgis::overload< const QString & >::of( &QgsMapLayerStore::layerWillBeRemoved ),
386  this, qgis::overload< const QString & >::of( &QgsProject::layerWillBeRemoved ) );
387  connect( mLayerStore.get(), qgis::overload< QgsMapLayer * >::of( &QgsMapLayerStore::layerWillBeRemoved ),
388  this, qgis::overload< QgsMapLayer * >::of( &QgsProject::layerWillBeRemoved ) );
389  connect( mLayerStore.get(), qgis::overload<const QStringList & >::of( &QgsMapLayerStore::layersRemoved ), this, &QgsProject::layersRemoved );
390  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this, &QgsProject::layerRemoved );
391  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this, &QgsProject::removeAll );
392  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this, &QgsProject::layersAdded );
393  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this, &QgsProject::layerWasAdded );
394 
395  if ( QgsApplication::instance() )
396  {
398  }
399 
400  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersWillBeRemoved ), this,
401  [ = ]( const QList<QgsMapLayer *> &layers )
402  {
403  for ( const auto &layer : layers )
404  {
405  disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
406  }
407  }
408  );
409  connect( mLayerStore.get(), qgis::overload< const QList<QgsMapLayer *> & >::of( &QgsMapLayerStore::layersAdded ), this,
410  [ = ]( const QList<QgsMapLayer *> &layers )
411  {
412  for ( const auto &layer : layers )
413  {
414  connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
415  }
416  }
417  );
418 }
419 
420 
422 {
423  mIsBeingDeleted = true;
424 
425  clear();
426  delete mBadLayerHandler;
427  delete mRelationManager;
428  delete mLayerTreeRegistryBridge;
429  delete mRootGroup;
430  if ( this == sProject )
431  {
432  sProject = nullptr;
433  }
434 }
435 
436 void QgsProject::setInstance( QgsProject *project )
437 {
438  sProject = project;
439 }
440 
441 
443 {
444  if ( !sProject )
445  {
446  sProject = new QgsProject;
447  }
448  return sProject;
449 }
450 
451 void QgsProject::setTitle( const QString &title )
452 {
453  if ( title == mMetadata.title() )
454  return;
455 
456  mMetadata.setTitle( title );
457  mProjectScope.reset();
458  emit metadataChanged();
459 
460  setDirty( true );
461 }
462 
463 QString QgsProject::title() const
464 {
465  return mMetadata.title();
466 }
467 
469 {
470  return mDirty;
471 }
472 
473 void QgsProject::setDirty( const bool dirty )
474 {
475  if ( dirty && mDirtyBlockCount > 0 )
476  return;
477 
478  if ( mDirty == dirty )
479  return;
480 
481  mDirty = dirty;
482  emit isDirtyChanged( mDirty );
483 }
484 
485 void QgsProject::setPresetHomePath( const QString &path )
486 {
487  if ( path == mHomePath )
488  return;
489 
490  mHomePath = path;
491  mCachedHomePath.clear();
492  mProjectScope.reset();
493 
494  emit homePathChanged();
495 
496  setDirty( true );
497 }
498 
499 void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
500 {
501  const QList<QgsAttributeEditorElement *> elements = parent->children();
502 
503  for ( QgsAttributeEditorElement *element : elements )
504  {
505  if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
506  {
507  QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
508 
509  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
510 
511  if ( !container->children().empty() )
512  registerTranslatableContainers( translationContext, container, layerId );
513  }
514  }
515 }
516 
518 {
519  //register layers
520  const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
521 
522  for ( const QgsLayerTreeLayer *layer : layers )
523  {
524  translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
525 
526  QgsMapLayer *mapLayer = layer->layer();
527  if ( mapLayer && mapLayer->type() == QgsMapLayerType::VectorLayer )
528  {
529  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
530 
531  //register aliases and fields
532  const QgsFields fields = vlayer->fields();
533  for ( const QgsField &field : fields )
534  {
535  QString fieldName;
536  if ( field.alias().isEmpty() )
537  fieldName = field.name();
538  else
539  fieldName = field.alias();
540 
541  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
542 
543  if ( field.editorWidgetSetup().type() == QStringLiteral( "ValueRelation" ) )
544  {
545  translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
546  }
547  }
548 
549  //register formcontainers
550  registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
551 
552  }
553  }
554 
555  //register layergroups
556  const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
557  for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
558  {
559  translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
560  }
561 
562  //register relations
563  const QList<QgsRelation> &relations = mRelationManager->relations().values();
564  for ( const QgsRelation &relation : relations )
565  {
566  translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
567  }
568 }
569 
570 void QgsProject::setFileName( const QString &name )
571 {
572  if ( name == mFile.fileName() )
573  return;
574 
575  QString oldHomePath = homePath();
576 
577  mFile.setFileName( name );
578  mCachedHomePath.clear();
579  mProjectScope.reset();
580 
581  emit fileNameChanged();
582 
583  QString newHomePath = homePath();
584  if ( newHomePath != oldHomePath )
585  emit homePathChanged();
586 
587  setDirty( true );
588 }
589 
590 QString QgsProject::fileName() const
591 {
592  return mFile.fileName();
593 }
594 
595 QFileInfo QgsProject::fileInfo() const
596 {
597  return QFileInfo( mFile );
598 }
599 
601 {
603 }
604 
605 QDateTime QgsProject::lastModified() const
606 {
607  if ( QgsProjectStorage *storage = projectStorage() )
608  {
610  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
611  return metadata.lastModified;
612  }
613  else
614  {
615  return QFileInfo( mFile.fileName() ).lastModified();
616  }
617 }
618 
620 {
621  if ( projectStorage() )
622  return QString();
623 
624  if ( mFile.fileName().isEmpty() )
625  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
626 
627  return QFileInfo( mFile.fileName() ).absolutePath();
628 }
629 
631 {
632  if ( projectStorage() )
633  return QString();
634 
635  if ( mFile.fileName().isEmpty() )
636  return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
637 
638  return QFileInfo( mFile.fileName() ).absoluteFilePath();
639 }
640 
641 QString QgsProject::baseName() const
642 {
643  if ( QgsProjectStorage *storage = projectStorage() )
644  {
646  storage->readProjectStorageMetadata( mFile.fileName(), metadata );
647  return metadata.name;
648  }
649  else
650  {
651  return QFileInfo( mFile.fileName() ).completeBaseName();
652  }
653 }
654 
656 {
657  return mCrs;
658 }
659 
660 void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
661 {
662  if ( crs != mCrs )
663  {
664  mCrs = crs;
665  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
666  mProjectScope.reset();
667  setDirty( true );
668  emit crsChanged();
669  }
670 
671  if ( adjustEllipsoid )
673 }
674 
675 QString QgsProject::ellipsoid() const
676 {
677  if ( !crs().isValid() )
678  return GEO_NONE;
679 
680  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), GEO_NONE );
681 }
682 
683 void QgsProject::setEllipsoid( const QString &ellipsoid )
684 {
685  if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
686  return;
687 
688  mProjectScope.reset();
689  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
690  emit ellipsoidChanged( ellipsoid );
691 }
692 
694 {
695  return mTransformContext;
696 }
697 
699 {
700  if ( context == mTransformContext )
701  return;
702 
703  mTransformContext = context;
704  mProjectScope.reset();
705 
706  for ( auto &layer : mLayerStore.get()->mapLayers() )
707  {
708  layer->setTransformContext( context );
709  }
711 }
712 
714 {
715  QgsSettings settings;
716 
717  mProjectScope.reset();
718  mFile.setFileName( QString() );
719  mProperties.clearKeys();
720  mHomePath.clear();
721  mCachedHomePath.clear();
722  mAutoTransaction = false;
723  mEvaluateDefaultValues = false;
724  mDirty = false;
725  mTrustLayerMetadata = false;
726  mCustomVariables.clear();
727  mMetadata = QgsProjectMetadata();
728  if ( !settings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
729  {
730  mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
732  }
733  emit metadataChanged();
734 
736  context.readSettings();
737  setTransformContext( context );
738 
739  mEmbeddedLayers.clear();
740  mRelationManager->clear();
741  mAnnotationManager->clear();
742  mLayoutManager->clear();
743  mBookmarkManager->clear();
744  mSnappingConfig.reset();
745  emit snappingConfigChanged( mSnappingConfig );
747 
748  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
750 
751  mLabelingEngineSettings->clear();
752 
753  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
754  mArchive->clear();
755 
757 
758  if ( !mIsBeingDeleted )
759  {
760  // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
761  emit projectColorsChanged();
762  }
763 
764  // reset some default project properties
765  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
766  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
767  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
768  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
769 
770  //copy default units to project
771  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), settings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
772  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), settings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
773 
774  int red = settings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
775  int green = settings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
776  int blue = settings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
777  setBackgroundColor( QColor( red, green, blue ) );
778 
779  red = settings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
780  green = settings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
781  blue = settings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
782  int alpha = settings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
783  setSelectionColor( QColor( red, green, blue, alpha ) );
784 
786  mRootGroup->clear();
787 
788  setDirty( false );
789  emit homePathChanged();
790  emit cleared();
791 }
792 
793 // basically a debugging tool to dump property list values
794 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
795 {
796  QgsDebugMsg( QStringLiteral( "current properties:" ) );
797  topQgsPropertyKey.dump();
798 }
799 
800 
831 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
832 {
833  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
834 
835  if ( propertiesElem.isNull() ) // no properties found, so we're done
836  {
837  return;
838  }
839 
840  QDomNodeList scopes = propertiesElem.childNodes();
841 
842  if ( scopes.count() < 1 )
843  {
844  QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
845  return;
846  }
847 
848  if ( ! project_properties.readXml( propertiesElem ) )
849  {
850  QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
851  }
852 }
853 
854 
859 static void _getTitle( const QDomDocument &doc, QString &title )
860 {
861  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
862 
863  title.clear(); // by default the title will be empty
864 
865  if ( !nl.count() )
866  {
867  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
868  return;
869  }
870 
871  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element OK
872 
873  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
874  {
875  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
876  return;
877  }
878 
879  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
880 
881  if ( !titleTextNode.isText() )
882  {
883  QgsDebugMsg( QStringLiteral( "unable to find title element" ) );
884  return;
885  }
886 
887  QDomText titleText = titleTextNode.toText();
888 
889  title = titleText.data();
890 
891 }
892 
893 QgsProjectVersion getVersion( const QDomDocument &doc )
894 {
895  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
896 
897  if ( !nl.count() )
898  {
899  QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
900  return QgsProjectVersion( 0, 0, 0, QString() );
901  }
902 
903  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
904 
905  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
906  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
907  return projectVersion;
908 }
909 
910 
912 {
913  return mSnappingConfig;
914 }
915 
917 {
918  if ( mSnappingConfig == snappingConfig )
919  return;
920 
921  mSnappingConfig = snappingConfig;
922  setDirty( true );
923  emit snappingConfigChanged( mSnappingConfig );
924 }
925 
926 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, QgsProject::ReadFlags flags )
927 {
928  // Layer order is set by the restoring the legend settings from project file.
929  // This is done on the 'readProject( ... )' signal
930 
931  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
932 
933  // process the map layer nodes
934 
935  if ( 0 == nl.count() ) // if we have no layers to process, bail
936  {
937  return true; // Decided to return "true" since it's
938  // possible for there to be a project with no
939  // layers; but also, more imporantly, this
940  // would cause the tests/qgsproject to fail
941  // since the test suite doesn't currently
942  // support test layers
943  }
944 
945  bool returnStatus = true;
946 
947  emit layerLoaded( 0, nl.count() );
948 
949  // order layers based on their dependencies
950  QgsLayerDefinition::DependencySorter depSorter( doc );
951  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
952  return false;
953 
954  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
955  const int totalLayerCount = sortedLayerNodes.count();
956 
957  int i = 0;
958  for ( const QDomNode &node : sortedLayerNodes )
959  {
960  const QDomElement element = node.toElement();
961 
962  const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
963  if ( !name.isNull() )
964  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
965 
966  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
967  {
968  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, flags );
969  }
970  else
971  {
972  QgsReadWriteContext context;
973  context.setPathResolver( pathResolver() );
974  context.setProjectTranslator( this );
976 
977  if ( !addLayer( element, brokenNodes, context, flags ) )
978  {
979  returnStatus = false;
980  }
981  const auto messages = context.takeMessages();
982  if ( !messages.isEmpty() )
983  {
984  emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
985  }
986  }
987  emit layerLoaded( i + 1, totalLayerCount );
988  i++;
989  }
990 
991  return returnStatus;
992 }
993 
994 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, QgsProject::ReadFlags flags )
995 {
996  QString type = layerElem.attribute( QStringLiteral( "type" ) );
997  QgsDebugMsgLevel( "Layer type is " + type, 4 );
998  std::unique_ptr<QgsMapLayer> mapLayer;
999 
1000  if ( type == QLatin1String( "vector" ) )
1001  {
1002  mapLayer = qgis::make_unique<QgsVectorLayer>();
1003  // apply specific settings to vector layer
1004  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1005  {
1006  vl->setReadExtentFromXml( mTrustLayerMetadata );
1007  }
1008  }
1009  else if ( type == QLatin1String( "raster" ) )
1010  {
1011  mapLayer = qgis::make_unique<QgsRasterLayer>();
1012  }
1013  else if ( type == QLatin1String( "mesh" ) )
1014  {
1015  mapLayer = qgis::make_unique<QgsMeshLayer>();
1016  }
1017  else if ( type == QLatin1String( "plugin" ) )
1018  {
1019  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1020  mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1021  }
1022 
1023  if ( !mapLayer )
1024  {
1025  QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1026  return false;
1027  }
1028 
1029  Q_CHECK_PTR( mapLayer ); // NOLINT
1030 
1031  // This is tricky: to avoid a leak we need to check if the layer was already in the store
1032  // because if it was, the newly created layer will not be added to the store and it would leak.
1033  const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1034  Q_ASSERT( ! layerId.isEmpty() );
1035  const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1036 
1037  // have the layer restore state that is stored in Dom node
1038  QgsMapLayer::ReadFlags layerFlags = nullptr;
1039  if ( flags & QgsProject::FlagDontResolveLayers )
1040  layerFlags |= QgsMapLayer::FlagDontResolveLayers;
1041  bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1042  QList<QgsMapLayer *> newLayers;
1043  newLayers << mapLayer.get();
1044  if ( layerIsValid || flags & QgsProject::FlagDontResolveLayers )
1045  {
1046  emit readMapLayer( mapLayer.get(), layerElem );
1047  addMapLayers( newLayers );
1048  }
1049  else
1050  {
1051  // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1052  addMapLayers( newLayers, false );
1053  newLayers.first();
1054  QgsDebugMsg( "Unable to load " + type + " layer" );
1055  brokenNodes.push_back( layerElem );
1056  }
1057 
1058  // It should be safe to delete the layer now if layer was stored, because all the store
1059  // had to to was to reset the data source in case the validity changed.
1060  if ( ! layerWasStored )
1061  {
1062  mapLayer.release();
1063  }
1064 
1065  return layerIsValid;
1066 }
1067 
1068 bool QgsProject::read( const QString &filename, QgsProject::ReadFlags flags )
1069 {
1070  mFile.setFileName( filename );
1071  mCachedHomePath.clear();
1072  mProjectScope.reset();
1073 
1074  return read( flags );
1075 }
1076 
1077 bool QgsProject::read( QgsProject::ReadFlags flags )
1078 {
1079  QString filename = mFile.fileName();
1080  bool returnValue;
1081 
1082  if ( QgsProjectStorage *storage = projectStorage() )
1083  {
1084  QTemporaryFile inDevice;
1085  if ( !inDevice.open() )
1086  {
1087  setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1088  return false;
1089  }
1090 
1091  QgsReadWriteContext context;
1092  context.setProjectTranslator( this );
1093  if ( !storage->readProject( filename, &inDevice, context ) )
1094  {
1095  QString err = tr( "Unable to open %1" ).arg( filename );
1096  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1097  if ( !messages.isEmpty() )
1098  err += QStringLiteral( "\n\n" ) + messages.last().message();
1099  setError( err );
1100  return false;
1101  }
1102  returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1103  }
1104  else
1105  {
1106  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1107  {
1108  returnValue = unzip( mFile.fileName(), flags );
1109  }
1110  else
1111  {
1112  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1113  returnValue = readProjectFile( mFile.fileName(), flags );
1114  }
1115 
1116  //on translation we should not change the filename back
1117  if ( !mTranslator )
1118  {
1119  mFile.setFileName( filename );
1120  mCachedHomePath.clear();
1121  mProjectScope.reset();
1122  }
1123  else
1124  {
1125  //but delete the translator
1126  mTranslator.reset( nullptr );
1127  }
1128  }
1129  emit homePathChanged();
1130  return returnValue;
1131 }
1132 
1133 bool QgsProject::readProjectFile( const QString &filename, QgsProject::ReadFlags flags )
1134 {
1135  QFile projectFile( filename );
1136  clearError();
1137 
1138  QgsSettings settings;
1139 
1140  QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), settings.value( QStringLiteral( "locale/userLocale" ), QString() ).toString() );
1141 
1142  if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1143  {
1144  mTranslator.reset( new QTranslator() );
1145  mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1146  }
1147 
1148  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1149 
1150  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1151  {
1152  projectFile.close();
1153 
1154  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1155 
1156  return false;
1157  }
1158 
1159  // location of problem associated with errorMsg
1160  int line, column;
1161  QString errorMsg;
1162 
1163  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1164  {
1165  // want to make this class as GUI independent as possible; so commented out
1166 #if 0
1167  QMessageBox::critical( 0, tr( "Read Project File" ),
1168  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
1169 #endif
1170 
1171  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1172  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1173 
1174  QgsDebugMsg( errorString );
1175 
1176  projectFile.close();
1177 
1178  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1179 
1180  return false;
1181  }
1182 
1183  projectFile.close();
1184 
1185  QgsDebugMsg( "Opened document " + projectFile.fileName() );
1186 
1187  // get project version string, if any
1188  QgsProjectVersion fileVersion = getVersion( *doc );
1189  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
1190 
1191  if ( thisVersion > fileVersion )
1192  {
1193  QgsLogger::warning( "Loading a file that was saved with an older "
1194  "version of qgis (saved in " + fileVersion.text() +
1195  ", loaded in " + Qgis::QGIS_VERSION +
1196  "). Problems may occur." );
1197 
1198  QgsProjectFileTransform projectFile( *doc, fileVersion );
1199 
1200  // Shows a warning when an old project file is read.
1201  emit oldProjectVersionWarning( fileVersion.text() );
1202 
1203  projectFile.updateRevision( thisVersion );
1204  }
1205 
1206  // start new project, just keep the file name and auxiliary storage
1207  QString fileName = mFile.fileName();
1208  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1209  clear();
1210  mAuxiliaryStorage = std::move( aStorage );
1211  mFile.setFileName( fileName );
1212  mCachedHomePath.clear();
1213  mProjectScope.reset();
1214 
1215  // now get any properties
1216  _getProperties( *doc, mProperties );
1217 
1218  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
1219 
1220 #if 0
1221  dump_( mProperties );
1222 #endif
1223 
1224  // get older style project title
1225  QString oldTitle;
1226  _getTitle( *doc, oldTitle );
1227 
1228  QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1229  if ( homePathNl.count() > 0 )
1230  {
1231  QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1232  QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1233  if ( !homePath.isEmpty() )
1234  setPresetHomePath( homePath );
1235  }
1236  else
1237  {
1238  emit homePathChanged();
1239  }
1240 
1241  const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1242  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1243  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1244  setBackgroundColor( backgroundColor );
1245  const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1246  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1247  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1248  readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1249  setSelectionColor( selectionColor );
1250 
1251  QgsReadWriteContext context;
1252  context.setPathResolver( pathResolver() );
1253  context.setProjectTranslator( this );
1254 
1255  //crs
1256  QgsCoordinateReferenceSystem projectCrs;
1257  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1258  {
1259  // first preference - dedicated projectCrs node
1260  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1261  if ( !srsNode.isNull() )
1262  {
1263  projectCrs.readXml( srsNode );
1264  }
1265 
1266  if ( !projectCrs.isValid() )
1267  {
1268  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1269  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1270 
1271  // try the CRS
1272  if ( currentCRS >= 0 )
1273  {
1274  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1275  }
1276 
1277  // if that didn't produce a match, try the proj.4 string
1278  if ( !projCrsString.isEmpty() && ( !projectCrs.isValid() || projectCrs.toProj4() != projCrsString ) )
1279  {
1280  projectCrs = QgsCoordinateReferenceSystem::fromProj4( projCrsString );
1281  }
1282 
1283  // last just take the given id
1284  if ( !projectCrs.isValid() )
1285  {
1286  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1287  }
1288  }
1289  }
1290  mCrs = projectCrs;
1291 
1292  QStringList datumErrors;
1293  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1294  {
1295  emit missingDatumTransforms( datumErrors );
1296  }
1297  emit transformContextChanged();
1298 
1299  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "projectMetadata" ) );
1300  if ( !nl.isEmpty() )
1301  {
1302  QDomElement metadataElement = nl.at( 0 ).toElement();
1303  mMetadata.readMetadataXml( metadataElement );
1304  }
1305  else
1306  {
1307  // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1308  mMetadata = QgsProjectMetadata();
1309  }
1310  if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1311  {
1312  // upgrade older title storage to storing within project metadata.
1313  mMetadata.setTitle( oldTitle );
1314  }
1315  emit metadataChanged();
1316 
1317  nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
1318  if ( nl.count() )
1319  {
1320  QDomElement transactionElement = nl.at( 0 ).toElement();
1321  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1322  mAutoTransaction = true;
1323  }
1324 
1325  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
1326  if ( nl.count() )
1327  {
1328  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
1329  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1330  mEvaluateDefaultValues = true;
1331  }
1332 
1333  nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
1334  if ( nl.count() )
1335  {
1336  QDomElement trustElement = nl.at( 0 ).toElement();
1337  if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
1338  mTrustLayerMetadata = true;
1339  }
1340 
1341  // read the layer tree from project file
1342 
1343  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1344 
1345  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1346  if ( !layerTreeElem.isNull() )
1347  {
1348  mRootGroup->readChildrenFromXml( layerTreeElem, context );
1349  }
1350  else
1351  {
1352  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1353  }
1354 
1355  mLayerTreeRegistryBridge->setEnabled( false );
1356 
1357  // get the map layers
1358  QList<QDomNode> brokenNodes;
1359  bool clean = _getMapLayers( *doc, brokenNodes, flags );
1360 
1361  // review the integrity of the retrieved map layers
1362  if ( !clean )
1363  {
1364  QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1365 
1366  if ( !brokenNodes.isEmpty() )
1367  {
1368  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1369  }
1370 
1371  // we let a custom handler decide what to do with missing layers
1372  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1373  mBadLayerHandler->handleBadLayers( brokenNodes );
1374  }
1375 
1376  // Resolve references to other layers
1377  // Needs to be done here once all dependent layers are loaded
1378  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1379  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
1380  {
1381  it.value()->resolveReferences( this );
1382  }
1383 
1384  mLayerTreeRegistryBridge->setEnabled( true );
1385 
1386  // load embedded groups and layers
1387  loadEmbeddedNodes( mRootGroup, flags );
1388 
1389  // now that layers are loaded, we can resolve layer tree's references to the layers
1390  mRootGroup->resolveReferences( this );
1391 
1392 
1393  if ( !layerTreeElem.isNull() )
1394  {
1395  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1396  }
1397 
1398  // Load pre 3.0 configuration
1399  QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1400  if ( !layerTreeCanvasElem.isNull( ) )
1401  {
1402  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1403  }
1404 
1405  // Convert pre 3.4 to create layers flags
1406  if ( QgsProjectVersion( 3, 4, 0 ) > fileVersion )
1407  {
1408  const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1409  for ( const QString &layerId : requiredLayerIds )
1410  {
1411  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1412  {
1413  layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1414  }
1415  }
1416  const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1417  for ( const QString &layerId : disabledLayerIds )
1418  {
1419  if ( QgsMapLayer *layer = mapLayer( layerId ) )
1420  {
1421  layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1422  }
1423  }
1424  }
1425 
1426  // After bad layer handling we might still have invalid layers,
1427  // store them in case the user wanted to handle them later
1428  // or wanted to pass them through when saving
1429  QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1430 
1431  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1432 
1433  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1435  mMapThemeCollection->readXml( *doc );
1436 
1437  mLabelingEngineSettings->readSettingsFromProject( this );
1439 
1440  mAnnotationManager->readXml( doc->documentElement(), context );
1441  if ( !( flags & QgsProject::FlagDontLoadLayouts ) )
1442  mLayoutManager->readXml( doc->documentElement(), *doc );
1443  mBookmarkManager->readXml( doc->documentElement(), *doc );
1444 
1445  // reassign change dependencies now that all layers are loaded
1446  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1447  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1448  {
1449  it.value()->setDependencies( it.value()->dependencies() );
1450  }
1451 
1452  mSnappingConfig.readProject( *doc );
1453  //add variables defined in project file
1454  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1455  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1456 
1457  mCustomVariables.clear();
1458  if ( variableNames.length() == variableValues.length() )
1459  {
1460  for ( int i = 0; i < variableNames.length(); ++i )
1461  {
1462  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1463  }
1464  }
1465  else
1466  {
1467  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1468  }
1469  emit customVariablesChanged();
1470  emit crsChanged();
1471  emit ellipsoidChanged( ellipsoid() );
1472 
1473  // read the project: used by map canvas and legend
1474  emit readProject( *doc );
1475  emit readProjectWithContext( *doc, context );
1476  emit snappingConfigChanged( mSnappingConfig );
1478  emit projectColorsChanged();
1479 
1480  // if all went well, we're allegedly in pristine state
1481  if ( clean )
1482  setDirty( false );
1483 
1487 
1488  if ( mTranslator )
1489  {
1490  //project possibly translated -> rename it with locale postfix
1491  QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
1492  setFileName( newFileName );
1493 
1494  if ( write() )
1495  {
1496  setTitle( localeFileName );
1497  QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Success );
1498  }
1499  else
1500  {
1501  QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::Critical );
1502  }
1503  }
1504  return true;
1505 }
1506 
1507 
1508 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
1509 {
1510 
1511  const auto constChildren = group->children();
1512  for ( QgsLayerTreeNode *child : constChildren )
1513  {
1514  if ( QgsLayerTree::isGroup( child ) )
1515  {
1516  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1517  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1518  {
1519  // make sure to convert the path from relative to absolute
1520  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1521  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1522  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
1523  if ( newGroup )
1524  {
1525  QList<QgsLayerTreeNode *> clonedChildren;
1526  const auto constChildren = newGroup->children();
1527  for ( QgsLayerTreeNode *newGroupChild : constChildren )
1528  clonedChildren << newGroupChild->clone();
1529  delete newGroup;
1530 
1531  childGroup->insertChildNodes( 0, clonedChildren );
1532  }
1533  }
1534  else
1535  {
1536  loadEmbeddedNodes( childGroup, flags );
1537  }
1538  }
1539  else if ( QgsLayerTree::isLayer( child ) )
1540  {
1541  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1542  {
1543  QList<QDomNode> brokenNodes;
1544  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes, flags );
1545  }
1546  }
1547 
1548  }
1549 }
1550 
1551 QVariantMap QgsProject::customVariables() const
1552 {
1553  return mCustomVariables;
1554 }
1555 
1556 void QgsProject::setCustomVariables( const QVariantMap &variables )
1557 {
1558  if ( variables == mCustomVariables )
1559  return;
1560 
1561  //write variable to project
1562  QStringList variableNames;
1563  QStringList variableValues;
1564 
1565  QVariantMap::const_iterator it = variables.constBegin();
1566  for ( ; it != variables.constEnd(); ++it )
1567  {
1568  variableNames << it.key();
1569  variableValues << it.value().toString();
1570  }
1571 
1572  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1573  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1574 
1575  mCustomVariables = variables;
1576  mProjectScope.reset();
1577 
1578  emit customVariablesChanged();
1579 }
1580 
1582 {
1583  *mLabelingEngineSettings = settings;
1585 }
1586 
1588 {
1589  return *mLabelingEngineSettings;
1590 }
1591 
1593 {
1594  return mLayerStore.get();
1595 }
1596 
1598 {
1599  return mLayerStore.get();
1600 }
1601 
1602 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1603 {
1604  QList<QgsVectorLayer *> layers;
1605  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1606  const auto constLayerIds = layerIds;
1607  for ( const QString &layerId : constLayerIds )
1608  {
1609  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1610  layers << vlayer;
1611  }
1612  return layers;
1613 }
1614 
1615 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1616 {
1617  QStringList list;
1618  const auto constLayers = layers;
1619  for ( QgsVectorLayer *layer : constLayers )
1620  list << layer->id();
1621  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1623 }
1624 
1626 {
1627  QgsExpressionContext context;
1628 
1631 
1632  return context;
1633 }
1634 
1636 {
1637  // MUCH cheaper to clone than build
1638  if ( mProjectScope )
1639  {
1640  std::unique_ptr< QgsExpressionContextScope > projectScope = qgis::make_unique< QgsExpressionContextScope >( *mProjectScope );
1641  // we can't cache these
1642  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
1643  projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
1644  return projectScope.release();
1645  }
1646 
1647  mProjectScope = qgis::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
1648 
1649  const QVariantMap vars = customVariables();
1650 
1651  QVariantMap::const_iterator it = vars.constBegin();
1652 
1653  for ( ; it != vars.constEnd(); ++it )
1654  {
1655  mProjectScope->setVariable( it.key(), it.value(), true );
1656  }
1657 
1658  QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
1659  QString projectFolder = QFileInfo( projectPath ).path();
1660  QString projectFilename = QFileInfo( projectPath ).fileName();
1661  QString projectBasename = baseName();
1662 
1663  //add other known project variables
1664  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
1665  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
1666  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
1667  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
1668  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
1669  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
1670  QgsCoordinateReferenceSystem projectCrs = crs();
1671  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
1672  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj4(), true, true ) );
1673  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
1674  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
1675 
1676  // metadata
1677  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
1678  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
1679  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
1680  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
1681 
1682  // keywords
1683  QVariantMap keywords;
1684  QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
1685  for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
1686  {
1687  keywords.insert( it.key(), it.value() );
1688  }
1689  mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
1690 
1691  mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
1692 
1694 }
1695 
1696 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1697 {
1698  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1699 
1700  bool tgChanged = false;
1701 
1702  const auto constLayers = layers;
1703  for ( QgsMapLayer *layer : constLayers )
1704  {
1705  if ( layer->isValid() )
1706  {
1707  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1708  if ( vlayer )
1709  {
1710  if ( autoTransaction() )
1711  {
1712  if ( QgsTransaction::supportsTransaction( vlayer ) )
1713  {
1714  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1715  QString key = vlayer->providerType();
1716 
1717  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1718 
1719  if ( !tg )
1720  {
1721  tg = new QgsTransactionGroup();
1722  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1723  tgChanged = true;
1724  }
1725  tg->addLayer( vlayer );
1726  }
1727  }
1729  }
1730 
1731  if ( tgChanged )
1732  emit transactionGroupsChanged();
1733 
1734  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1735 
1736  // check if we have to update connections for layers with dependencies
1737  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1738  {
1739  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1740  if ( deps.contains( layer->id() ) )
1741  {
1742  // reconnect to change signals
1743  it.value()->setDependencies( deps );
1744  }
1745  }
1746  }
1747  }
1748 
1749  if ( mSnappingConfig.addLayers( layers ) )
1750  emit snappingConfigChanged( mSnappingConfig );
1751 }
1752 
1753 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1754 {
1755  if ( mSnappingConfig.removeLayers( layers ) )
1756  emit snappingConfigChanged( mSnappingConfig );
1757 }
1758 
1759 void QgsProject::cleanTransactionGroups( bool force )
1760 {
1761  bool changed = false;
1762  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1763  {
1764  if ( tg.value()->isEmpty() || force )
1765  {
1766  delete tg.value();
1767  tg = mTransactionGroups.erase( tg );
1768  changed = true;
1769  }
1770  else
1771  {
1772  ++tg;
1773  }
1774  }
1775  if ( changed )
1776  emit transactionGroupsChanged();
1777 }
1778 
1779 bool QgsProject::readLayer( const QDomNode &layerNode )
1780 {
1781  QgsReadWriteContext context;
1782  context.setPathResolver( pathResolver() );
1783  context.setProjectTranslator( this );
1785  QList<QDomNode> brokenNodes;
1786  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
1787  {
1788  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1789  // added layer for joins
1790  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1791  const auto constVectorLayers = vectorLayers;
1792  for ( QgsVectorLayer *layer : constVectorLayers )
1793  {
1794  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1795  layer->resolveReferences( this );
1796  }
1797 
1798  return true;
1799  }
1800  return false;
1801 }
1802 
1803 bool QgsProject::write( const QString &filename )
1804 {
1805  mFile.setFileName( filename );
1806  mCachedHomePath.clear();
1807  mProjectScope.reset();
1808 
1809  return write();
1810 }
1811 
1813 {
1814  if ( QgsProjectStorage *storage = projectStorage() )
1815  {
1816  QgsReadWriteContext context;
1817  // for projects stored in a custom storage, we have to check for the support
1818  // of relative paths since the storage most likely will not be in a file system
1819  QString storageFilePath { storage->filePath( mFile.fileName() ) };
1820  if ( storageFilePath.isEmpty() )
1821  {
1822  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
1823  }
1824  context.setPathResolver( pathResolver() );
1825 
1826  QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
1827  QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
1828 
1829  if ( !zip( tmpZipFilename ) )
1830  return false; // zip() already calls setError() when returning false
1831 
1832  QFile tmpZipFile( tmpZipFilename );
1833  if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
1834  {
1835  setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
1836  return false;
1837  }
1838 
1840  if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
1841  {
1842  QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
1843  QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1844  if ( !messages.isEmpty() )
1845  err += QStringLiteral( "\n\n" ) + messages.last().message();
1846  setError( err );
1847  return false;
1848  }
1849 
1850  tmpZipFile.close();
1851  QFile::remove( tmpZipFilename );
1852 
1853  return true;
1854  }
1855 
1856  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1857  {
1858  return zip( mFile.fileName() );
1859  }
1860  else
1861  {
1862  // write project file even if the auxiliary storage is not correctly
1863  // saved
1864  const bool asOk = saveAuxiliaryStorage();
1865  const bool writeOk = writeProjectFile( mFile.fileName() );
1866 
1867  // errors raised during writing project file are more important
1868  if ( !asOk && writeOk )
1869  {
1870  const QString err = mAuxiliaryStorage->errorString();
1871  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
1872  }
1873 
1874  return asOk && writeOk;
1875  }
1876 }
1877 
1878 bool QgsProject::writeProjectFile( const QString &filename )
1879 {
1880  QFile projectFile( filename );
1881  clearError();
1882 
1883  // if we have problems creating or otherwise writing to the project file,
1884  // let's find out up front before we go through all the hand-waving
1885  // necessary to create all the Dom objects
1886  QFileInfo myFileInfo( projectFile );
1887  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1888  {
1889  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1890  .arg( projectFile.fileName() ) );
1891  return false;
1892  }
1893 
1894  QgsReadWriteContext context;
1895  context.setPathResolver( pathResolver() );
1897 
1898  QDomImplementation DomImplementation;
1899  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1900 
1901  QDomDocumentType documentType =
1902  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1903  QStringLiteral( "SYSTEM" ) );
1904  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1905 
1906  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1907  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1908  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1909 
1910  doc->appendChild( qgisNode );
1911 
1912  QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
1913  homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
1914  qgisNode.appendChild( homePathNode );
1915 
1916  // title
1917  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1918  qgisNode.appendChild( titleNode );
1919 
1920  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1921  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? 1 : 0 );
1922  qgisNode.appendChild( transactionNode );
1923 
1924  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1925  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? 1 : 0 );
1926  qgisNode.appendChild( evaluateDefaultValuesNode );
1927 
1928  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
1929  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? 1 : 0 );
1930  qgisNode.appendChild( trustNode );
1931 
1932  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1933  titleNode.appendChild( titleText );
1934 
1935  // write project CRS
1936  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
1937  mCrs.writeXml( srsNode, *doc );
1938  qgisNode.appendChild( srsNode );
1939 
1940  // write layer tree - make sure it is without embedded subgroups
1941  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1943  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
1944 
1945  clonedRoot->writeXml( qgisNode, context );
1946  delete clonedRoot;
1947 
1948  mSnappingConfig.writeProject( *doc );
1949 
1950  // let map canvas and legend write their information
1951  emit writeProject( *doc );
1952 
1953  // within top level node save list of layers
1954  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
1955 
1956  // Iterate over layers in zOrder
1957  // Call writeXml() on each
1958  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1959 
1960  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
1961  while ( li != layers.end() )
1962  {
1963  QgsMapLayer *ml = li.value();
1964 
1965  if ( ml )
1966  {
1967  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1968  if ( emIt == mEmbeddedLayers.constEnd() )
1969  {
1970  QDomElement maplayerElem;
1971  // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
1972  // not available, just write what we DO have
1973  if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
1974  {
1975  // general layer metadata
1976  maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1977  ml->writeLayerXml( maplayerElem, *doc, context );
1978  }
1979  else if ( ! ml->originalXmlProperties().isEmpty() )
1980  {
1981  QDomDocument document;
1982  if ( document.setContent( ml->originalXmlProperties() ) )
1983  {
1984  maplayerElem = document.firstChildElement();
1985  }
1986  else
1987  {
1988  QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
1989  }
1990  }
1991 
1992  emit writeMapLayer( ml, maplayerElem, *doc );
1993 
1994  projectLayersNode.appendChild( maplayerElem );
1995  }
1996  else
1997  {
1998  // layer defined in an external project file
1999  // only save embedded layer if not managed by a legend group
2000  if ( emIt.value().second )
2001  {
2002  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2003  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
2004  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
2005  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
2006  projectLayersNode.appendChild( mapLayerElem );
2007  }
2008  }
2009  }
2010  li++;
2011  }
2012 
2013  qgisNode.appendChild( projectLayersNode );
2014 
2015  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
2016  const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
2017  for ( QgsMapLayer *layer : constCustomLayerOrder )
2018  {
2019  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
2020  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
2021  layerOrderNode.appendChild( mapLayerElem );
2022  }
2023  qgisNode.appendChild( layerOrderNode );
2024 
2025  mLabelingEngineSettings->writeSettingsToProject( this );
2026 
2027  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
2028  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
2029  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
2030 
2031  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
2032  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
2033  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
2034  writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
2035 
2036  // now add the optional extra properties
2037 #if 0
2038  dump_( mProperties );
2039 #endif
2040 
2041  QgsDebugMsg( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
2042 
2043  if ( !mProperties.isEmpty() ) // only worry about properties if we
2044  // actually have any properties
2045  {
2046  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2047  }
2048 
2049  mMapThemeCollection->writeXml( *doc );
2050 
2051  mTransformContext.writeXml( qgisNode, context );
2052 
2053  QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2054  mMetadata.writeMetadataXml( metadataElem, *doc );
2055  qgisNode.appendChild( metadataElem );
2056 
2057  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2058  qgisNode.appendChild( annotationsElem );
2059 
2060  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2061  qgisNode.appendChild( layoutElem );
2062 
2063  QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2064  qgisNode.appendChild( bookmarkElem );
2065 
2066  // now wrap it up and ship it to the project file
2067  doc->normalize(); // XXX I'm not entirely sure what this does
2068 
2069  // Create backup file
2070  if ( QFile::exists( fileName() ) )
2071  {
2072  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2073  bool ok = true;
2074  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2075  ok &= projectFile.open( QIODevice::ReadOnly );
2076 
2077  QByteArray ba;
2078  while ( ok && !projectFile.atEnd() )
2079  {
2080  ba = projectFile.read( 10240 );
2081  ok &= backupFile.write( ba ) == ba.size();
2082  }
2083 
2084  projectFile.close();
2085  backupFile.close();
2086 
2087  if ( !ok )
2088  {
2089  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2090  return false;
2091  }
2092 
2093  QFileInfo fi( fileName() );
2094  struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2095  utime( backupFile.fileName().toUtf8().constData(), &tb );
2096  }
2097 
2098  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2099  {
2100  projectFile.close(); // even though we got an error, let's make
2101  // sure it's closed anyway
2102 
2103  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2104  return false;
2105  }
2106 
2107  QTemporaryFile tempFile;
2108  bool ok = tempFile.open();
2109  if ( ok )
2110  {
2111  QTextStream projectFileStream( &tempFile );
2112  doc->save( projectFileStream, 2 ); // save as utf-8
2113  ok &= projectFileStream.pos() > -1;
2114 
2115  ok &= tempFile.seek( 0 );
2116 
2117  QByteArray ba;
2118  while ( ok && !tempFile.atEnd() )
2119  {
2120  ba = tempFile.read( 10240 );
2121  ok &= projectFile.write( ba ) == ba.size();
2122  }
2123 
2124  ok &= projectFile.error() == QFile::NoError;
2125 
2126  projectFile.close();
2127  }
2128 
2129  tempFile.close();
2130 
2131  if ( !ok )
2132  {
2133  setError( tr( "Unable to save to file %1. Your project "
2134  "may be corrupted on disk. Try clearing some space on the volume and "
2135  "check file permissions before pressing save again." )
2136  .arg( projectFile.fileName() ) );
2137  return false;
2138  }
2139 
2140  setDirty( false ); // reset to pristine state
2141 
2142  emit projectSaved();
2143 
2144  return true;
2145 }
2146 
2147 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool 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, double 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 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2170 {
2171  bool propertiesModified;
2172  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2173 
2174  if ( propertiesModified )
2175  setDirty( true );
2176 
2177  return success;
2178 }
2179 
2180 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2181 {
2182  bool propertiesModified;
2183  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2184 
2185  if ( propertiesModified )
2186  setDirty( true );
2187 
2188  return success;
2189 }
2190 
2191 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2192 {
2193  bool propertiesModified;
2194  bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2195 
2196  if ( propertiesModified )
2197  setDirty( true );
2198 
2199  return success;
2200 }
2201 
2202 QStringList QgsProject::readListEntry( const QString &scope,
2203  const QString &key,
2204  const QStringList &def,
2205  bool *ok ) const
2206 {
2207  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2208 
2209  QVariant value;
2210 
2211  if ( property )
2212  {
2213  value = property->value();
2214 
2215  bool valid = QVariant::StringList == value.type();
2216  if ( ok )
2217  *ok = valid;
2218 
2219  if ( valid )
2220  {
2221  return value.toStringList();
2222  }
2223  }
2224 
2225  return def;
2226 }
2227 
2228 
2229 QString QgsProject::readEntry( const QString &scope,
2230  const QString &key,
2231  const QString &def,
2232  bool *ok ) const
2233 {
2234  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2235 
2236  QVariant value;
2237 
2238  if ( property )
2239  {
2240  value = property->value();
2241 
2242  bool valid = value.canConvert( QVariant::String );
2243  if ( ok )
2244  *ok = valid;
2245 
2246  if ( valid )
2247  return value.toString();
2248  }
2249 
2250  return def;
2251 }
2252 
2253 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2254  bool *ok ) const
2255 {
2256  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2257 
2258  QVariant value;
2259 
2260  if ( property )
2261  {
2262  value = property->value();
2263  }
2264 
2265  bool valid = value.canConvert( QVariant::Int );
2266 
2267  if ( ok )
2268  {
2269  *ok = valid;
2270  }
2271 
2272  if ( valid )
2273  {
2274  return value.toInt();
2275  }
2276 
2277  return def;
2278 }
2279 
2280 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2281  double def,
2282  bool *ok ) const
2283 {
2284  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2285  if ( property )
2286  {
2287  QVariant value = property->value();
2288 
2289  bool valid = value.canConvert( QVariant::Double );
2290  if ( ok )
2291  *ok = valid;
2292 
2293  if ( valid )
2294  return value.toDouble();
2295  }
2296 
2297  return def;
2298 }
2299 
2300 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
2301  bool *ok ) const
2302 {
2303  QgsProjectProperty *property = findKey_( scope, key, mProperties );
2304 
2305  if ( property )
2306  {
2307  QVariant value = property->value();
2308 
2309  bool valid = value.canConvert( QVariant::Bool );
2310  if ( ok )
2311  *ok = valid;
2312 
2313  if ( valid )
2314  return value.toBool();
2315  }
2316 
2317  return def;
2318 }
2319 
2320 
2321 bool QgsProject::removeEntry( const QString &scope, const QString &key )
2322 {
2323  if ( findKey_( scope, key, mProperties ) )
2324  {
2325  removeKey_( scope, key, mProperties );
2326  setDirty( true );
2327  }
2328 
2329  return !findKey_( scope, key, mProperties );
2330 }
2331 
2332 
2333 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
2334 {
2335  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2336 
2337  QStringList entries;
2338 
2339  if ( foundProperty )
2340  {
2341  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2342 
2343  if ( propertyKey )
2344  { propertyKey->entryList( entries ); }
2345  }
2346 
2347  return entries;
2348 }
2349 
2350 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
2351 {
2352  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
2353 
2354  QStringList entries;
2355 
2356  if ( foundProperty )
2357  {
2358  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
2359 
2360  if ( propertyKey )
2361  { propertyKey->subkeyList( entries ); }
2362  }
2363 
2364  return entries;
2365 }
2366 
2368 {
2369  dump_( mProperties );
2370 }
2371 
2373 {
2374  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
2375  QString filePath;
2376  if ( ! absolutePaths )
2377  {
2378  // for projects stored in a custom storage, we need to ask to the
2379  // storage for the path, if the storage returns an empty path
2380  // relative paths are not supported
2381  if ( QgsProjectStorage *storage = projectStorage() )
2382  {
2383  filePath = storage->filePath( mFile.fileName() );
2384  }
2385  else
2386  {
2387  filePath = fileName();
2388  }
2389  }
2390  return QgsPathResolver( filePath );
2391 }
2392 
2393 QString QgsProject::readPath( const QString &src ) const
2394 {
2395  return pathResolver().readPath( src );
2396 }
2397 
2398 QString QgsProject::writePath( const QString &src ) const
2399 {
2400  return pathResolver().writePath( src );
2401 }
2402 
2403 void QgsProject::setError( const QString &errorMessage )
2404 {
2405  mErrorMessage = errorMessage;
2406 }
2407 
2408 QString QgsProject::error() const
2409 {
2410  return mErrorMessage;
2411 }
2412 
2413 void QgsProject::clearError()
2414 {
2415  setError( QString() );
2416 }
2417 
2419 {
2420  delete mBadLayerHandler;
2421  mBadLayerHandler = handler;
2422 }
2423 
2424 QString QgsProject::layerIsEmbedded( const QString &id ) const
2425 {
2426  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
2427  if ( it == mEmbeddedLayers.constEnd() )
2428  {
2429  return QString();
2430  }
2431  return it.value().first;
2432 }
2433 
2434 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
2435  bool saveFlag, QgsProject::ReadFlags flags )
2436 {
2437  QgsDebugCall;
2438 
2439  static QString sPrevProjectFilePath;
2440  static QDateTime sPrevProjectFileTimestamp;
2441  static QDomDocument sProjectDocument;
2442 
2443  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
2444 
2445  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
2446  {
2447  sPrevProjectFilePath.clear();
2448 
2449  QFile projectFile( projectFilePath );
2450  if ( !projectFile.open( QIODevice::ReadOnly ) )
2451  {
2452  return false;
2453  }
2454 
2455  if ( !sProjectDocument.setContent( &projectFile ) )
2456  {
2457  return false;
2458  }
2459 
2460  sPrevProjectFilePath = projectFilePath;
2461  sPrevProjectFileTimestamp = projectFileTimestamp;
2462  }
2463 
2464  // does project store paths absolute or relative?
2465  bool useAbsolutePaths = true;
2466 
2467  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
2468  if ( !propertiesElem.isNull() )
2469  {
2470  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
2471  if ( !absElem.isNull() )
2472  {
2473  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
2474  }
2475  }
2476 
2477  QgsReadWriteContext embeddedContext;
2478  if ( !useAbsolutePaths )
2479  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
2480  embeddedContext.setProjectTranslator( this );
2481  embeddedContext.setTransformContext( transformContext() );
2482 
2483  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
2484  if ( projectLayersElem.isNull() )
2485  {
2486  return false;
2487  }
2488 
2489  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
2490  for ( int i = 0; i < mapLayerNodes.size(); ++i )
2491  {
2492  // get layer id
2493  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
2494  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
2495  if ( id == layerId )
2496  {
2497  // layer can be embedded only once
2498  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
2499  {
2500  return false;
2501  }
2502 
2503  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
2504 
2505  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
2506  {
2507  return true;
2508  }
2509  else
2510  {
2511  mEmbeddedLayers.remove( layerId );
2512  return false;
2513  }
2514  }
2515  }
2516 
2517  return false;
2518 }
2519 
2520 
2521 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, QgsProject::ReadFlags flags )
2522 {
2523  // open project file, get layer ids in group, add the layers
2524  QFile projectFile( projectFilePath );
2525  if ( !projectFile.open( QIODevice::ReadOnly ) )
2526  {
2527  return nullptr;
2528  }
2529 
2530  QDomDocument projectDocument;
2531  if ( !projectDocument.setContent( &projectFile ) )
2532  {
2533  return nullptr;
2534  }
2535 
2536  QgsReadWriteContext context;
2537  context.setPathResolver( pathResolver() );
2538  context.setProjectTranslator( this );
2540 
2542 
2543  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2544  if ( !layerTreeElem.isNull() )
2545  {
2546  root->readChildrenFromXml( layerTreeElem, context );
2547  }
2548  else
2549  {
2550  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2551  }
2552 
2553  QgsLayerTreeGroup *group = root->findGroup( groupName );
2554  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
2555  {
2556  // embedded groups cannot be embedded again
2557  delete root;
2558  return nullptr;
2559  }
2560 
2561  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
2562  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
2563  delete root;
2564  root = nullptr;
2565 
2566  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2567  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
2568 
2569  // set "embedded" to all children + load embedded layers
2570  mLayerTreeRegistryBridge->setEnabled( false );
2571  initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
2572  mLayerTreeRegistryBridge->setEnabled( true );
2573 
2574  // consider the layers might be identify disabled in its project
2575  const auto constFindLayerIds = newGroup->findLayerIds();
2576  for ( const QString &layerId : constFindLayerIds )
2577  {
2578  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
2579  if ( layer )
2580  {
2581  layer->resolveReferences( this );
2582  layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
2583  }
2584  }
2585 
2586  return newGroup;
2587 }
2588 
2589 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, QgsProject::ReadFlags flags )
2590 {
2591  const auto constChildren = group->children();
2592  for ( QgsLayerTreeNode *child : constChildren )
2593  {
2594  // all nodes in the subtree will have "embedded" custom property set
2595  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
2596 
2597  if ( QgsLayerTree::isGroup( child ) )
2598  {
2599  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
2600  }
2601  else if ( QgsLayerTree::isLayer( child ) )
2602  {
2603  // load the layer into our project
2604  QList<QDomNode> brokenNodes;
2605  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
2606  }
2607  }
2608 }
2609 
2611 {
2612  return mEvaluateDefaultValues;
2613 }
2614 
2616 {
2617  if ( evaluateDefaultValues == mEvaluateDefaultValues )
2618  return;
2619 
2620  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2621  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
2622  for ( ; layerIt != layers.constEnd(); ++layerIt )
2623  {
2624  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
2625  if ( vl )
2626  {
2628  }
2629  }
2630 
2631  mEvaluateDefaultValues = evaluateDefaultValues;
2632 }
2633 
2635 {
2636  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2638 }
2639 
2641 {
2642  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2643 }
2644 
2646 {
2647  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2648  if ( !distanceUnitString.isEmpty() )
2649  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2650 
2651  //fallback to QGIS default measurement unit
2652  QgsSettings s;
2653  bool ok = false;
2654  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2655  return ok ? type : QgsUnitTypes::DistanceMeters;
2656 }
2657 
2659 {
2660  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2661 }
2662 
2664 {
2665  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2666  if ( !areaUnitString.isEmpty() )
2667  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2668 
2669  //fallback to QGIS default area unit
2670  QgsSettings s;
2671  bool ok = false;
2672  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2673  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2674 }
2675 
2677 {
2678  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2679 }
2680 
2681 QString QgsProject::homePath() const
2682 {
2683  if ( !mCachedHomePath.isEmpty() )
2684  return mCachedHomePath;
2685 
2686  if ( !mHomePath.isEmpty() )
2687  {
2688  QFileInfo homeInfo( mHomePath );
2689  if ( !homeInfo.isRelative() )
2690  {
2691  mCachedHomePath = mHomePath;
2692  return mHomePath;
2693  }
2694  }
2695 
2696  QFileInfo pfi( fileName() );
2697  if ( !pfi.exists() )
2698  {
2699  mCachedHomePath = mHomePath;
2700  return mHomePath;
2701  }
2702 
2703  if ( !mHomePath.isEmpty() )
2704  {
2705  // path is relative to project file
2706  mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
2707  }
2708  else
2709  {
2710  mCachedHomePath = pfi.canonicalPath();
2711  }
2712  return mCachedHomePath;
2713 }
2714 
2716 {
2717  return mHomePath;
2718 }
2719 
2721 {
2722  return mRelationManager;
2723 }
2724 
2726 {
2727  return mLayoutManager.get();
2728 }
2729 
2731 {
2732  return mLayoutManager.get();
2733 }
2734 
2736 {
2737  return mBookmarkManager;
2738 }
2739 
2741 {
2742  return mBookmarkManager;
2743 }
2744 
2746 {
2747  return mRootGroup;
2748 }
2749 
2751 {
2752  return mMapThemeCollection.get();
2753 }
2754 
2756 {
2757  return mAnnotationManager.get();
2758 }
2759 
2761 {
2762  return mAnnotationManager.get();
2763 }
2764 
2765 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2766 {
2767  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
2768  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
2769  {
2770  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2771  continue;
2772 
2773  if ( layers.contains( it.value() ) )
2774  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
2775  else
2776  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
2777  }
2778 
2782 }
2783 
2784 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2785 {
2786  QList<QgsMapLayer *> nonIdentifiableLayers;
2787  nonIdentifiableLayers.reserve( layerIds.count() );
2788  for ( const QString &layerId : layerIds )
2789  {
2790  QgsMapLayer *layer = mapLayer( layerId );
2791  if ( layer )
2792  nonIdentifiableLayers << layer;
2793  }
2795  setNonIdentifiableLayers( nonIdentifiableLayers );
2797 }
2798 
2799 QStringList QgsProject::nonIdentifiableLayers() const
2800 {
2801  QStringList nonIdentifiableLayers;
2802 
2803  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
2804  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
2805  {
2806  if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
2807  {
2808  nonIdentifiableLayers.append( it.value()->id() );
2809  }
2810  }
2811  return nonIdentifiableLayers;
2812 }
2813 
2815 {
2816  return mAutoTransaction;
2817 }
2818 
2820 {
2821  if ( autoTransaction != mAutoTransaction )
2822  {
2823  mAutoTransaction = autoTransaction;
2824 
2825  if ( autoTransaction )
2826  onMapLayersAdded( mapLayers().values() );
2827  else
2828  cleanTransactionGroups( true );
2829  }
2830 }
2831 
2832 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2833 {
2834  return mTransactionGroups;
2835 }
2836 
2837 
2838 //
2839 // QgsMapLayerStore methods
2840 //
2841 
2842 
2844 {
2845  return mLayerStore->count();
2846 }
2847 
2849 {
2850  return mLayerStore->validCount();
2851 }
2852 
2853 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
2854 {
2855  return mLayerStore->mapLayer( layerId );
2856 }
2857 
2858 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
2859 {
2860  return mLayerStore->mapLayersByName( layerName );
2861 }
2862 
2863 QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
2864 {
2865  QList<QgsMapLayer *> layers;
2866  const auto constMapLayers { mLayerStore->mapLayers() };
2867  for ( const auto &l : constMapLayers )
2868  {
2869  if ( ! l->shortName().isEmpty() )
2870  {
2871  if ( l->shortName() == shortName )
2872  layers << l;
2873  }
2874  else if ( l->name() == shortName )
2875  {
2876  layers << l;
2877  }
2878  }
2879  return layers;
2880 }
2881 
2882 bool QgsProject::unzip( const QString &filename, QgsProject::ReadFlags flags )
2883 {
2884  clearError();
2885  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2886 
2887  // unzip the archive
2888  if ( !archive->unzip( filename ) )
2889  {
2890  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
2891  return false;
2892  }
2893 
2894  // test if zip provides a .qgs file
2895  if ( archive->projectFile().isEmpty() )
2896  {
2897  setError( tr( "Zip archive does not provide a project file" ) );
2898  return false;
2899  }
2900 
2901  // load auxiliary storage
2902  if ( !archive->auxiliaryStorageFile().isEmpty() )
2903  {
2904  // database file is already a copy as it's been unzipped. So we don't open
2905  // auxiliary storage in copy mode in this case
2906  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
2907  }
2908  else
2909  {
2910  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
2911  }
2912 
2913  // read the project file
2914  if ( ! readProjectFile( archive->projectFile(), flags ) )
2915  {
2916  setError( tr( "Cannot read unzipped qgs project file" ) );
2917  return false;
2918  }
2919 
2920  // keep the archive and remove the temporary .qgs file
2921  mArchive = std::move( archive );
2922  mArchive->clearProjectFile();
2923 
2924  return true;
2925 }
2926 
2927 bool QgsProject::zip( const QString &filename )
2928 {
2929  clearError();
2930 
2931  // save the current project in a temporary .qgs file
2932  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2933  const QString baseName = QFileInfo( filename ).baseName();
2934  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
2935  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
2936 
2937  bool writeOk = false;
2938  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2939  {
2940  writeOk = writeProjectFile( qgsFile.fileName() );
2941  qgsFile.close();
2942  }
2943 
2944  // stop here with an error message
2945  if ( ! writeOk )
2946  {
2947  setError( tr( "Unable to write temporary qgs file" ) );
2948  return false;
2949  }
2950 
2951  // save auxiliary storage
2952  const QFileInfo info( qgsFile );
2953  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
2954 
2955  if ( ! saveAuxiliaryStorage( asFileName ) )
2956  {
2957  const QString err = mAuxiliaryStorage->errorString();
2958  setError( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2959  return false;
2960  }
2961 
2962  // create the archive
2963  archive->addFile( qgsFile.fileName() );
2964  archive->addFile( asFileName );
2965 
2966  // zip
2967  if ( !archive->zip( filename ) )
2968  {
2969  setError( tr( "Unable to perform zip" ) );
2970  return false;
2971  }
2972 
2973  return true;
2974 }
2975 
2977 {
2978  return QgsZipUtils::isZipFile( mFile.fileName() );
2979 }
2980 
2981 QList<QgsMapLayer *> QgsProject::addMapLayers(
2982  const QList<QgsMapLayer *> &layers,
2983  bool addToLegend,
2984  bool takeOwnership )
2985 {
2986  const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
2987  if ( !myResultList.isEmpty() )
2988  {
2989  // Update transform context
2990  for ( auto &l : myResultList )
2991  {
2992  l->setTransformContext( transformContext() );
2993  }
2994  if ( addToLegend )
2995  {
2996  emit legendLayersAdded( myResultList );
2997  }
2998  }
2999 
3000  if ( mAuxiliaryStorage )
3001  {
3002  for ( QgsMapLayer *mlayer : myResultList )
3003  {
3004  if ( mlayer->type() != QgsMapLayerType::VectorLayer )
3005  continue;
3006 
3007  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
3008  if ( vl )
3009  {
3010  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
3011  }
3012  }
3013  }
3014 
3015  return myResultList;
3016 }
3017 
3018 QgsMapLayer *
3020  bool addToLegend,
3021  bool takeOwnership )
3022 {
3023  QList<QgsMapLayer *> addedLayers;
3024  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
3025  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
3026 }
3027 
3028 void QgsProject::removeMapLayers( const QStringList &layerIds )
3029 {
3030  mLayerStore->removeMapLayers( layerIds );
3031 }
3032 
3033 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3034 {
3035  mLayerStore->removeMapLayers( layers );
3036 }
3037 
3038 void QgsProject::removeMapLayer( const QString &layerId )
3039 {
3040  mLayerStore->removeMapLayer( layerId );
3041 }
3042 
3044 {
3045  mLayerStore->removeMapLayer( layer );
3046 }
3047 
3049 {
3050  return mLayerStore->takeMapLayer( layer );
3051 }
3052 
3054 {
3055  mLayerStore->removeAllMapLayers();
3056 }
3057 
3059 {
3060  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3061  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3062  for ( ; it != layers.constEnd(); ++it )
3063  {
3064  it.value()->reload();
3065  }
3066 }
3067 
3068 QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3069 {
3070  return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3071 }
3072 
3073 QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3074 {
3075  return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3076 }
3077 
3079 {
3080  QgsSettings settings;
3081  QgsCoordinateReferenceSystem defaultCrs;
3082 
3083  // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
3084  // in the meantime, we have a slightly hacky way to read the settings key using an enum which isn't available (since it lives in app)
3085  if ( settings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
3086  || settings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == 2 )
3087  {
3088  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
3089  defaultCrs = crs();
3090  }
3091  else
3092  {
3093  // global crs
3094  QString layerDefaultCrs = settings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), GEO_EPSG_CRS_AUTHID ).toString();
3095  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
3096  }
3097 
3098  return defaultCrs;
3099 }
3100 
3102 {
3103  mTrustLayerMetadata = trust;
3104 
3105  auto layers = mapLayers();
3106  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3107  {
3108  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3109  if ( vl )
3110  {
3111  vl->setReadExtentFromXml( trust );
3112  }
3113  }
3114 }
3115 
3116 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
3117 {
3118  const QMap<QString, QgsMapLayer *> layers = mapLayers();
3119  bool empty = true;
3120  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3121  {
3122  if ( it.value()->type() != QgsMapLayerType::VectorLayer )
3123  continue;
3124 
3125  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
3126  if ( vl && vl->auxiliaryLayer() )
3127  {
3128  vl->auxiliaryLayer()->save();
3129  empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
3130  }
3131  }
3132 
3133  if ( !mAuxiliaryStorage->exists( *this ) && filename.isEmpty() && empty )
3134  {
3135  return true; // it's not an error
3136  }
3137  else if ( !filename.isEmpty() )
3138  {
3139  return mAuxiliaryStorage->saveAs( filename );
3140  }
3141  else
3142  {
3143  return mAuxiliaryStorage->saveAs( *this );
3144  }
3145 }
3146 
3148 {
3149  return mAuxiliaryStorage.get();
3150 }
3151 
3153 {
3154  return mAuxiliaryStorage.get();
3155 }
3156 
3158 {
3159  return mMetadata;
3160 }
3161 
3163 {
3164  if ( metadata == mMetadata )
3165  return;
3166 
3167  mMetadata = metadata;
3168  mProjectScope.reset();
3169 
3170  emit metadataChanged();
3171 
3172  setDirty( true );
3173 }
3174 
3175 QSet<QgsMapLayer *> QgsProject::requiredLayers() const
3176 {
3177  QSet<QgsMapLayer *> requiredLayers;
3178 
3179  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3180  for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3181  {
3182  if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3183  {
3184  requiredLayers.insert( it.value() );
3185  }
3186  }
3187  return requiredLayers;
3188 }
3189 
3190 void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
3191 {
3192  const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3193  for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3194  {
3195  if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
3196  continue;
3197 
3198  if ( layers.contains( it.value() ) )
3199  it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
3200  else
3201  it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
3202  }
3203 }
3204 
3206 {
3207  // save colors to project
3208  QStringList customColors;
3209  QStringList customColorLabels;
3210 
3211  QgsNamedColorList::const_iterator colorIt = colors.constBegin();
3212  for ( ; colorIt != colors.constEnd(); ++colorIt )
3213  {
3214  QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
3215  QString label = ( *colorIt ).second;
3216  customColors.append( color );
3217  customColorLabels.append( label );
3218  }
3219  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
3220  writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
3221  mProjectScope.reset();
3222  emit projectColorsChanged();
3223 }
3224 
3225 void QgsProject::setBackgroundColor( const QColor &color )
3226 {
3227  if ( mBackgroundColor == color )
3228  return;
3229 
3230  mBackgroundColor = color;
3231  emit backgroundColorChanged();
3232 }
3233 
3234 QColor QgsProject::backgroundColor() const
3235 {
3236  return mBackgroundColor;
3237 }
3238 
3239 void QgsProject::setSelectionColor( const QColor &color )
3240 {
3241  if ( mSelectionColor == color )
3242  return;
3243 
3244  mSelectionColor = color;
3245  emit selectionColorChanged();
3246 }
3247 
3248 QColor QgsProject::selectionColor() const
3249 {
3250  return mSelectionColor;
3251 }
3252 
3253 void QgsProject::setMapScales( const QVector<double> &scales )
3254 {
3255  // sort scales in descending order
3256  QVector< double > sorted = scales;
3257  std::sort( sorted.begin(), sorted.end(), std::greater<double>() );
3258 
3259  if ( sorted == mapScales() )
3260  return;
3261 
3262  QStringList store;
3263  store.reserve( sorted.size() );
3264  for ( double scale : qgis::as_const( sorted ) )
3265  store << QStringLiteral( "1:%1" ).arg( scale );
3266 
3267  writeEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ), store );
3268  emit mapScalesChanged();
3269 }
3270 
3271 QVector<double> QgsProject::mapScales() const
3272 {
3273  const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
3274  QVector<double> res;
3275  for ( const QString &scale : scales )
3276  {
3277  const QStringList parts = scale.split( ':' );
3278  if ( parts.size() != 2 )
3279  continue;
3280 
3281  bool ok = false;
3282  const double denominator = QLocale().toDouble( parts[1], &ok );
3283  if ( ok )
3284  {
3285  res << denominator;
3286  }
3287  }
3288  return res;
3289 }
3290 
3292 {
3293  if ( enabled == useProjectScales() )
3294  return;
3295 
3296  writeEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ), enabled );
3297  emit mapScalesChanged();
3298 }
3299 
3301 {
3302  return readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) );
3303 }
3304 
3305 void QgsProject::generateTsFile( const QString &locale )
3306 {
3307  QgsTranslationContext translationContext;
3308  translationContext.setProject( this );
3309  translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
3310 
3311  QgsApplication::instance()->collectTranslatableObjects( &translationContext );
3312 
3313  translationContext.writeTsFile( locale );
3314 }
3315 
3316 QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
3317 {
3318  if ( !mTranslator )
3319  {
3320  return sourceText;
3321  }
3322 
3323  QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
3324 
3325  if ( result.isEmpty() )
3326  {
3327  return sourceText;
3328  }
3329  return result;
3330 }
3331 
3333 {
3334  const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
3335  if ( !layers.empty() )
3336  {
3337  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
3338  {
3339  // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
3340  if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3341  {
3342  if ( !( ( *it )->accept( visitor ) ) )
3343  return false;
3344 
3345  if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
3346  return false;
3347  }
3348  }
3349  }
3350 
3351  if ( !mLayoutManager->accept( visitor ) )
3352  return false;
3353 
3354  if ( !mAnnotationManager->accept( visitor ) )
3355  return false;
3356 
3357  return true;
3358 }
3359 
3361 GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
3362  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3363 {
3364  if ( !project )
3365  return;
3366 
3367  //build up color list from project. Do this in advance for speed
3368  QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
3369  QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
3370 
3371  //generate list from custom colors
3372  int colorIndex = 0;
3373  for ( QStringList::iterator it = colorStrings.begin();
3374  it != colorStrings.end(); ++it )
3375  {
3376  QColor color = QgsSymbolLayerUtils::decodeColor( *it );
3377  QString label;
3378  if ( colorLabels.length() > colorIndex )
3379  {
3380  label = colorLabels.at( colorIndex );
3381  }
3382 
3383  mColors.insert( label.toLower(), color );
3384  colorIndex++;
3385  }
3386 }
3387 
3388 GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
3389  : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
3390  , mColors( colors )
3391 {
3392 }
3393 
3394 QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
3395 {
3396  QString colorName = values.at( 0 ).toString().toLower();
3397  if ( mColors.contains( colorName ) )
3398  {
3399  return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
3400  }
3401  else
3402  return QVariant();
3403 }
3404 
3405 QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
3406 {
3407  return new GetNamedProjectColor( mColors );
3408 }
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:473
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:468
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:605
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:52
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
void selectionColorChanged()
Emitted whenever the project&#39;s selection color has been changed.
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:794
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Don&#39;t resolve layer paths (i.e. don&#39;t load any layer content). Dramatically improves project read tim...
Definition: qgsproject.h:276
const QgsProjectMetadata & metadata() const
Returns a reference to the project&#39;s metadata store.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ...
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
void backgroundColorChanged()
Emitted whenever the project&#39;s canvas background color has been changed.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
Definition: qgsproject.cpp:916
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:649
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:570
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 mapScalesChanged()
Emitted when the list of custom project map scales changes.
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:355
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:893
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.
QColor selectionColor() const
Returns the color used to highlight selected features.
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:660
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.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:89
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:683
~QgsProject() override
Definition: qgsproject.cpp:421
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:295
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:600
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...
void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
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:89
void setSelectionColor(const QColor &color)
Sets the color used to highlight selected features.
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:517
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:619
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.
bool useProjectScales() const
Returns true if project mapScales() are enabled.
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:485
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:650
void setFileName(const QString &fileName)
Sets the fileName of the TS file.
void setBackgroundColor(const QColor &color)
Sets the default background color used by default map canvases.
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:595
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:499
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed...
QVector< double > mapScales() const
Returns the list of custom project map scales.
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:442
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).
void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
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:698
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:451
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:463
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:641
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
Resolves relative paths into absolute paths and vice versa.
Don&#39;t load print layouts. Improves project read time if layouts are not required, and allows projects...
Definition: qgsproject.h:277
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:630
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:131
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.
QColor backgroundColor() const
Returns the default background color used by default map canvases.
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:208
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:831
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:713
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.