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