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