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