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