QGIS API Documentation  2.99.0-Master (19b062c)
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 "qgsprojectversion.h"
33 #include "qgsrasterlayer.h"
34 #include "qgsreadwritecontext.h"
35 #include "qgsrectangle.h"
36 #include "qgsrelationmanager.h"
37 #include "qgsannotationmanager.h"
38 #include "qgsvectorlayer.h"
39 #include "qgsvectorlayerjoininfo.h"
40 #include "qgsmapthemecollection.h"
41 #include "qgslayerdefinition.h"
42 #include "qgsunittypes.h"
43 #include "qgstransaction.h"
44 #include "qgstransactiongroup.h"
45 #include "qgsvectordataprovider.h"
47 #include "qgssettings.h"
48 #include "qgsmaplayerlistutils.h"
49 #include "qgslayoutmanager.h"
50 #include "qgsmaplayerstore.h"
51 #include "qgsziputils.h"
52 #include "qgsauxiliarystorage.h"
53 
54 #include <QApplication>
55 #include <QFileInfo>
56 #include <QDomNode>
57 #include <QObject>
58 #include <QTextStream>
59 #include <QTemporaryFile>
60 #include <QDir>
61 #include <QUrl>
62 
63 #ifdef _MSC_VER
64 #include <sys/utime.h>
65 #else
66 #include <utime.h>
67 #endif
68 
69 // canonical project instance
70 QgsProject *QgsProject::sProject = nullptr;
71 
80 QStringList makeKeyTokens_( const QString &scope, const QString &key )
81 {
82  QStringList keyTokens = QStringList( scope );
83  keyTokens += key.split( '/', QString::SkipEmptyParts );
84 
85  // be sure to include the canonical root node
86  keyTokens.push_front( QStringLiteral( "properties" ) );
87 
88  //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.
89  for ( int i = 0; i < keyTokens.size(); ++i )
90  {
91  QString keyToken = keyTokens.at( i );
92 
93  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
94  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
95  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]" );
96  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]" );
97 
98  if ( keyToken.contains( QRegExp( nameCharRegexp ) ) || keyToken.contains( QRegExp( nameStartCharRegexp ) ) )
99  {
100 
101  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
102  QgsMessageLog::logMessage( errorString, QString(), QgsMessageLog::CRITICAL );
103 
104  }
105 
106  }
107 
108  return keyTokens;
109 }
110 
111 
112 
122 QgsProjectProperty *findKey_( const QString &scope,
123  const QString &key,
124  QgsProjectPropertyKey &rootProperty )
125 {
126  QgsProjectPropertyKey *currentProperty = &rootProperty;
127  QgsProjectProperty *nextProperty; // link to next property down hierarchy
128 
129  QStringList keySequence = makeKeyTokens_( scope, key );
130 
131  while ( !keySequence.isEmpty() )
132  {
133  // if the current head of the sequence list matches the property name,
134  // then traverse down the property hierarchy
135  if ( keySequence.first() == currentProperty->name() )
136  {
137  // remove front key since we're traversing down a level
138  keySequence.pop_front();
139 
140  if ( 1 == keySequence.count() )
141  {
142  // if we have only one key name left, then return the key found
143  return currentProperty->find( keySequence.front() );
144  }
145  else if ( keySequence.isEmpty() )
146  {
147  // if we're out of keys then the current property is the one we
148  // want; i.e., we're in the rate case of being at the top-most
149  // property node
150  return currentProperty;
151  }
152  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
153  {
154  if ( nextProperty->isKey() )
155  {
156  currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
157  }
158  else if ( nextProperty->isValue() && 1 == keySequence.count() )
159  {
160  // it may be that this may be one of several property value
161  // nodes keyed by QDict string; if this is the last remaining
162  // key token and the next property is a value node, then
163  // that's the situation, so return the currentProperty
164  return currentProperty;
165  }
166  else
167  {
168  // QgsProjectPropertyValue not Key, so return null
169  return nullptr;
170  }
171  }
172  else
173  {
174  // if the next key down isn't found
175  // then the overall key sequence doesn't exist
176  return nullptr;
177  }
178  }
179  else
180  {
181  return nullptr;
182  }
183  }
184 
185  return nullptr;
186 }
187 
188 
189 
198 QgsProjectProperty *addKey_( const QString &scope,
199  const QString &key,
200  QgsProjectPropertyKey *rootProperty,
201  const QVariant &value )
202 {
203  QStringList keySequence = makeKeyTokens_( scope, key );
204 
205  // cursor through property key/value hierarchy
206  QgsProjectPropertyKey *currentProperty = rootProperty;
207  QgsProjectProperty *nextProperty; // link to next property down hierarchy
208  QgsProjectPropertyKey *newPropertyKey = nullptr;
209 
210  while ( ! keySequence.isEmpty() )
211  {
212  // if the current head of the sequence list matches the property name,
213  // then traverse down the property hierarchy
214  if ( keySequence.first() == currentProperty->name() )
215  {
216  // remove front key since we're traversing down a level
217  keySequence.pop_front();
218 
219  // if key sequence has one last element, then we use that as the
220  // name to store the value
221  if ( 1 == keySequence.count() )
222  {
223  currentProperty->setValue( keySequence.front(), value );
224  return currentProperty;
225  }
226  // we're at the top element if popping the keySequence element
227  // will leave it empty; in that case, just add the key
228  else if ( keySequence.isEmpty() )
229  {
230  currentProperty->setValue( value );
231 
232  return currentProperty;
233  }
234  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
235  {
236  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
237 
238  if ( currentProperty )
239  {
240  continue;
241  }
242  else // QgsProjectPropertyValue not Key, so return null
243  {
244  return nullptr;
245  }
246  }
247  else // the next subkey doesn't exist, so add it
248  {
249  if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
250  {
251  currentProperty = newPropertyKey;
252  }
253  continue;
254  }
255  }
256  else
257  {
258  return nullptr;
259  }
260  }
261 
262  return nullptr;
263 
264 }
265 
266 
267 void removeKey_( const QString &scope,
268  const QString &key,
269  QgsProjectPropertyKey &rootProperty )
270 {
271  QgsProjectPropertyKey *currentProperty = &rootProperty;
272 
273  QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
274  QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
275 
276  QStringList keySequence = makeKeyTokens_( scope, key );
277 
278  while ( ! keySequence.isEmpty() )
279  {
280  // if the current head of the sequence list matches the property name,
281  // then traverse down the property hierarchy
282  if ( keySequence.first() == currentProperty->name() )
283  {
284  // remove front key since we're traversing down a level
285  keySequence.pop_front();
286 
287  // if we have only one key name left, then try to remove the key
288  // with that name
289  if ( 1 == keySequence.count() )
290  {
291  currentProperty->removeKey( keySequence.front() );
292  }
293  // if we're out of keys then the current property is the one we
294  // want to remove, but we can't delete it directly; we need to
295  // delete it from the parent property key container
296  else if ( keySequence.isEmpty() )
297  {
298  previousQgsPropertyKey->removeKey( currentProperty->name() );
299  }
300  else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
301  {
302  previousQgsPropertyKey = currentProperty;
303  currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
304 
305  if ( currentProperty )
306  {
307  continue;
308  }
309  else // QgsProjectPropertyValue not Key, so return null
310  {
311  return;
312  }
313  }
314  else // if the next key down isn't found
315  {
316  // then the overall key sequence doesn't exist
317  return;
318  }
319  }
320  else
321  {
322  return;
323  }
324  }
325 
326 }
327 
328 QgsProject::QgsProject( QObject *parent )
329  : QObject( parent )
330  , mLayerStore( new QgsMapLayerStore( this ) )
331  , mBadLayerHandler( new QgsProjectBadLayerHandler() )
332  , mSnappingConfig( this )
333  , mRelationManager( new QgsRelationManager( this ) )
334  , mAnnotationManager( new QgsAnnotationManager( this ) )
335  , mLayoutManager( new QgsLayoutManager( this ) )
336  , mRootGroup( new QgsLayerTree )
337  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
338  , mArchive( new QgsProjectArchive() )
339  , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
340 {
341  mProperties.setName( QStringLiteral( "properties" ) );
342  clear();
343 
344  // bind the layer tree to the map layer registry.
345  // whenever layers are added to or removed from the registry,
346  // layer tree will be updated
347  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
348  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
349  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
350  connect( this, static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *> & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
351 
352  // proxy map layer store signals to this
353  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersWillBeRemoved ),
354  this, static_cast<void ( QgsProject::* )( const QStringList & )>( &QgsProject::layersWillBeRemoved ) );
355  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QList<QgsMapLayer *> & )>( &QgsMapLayerStore::layersWillBeRemoved ),
356  this, static_cast<void ( QgsProject::* )( const QList<QgsMapLayer *> & )>( &QgsProject::layersWillBeRemoved ) );
357  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QString & )>( &QgsMapLayerStore::layerWillBeRemoved ),
358  this, static_cast<void ( QgsProject::* )( const QString & )>( &QgsProject::layerWillBeRemoved ) );
359  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( QgsMapLayer * )>( &QgsMapLayerStore::layerWillBeRemoved ),
360  this, static_cast<void ( QgsProject::* )( QgsMapLayer * )>( &QgsProject::layerWillBeRemoved ) );
361  connect( mLayerStore.get(), static_cast<void ( QgsMapLayerStore::* )( const QStringList & )>( &QgsMapLayerStore::layersRemoved ), this, &QgsProject::layersRemoved );
362  connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this, &QgsProject::layerRemoved );
363  connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this, &QgsProject::removeAll );
364  connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this, &QgsProject::layersAdded );
365  connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this, &QgsProject::layerWasAdded );
366 }
367 
368 
370 {
371  clear();
372  delete mBadLayerHandler;
373  delete mRelationManager;
374  delete mLayerTreeRegistryBridge;
375  delete mRootGroup;
376  if ( this == sProject )
377  {
378  sProject = nullptr;
379  }
380 }
381 
382 
384 {
385  if ( !sProject )
386  {
387  sProject = new QgsProject;
388  }
389  return sProject;
390 }
391 
392 void QgsProject::setTitle( const QString &title )
393 {
394  if ( title == mTitle )
395  return;
396 
397  mTitle = title;
398 
399  setDirty( true );
400 }
401 
402 
403 QString QgsProject::title() const
404 {
405  return mTitle;
406 }
407 
408 
410 {
411  return mDirty;
412 }
413 
414 void QgsProject::setDirty( bool b )
415 {
416  mDirty = b;
417 }
418 
419 void QgsProject::setFileName( const QString &name )
420 {
421  if ( name == mFile.fileName() )
422  return;
423 
424  QString oldHomePath = homePath();
425 
426  mFile.setFileName( name );
427  emit fileNameChanged();
428 
429  QString newHomePath = homePath();
430  if ( newHomePath != oldHomePath )
431  emit homePathChanged();
432 
433  setDirty( true );
434 }
435 
436 QString QgsProject::fileName() const
437 {
438  return mFile.fileName();
439 }
440 
441 QFileInfo QgsProject::fileInfo() const
442 {
443  return QFileInfo( mFile );
444 }
445 
447 {
448  return mCrs;
449 }
450 
452 {
453  mCrs = crs;
454  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
455  setDirty( true );
456  emit crsChanged();
457 }
458 
459 QString QgsProject::ellipsoid() const
460 {
461  if ( !crs().isValid() )
462  return GEO_NONE;
463 
464  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), GEO_NONE );
465 }
466 
467 void QgsProject::setEllipsoid( const QString &ellipsoid )
468 {
469  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
470  setDirty( true );
471  emit ellipsoidChanged( ellipsoid );
472 }
473 
475 {
476  mFile.setFileName( QString() );
477  mProperties.clearKeys();
478  mTitle.clear();
479  mAutoTransaction = false;
480  mEvaluateDefaultValues = false;
481  mDirty = false;
482  mTrustLayerMetadata = false;
483  mCustomVariables.clear();
484 
485  mEmbeddedLayers.clear();
486  mRelationManager->clear();
487  mAnnotationManager->clear();
488  mLayoutManager->clear();
489  mSnappingConfig.reset();
490  emit snappingConfigChanged( mSnappingConfig );
491 
492  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
494 
495  mLabelingEngineSettings->clear();
496 
497  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
498  mArchive->clear();
499 
501 
502  // reset some default project properties
503  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
504  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
505  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
506  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
507 
508  //copy default units to project
509  QgsSettings s;
510  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
511  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
512 
514  mRootGroup->clear();
515 
516  setDirty( false );
517 }
518 
519 // basically a debugging tool to dump property list values
520 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
521 {
522  QgsDebugMsg( "current properties:" );
523  topQgsPropertyKey.dump();
524 }
525 
526 
557 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
558 {
559  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
560 
561  if ( propertiesElem.isNull() ) // no properties found, so we're done
562  {
563  return;
564  }
565 
566  QDomNodeList scopes = propertiesElem.childNodes();
567 
568  if ( scopes.count() < 1 )
569  {
570  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
571  return;
572  }
573 
574  if ( ! project_properties.readXml( propertiesElem ) )
575  {
576  QgsDebugMsg( "Project_properties.readXml() failed" );
577  }
578 }
579 
580 
585 static void _getTitle( const QDomDocument &doc, QString &title )
586 {
587  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
588 
589  title.clear(); // by default the title will be empty
590 
591  if ( !nl.count() )
592  {
593  QgsDebugMsg( "unable to find title element" );
594  return;
595  }
596 
597  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element OK
598 
599  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
600  {
601  QgsDebugMsg( "unable to find title element" );
602  return;
603  }
604 
605  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
606 
607  if ( !titleTextNode.isText() )
608  {
609  QgsDebugMsg( "unable to find title element" );
610  return;
611  }
612 
613  QDomText titleText = titleTextNode.toText();
614 
615  title = titleText.data();
616 
617 }
618 
619 QgsProjectVersion getVersion( const QDomDocument &doc )
620 {
621  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
622 
623  if ( !nl.count() )
624  {
625  QgsDebugMsg( " unable to find qgis element in project file" );
626  return QgsProjectVersion( 0, 0, 0, QString() );
627  }
628 
629  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
630 
631  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
632  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
633  return projectVersion;
634 }
635 
636 
638 {
639  return mSnappingConfig;
640 }
641 
643 {
644  if ( mSnappingConfig == snappingConfig )
645  return;
646 
647  mSnappingConfig = snappingConfig;
648  setDirty();
649  emit snappingConfigChanged( mSnappingConfig );
650 }
651 
652 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes )
653 {
654  // Layer order is set by the restoring the legend settings from project file.
655  // This is done on the 'readProject( ... )' signal
656 
657  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
658 
659  // process the map layer nodes
660 
661  if ( 0 == nl.count() ) // if we have no layers to process, bail
662  {
663  return true; // Decided to return "true" since it's
664  // possible for there to be a project with no
665  // layers; but also, more imporantly, this
666  // would cause the tests/qgsproject to fail
667  // since the test suite doesn't currently
668  // support test layers
669  }
670 
671  bool returnStatus = true;
672 
673  emit layerLoaded( 0, nl.count() );
674 
675  // order layers based on their dependencies
676  QgsLayerDefinition::DependencySorter depSorter( doc );
677  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
678  return false;
679 
680  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
681 
682  int i = 0;
683  Q_FOREACH ( const QDomNode &node, sortedLayerNodes )
684  {
685  QDomElement element = node.toElement();
686 
687  QString name = node.namedItem( QStringLiteral( "layername" ) ).toElement().text();
688  if ( !name.isNull() )
689  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
690 
691  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
692  {
693  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes );
694  continue;
695  }
696  else
697  {
698  QgsReadWriteContext context;
699  context.setPathResolver( pathResolver() );
700  if ( !addLayer( element, brokenNodes, context ) )
701  {
702  returnStatus = false;
703  }
704  }
705  emit layerLoaded( i + 1, nl.count() );
706  i++;
707  }
708 
709  return returnStatus;
710 }
711 
712 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, const QgsReadWriteContext &context )
713 {
714  QString type = layerElem.attribute( QStringLiteral( "type" ) );
715  QgsDebugMsgLevel( "Layer type is " + type, 4 );
716  QgsMapLayer *mapLayer = nullptr;
717 
718  if ( type == QLatin1String( "vector" ) )
719  {
720  mapLayer = new QgsVectorLayer;
721 
722  // apply specific settings to vector layer
723  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer ) )
724  {
725  vl->setReadExtentFromXml( mTrustLayerMetadata );
726  }
727  }
728  else if ( type == QLatin1String( "raster" ) )
729  {
730  mapLayer = new QgsRasterLayer;
731  }
732  else if ( type == QLatin1String( "plugin" ) )
733  {
734  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
735  mapLayer = QgsApplication::pluginLayerRegistry()->createLayer( typeName );
736  }
737 
738  if ( !mapLayer )
739  {
740  QgsDebugMsg( "Unable to create layer" );
741 
742  return false;
743  }
744 
745  Q_CHECK_PTR( mapLayer ); // NOLINT
746 
747  // have the layer restore state that is stored in Dom node
748  if ( mapLayer->readLayerXml( layerElem, context ) && mapLayer->isValid() )
749  {
750  emit readMapLayer( mapLayer, layerElem );
751 
752  QList<QgsMapLayer *> myLayers;
753  myLayers << mapLayer;
754  addMapLayers( myLayers );
755 
756  return true;
757  }
758  else
759  {
760  delete mapLayer;
761 
762  QgsDebugMsg( "Unable to load " + type + " layer" );
763  brokenNodes.push_back( layerElem );
764  return false;
765  }
766 }
767 
768 bool QgsProject::read( const QString &filename )
769 {
770  mFile.setFileName( filename );
771 
772  return read();
773 }
774 
776 {
777  QString filename = mFile.fileName();
778  bool rc;
779 
780  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
781  {
782  rc = unzip( mFile.fileName() );
783  }
784  else
785  {
786  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
787  rc = readProjectFile( mFile.fileName() );
788  }
789 
790  mFile.setFileName( filename );
791  return rc;
792 }
793 
794 bool QgsProject::readProjectFile( const QString &filename )
795 {
796  QFile projectFile( filename );
797  clearError();
798 
799  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
800 
801  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
802  {
803  projectFile.close();
804 
805  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
806 
807  return false;
808  }
809 
810  // location of problem associated with errorMsg
811  int line, column;
812  QString errorMsg;
813 
814  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
815  {
816  // want to make this class as GUI independent as possible; so commented out
817 #if 0
818  QMessageBox::critical( 0, tr( "Project File Read Error" ),
819  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
820 #endif
821 
822  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
823  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
824 
825  QgsDebugMsg( errorString );
826 
827  projectFile.close();
828 
829  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
830 
831  return false;
832  }
833 
834  projectFile.close();
835 
836 
837  QgsDebugMsg( "Opened document " + projectFile.fileName() );
838  QgsDebugMsg( "Project title: " + mTitle );
839 
840  // get project version string, if any
841  QgsProjectVersion fileVersion = getVersion( *doc );
842  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
843 
844  if ( thisVersion > fileVersion )
845  {
846  QgsLogger::warning( "Loading a file that was saved with an older "
847  "version of qgis (saved in " + fileVersion.text() +
848  ", loaded in " + Qgis::QGIS_VERSION +
849  "). Problems may occur." );
850 
851  QgsProjectFileTransform projectFile( *doc, fileVersion );
852 
853  // Shows a warning when an old project file is read.
854  emit oldProjectVersionWarning( fileVersion.text() );
855  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
856 
857  projectFile.updateRevision( thisVersion );
858  }
859 
860  // start new project, just keep the file name and auxiliary storage
861  QString fileName = mFile.fileName();
862  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
863  clear();
864  mAuxiliaryStorage = std::move( aStorage );
865  mFile.setFileName( fileName );
866 
867  // now get any properties
868  _getProperties( *doc, mProperties );
869 
870  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
871 
872  dump_( mProperties );
873 
874  // now get project title
875  _getTitle( *doc, mTitle );
876 
877  QgsReadWriteContext context;
878  context.setPathResolver( pathResolver() );
879 
880  //crs
881  QgsCoordinateReferenceSystem projectCrs;
882  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
883  {
884  // first preference - dedicated projectCrs node
885  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
886  if ( !srsNode.isNull() )
887  {
888  projectCrs.readXml( srsNode );
889  }
890 
891  if ( !projectCrs.isValid() )
892  {
893  // else we try using the stored proj4 string - it's consistent across different QGIS installs,
894  // whereas the srsid can vary (e.g. for custom projections)
895  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
896  if ( !projCrsString.isEmpty() )
897  {
898  projectCrs = QgsCoordinateReferenceSystem::fromProj4( projCrsString );
899  }
900  // last try using crs id - most fragile
901  if ( !projectCrs.isValid() )
902  {
903  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
904  if ( currentCRS != -1 && currentCRS < USER_CRS_START_ID )
905  {
906  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
907  }
908  }
909  }
910  }
911  mCrs = projectCrs;
912 
913  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
914  if ( nl.count() )
915  {
916  QDomElement transactionElement = nl.at( 0 ).toElement();
917  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
918  mAutoTransaction = true;
919  }
920 
921  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
922  if ( nl.count() )
923  {
924  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
925  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
926  mEvaluateDefaultValues = true;
927  }
928 
929  nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
930  if ( nl.count() )
931  {
932  QDomElement trustElement = nl.at( 0 ).toElement();
933  if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
934  mTrustLayerMetadata = true;
935  }
936 
937  // read the layer tree from project file
938 
939  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
940 
941  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
942  if ( !layerTreeElem.isNull() )
943  {
944  mRootGroup->readChildrenFromXml( layerTreeElem, context );
945  }
946  else
947  {
948  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
949  }
950 
951  mLayerTreeRegistryBridge->setEnabled( false );
952 
953  // get the map layers
954  QList<QDomNode> brokenNodes;
955  bool clean = _getMapLayers( *doc, brokenNodes );
956 
957  // review the integrity of the retrieved map layers
958  if ( !clean )
959  {
960  QgsDebugMsg( "Unable to get map layers from project file." );
961 
962  if ( !brokenNodes.isEmpty() )
963  {
964  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
965  }
966 
967  // we let a custom handler decide what to do with missing layers
968  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
969  mBadLayerHandler->handleBadLayers( brokenNodes );
970  }
971 
972  // Resolve references to other layers
973  // Needs to be done here once all dependent layers are loaded
974  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
975  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
976  {
977  it.value()->resolveReferences( this );
978  }
979 
980  mLayerTreeRegistryBridge->setEnabled( true );
981 
982  // load embedded groups and layers
983  loadEmbeddedNodes( mRootGroup );
984 
985  // now that layers are loaded, we can resolve layer tree's references to the layers
986  mRootGroup->resolveReferences( this );
987 
988 
989  if ( !layerTreeElem.isNull() )
990  {
991  mRootGroup->readLayerOrderFromXml( layerTreeElem );
992  }
993  else
994  {
995  // Load pre 3.0 configuration
996  QDomElement elem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
997  mRootGroup->readLayerOrderFromXml( elem );
998  }
999 
1000  // make sure the are just valid layers
1002 
1003  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1004 
1005  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1007  mMapThemeCollection->readXml( *doc );
1008 
1009  mLabelingEngineSettings->readSettingsFromProject( this );
1011 
1012  mAnnotationManager->readXml( doc->documentElement(), context );
1013  mLayoutManager->readXml( doc->documentElement(), *doc );
1014 
1015  // reassign change dependencies now that all layers are loaded
1016  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1017  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1018  {
1019  it.value()->setDependencies( it.value()->dependencies() );
1020  }
1021 
1022  mSnappingConfig.readProject( *doc );
1023  emit snappingConfigChanged( mSnappingConfig );
1024 
1025  //add variables defined in project file
1026  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1027  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1028 
1029  mCustomVariables.clear();
1030  if ( variableNames.length() == variableValues.length() )
1031  {
1032  for ( int i = 0; i < variableNames.length(); ++i )
1033  {
1034  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1035  }
1036  }
1037  else
1038  {
1039  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1040  }
1041  emit customVariablesChanged();
1042 
1043  // read the project: used by map canvas and legend
1044  emit readProject( *doc );
1045 
1046  // if all went well, we're allegedly in pristine state
1047  if ( clean )
1048  setDirty( false );
1049 
1051  emit crsChanged();
1052  emit ellipsoidChanged( ellipsoid() );
1053 
1054  return true;
1055 }
1056 
1057 
1058 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group )
1059 {
1060 
1061  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1062  {
1063  if ( QgsLayerTree::isGroup( child ) )
1064  {
1065  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1066  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1067  {
1068  // make sure to convert the path from relative to absolute
1069  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1070  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1071  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList() );
1072  if ( newGroup )
1073  {
1074  QList<QgsLayerTreeNode *> clonedChildren;
1075  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
1076  clonedChildren << newGroupChild->clone();
1077  delete newGroup;
1078 
1079  childGroup->insertChildNodes( 0, clonedChildren );
1080  }
1081  }
1082  else
1083  {
1084  loadEmbeddedNodes( childGroup );
1085  }
1086  }
1087  else if ( QgsLayerTree::isLayer( child ) )
1088  {
1089  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1090  {
1091  QList<QDomNode> brokenNodes;
1092  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes );
1093  }
1094  }
1095 
1096  }
1097 }
1098 
1099 QVariantMap QgsProject::customVariables() const
1100 {
1101  return mCustomVariables;
1102 }
1103 
1104 void QgsProject::setCustomVariables( const QVariantMap &variables )
1105 {
1106  if ( variables == mCustomVariables )
1107  return;
1108 
1109  //write variable to project
1110  QStringList variableNames;
1111  QStringList variableValues;
1112 
1113  QVariantMap::const_iterator it = variables.constBegin();
1114  for ( ; it != variables.constEnd(); ++it )
1115  {
1116  variableNames << it.key();
1117  variableValues << it.value().toString();
1118  }
1119 
1120  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1121  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1122 
1123  mCustomVariables = variables;
1124 
1125  emit customVariablesChanged();
1126 }
1127 
1129 {
1130  *mLabelingEngineSettings = settings;
1132 }
1133 
1135 {
1136  return *mLabelingEngineSettings;
1137 }
1138 
1140 {
1141  return mLayerStore.get();
1142 }
1143 
1145 {
1146  return mLayerStore.get();
1147 }
1148 
1149 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1150 {
1151  QList<QgsVectorLayer *> layers;
1152  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1153  Q_FOREACH ( const QString &layerId, layerIds )
1154  {
1155  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1156  layers << vlayer;
1157  }
1158  return layers;
1159 }
1160 
1161 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1162 {
1163  QStringList list;
1164  Q_FOREACH ( QgsVectorLayer *layer, layers )
1165  list << layer->id();
1166  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1168 }
1169 
1171 {
1172  QgsExpressionContext context;
1173 
1176 
1177  return context;
1178 }
1179 
1180 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1181 {
1182  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1183 
1184  bool tgChanged = false;
1185 
1186  Q_FOREACH ( QgsMapLayer *layer, layers )
1187  {
1188  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1189  if ( vlayer )
1190  {
1191  if ( autoTransaction() )
1192  {
1193  if ( QgsTransaction::supportsTransaction( vlayer ) )
1194  {
1195  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1196  QString key = vlayer->providerType();
1197 
1198  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1199 
1200  if ( !tg )
1201  {
1202  tg = new QgsTransactionGroup();
1203  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1204  tgChanged = true;
1205  }
1206  tg->addLayer( vlayer );
1207  }
1208  }
1210  }
1211 
1212  if ( tgChanged )
1213  emit transactionGroupsChanged();
1214 
1215  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1216 
1217  // check if we have to update connections for layers with dependencies
1218  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1219  {
1220  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1221  if ( deps.contains( layer->id() ) )
1222  {
1223  // reconnect to change signals
1224  it.value()->setDependencies( deps );
1225  }
1226  }
1227  }
1228 
1229  if ( mSnappingConfig.addLayers( layers ) )
1230  emit snappingConfigChanged( mSnappingConfig );
1231 }
1232 
1233 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1234 {
1235  if ( mSnappingConfig.removeLayers( layers ) )
1236  emit snappingConfigChanged( mSnappingConfig );
1237 }
1238 
1239 void QgsProject::cleanTransactionGroups( bool force )
1240 {
1241  bool changed = false;
1242  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1243  {
1244  if ( tg.value()->isEmpty() || force )
1245  {
1246  delete tg.value();
1247  tg = mTransactionGroups.erase( tg );
1248  changed = true;
1249  }
1250  else
1251  {
1252  ++tg;
1253  }
1254  }
1255  if ( changed )
1256  emit transactionGroupsChanged();
1257 }
1258 
1259 bool QgsProject::readLayer( const QDomNode &layerNode )
1260 {
1261  QgsReadWriteContext context;
1262  context.setPathResolver( pathResolver() );
1263  QList<QDomNode> brokenNodes;
1264  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
1265  {
1266  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1267  // added layer for joins
1268  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1269  Q_FOREACH ( QgsVectorLayer *layer, vectorLayers )
1270  {
1271  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1272  layer->resolveReferences( this );
1273  }
1274 
1275  return true;
1276  }
1277  return false;
1278 }
1279 
1280 bool QgsProject::write( const QString &filename )
1281 {
1282  mFile.setFileName( filename );
1283 
1284  return write();
1285 }
1286 
1288 {
1289  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1290  {
1291  return zip( mFile.fileName() );
1292  }
1293  else
1294  {
1295  // write project file even if the auxiliary storage is not correctly
1296  // saved
1297  const bool asOk = saveAuxiliaryStorage();
1298  const bool writeOk = writeProjectFile( mFile.fileName() );
1299 
1300  // errors raised during writing project file are more important
1301  if ( !asOk && writeOk )
1302  setError( tr( "Unable to save auxiliary storage" ) );
1303 
1304  return asOk && writeOk;
1305  }
1306 }
1307 
1308 bool QgsProject::writeProjectFile( const QString &filename )
1309 {
1310  QFile projectFile( filename );
1311  clearError();
1312 
1313  // if we have problems creating or otherwise writing to the project file,
1314  // let's find out up front before we go through all the hand-waving
1315  // necessary to create all the Dom objects
1316  QFileInfo myFileInfo( projectFile );
1317  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1318  {
1319  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1320  .arg( projectFile.fileName() ) );
1321  return false;
1322  }
1323 
1324  QgsReadWriteContext context;
1325  context.setPathResolver( pathResolver() );
1326 
1327  QDomImplementation DomImplementation;
1328  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1329 
1330  QDomDocumentType documentType =
1331  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1332  QStringLiteral( "SYSTEM" ) );
1333  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1334 
1335  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1336  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1337  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1338 
1339  doc->appendChild( qgisNode );
1340 
1341  // title
1342  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1343  qgisNode.appendChild( titleNode );
1344 
1345  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1346  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? "1" : "0" );
1347  qgisNode.appendChild( transactionNode );
1348 
1349  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1350  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? "1" : "0" );
1351  qgisNode.appendChild( evaluateDefaultValuesNode );
1352 
1353  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
1354  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? "1" : "0" );
1355  qgisNode.appendChild( trustNode );
1356 
1357  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1358  titleNode.appendChild( titleText );
1359 
1360  // write project CRS
1361  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
1362  mCrs.writeXml( srsNode, *doc );
1363  qgisNode.appendChild( srsNode );
1364 
1365  // write layer tree - make sure it is without embedded subgroups
1366  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1368  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
1369 
1370  clonedRoot->writeXml( qgisNode, context );
1371  delete clonedRoot;
1372 
1373  mSnappingConfig.writeProject( *doc );
1374 
1375  // let map canvas and legend write their information
1376  emit writeProject( *doc );
1377 
1378  // within top level node save list of layers
1379  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
1380 
1381  // Iterate over layers in zOrder
1382  // Call writeXml() on each
1383  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1384 
1385  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
1386  while ( li != layers.end() )
1387  {
1388  QgsMapLayer *ml = li.value();
1389 
1390  if ( ml )
1391  {
1392  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1393  if ( emIt == mEmbeddedLayers.constEnd() )
1394  {
1395  // general layer metadata
1396  QDomElement maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1397 
1398  ml->writeLayerXml( maplayerElem, *doc, context );
1399 
1400  emit writeMapLayer( ml, maplayerElem, *doc );
1401 
1402  projectLayersNode.appendChild( maplayerElem );
1403  }
1404  else
1405  {
1406  // layer defined in an external project file
1407  // only save embedded layer if not managed by a legend group
1408  if ( emIt.value().second )
1409  {
1410  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1411  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
1412  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
1413  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
1414  projectLayersNode.appendChild( mapLayerElem );
1415  }
1416  }
1417  }
1418  li++;
1419  }
1420 
1421  qgisNode.appendChild( projectLayersNode );
1422 
1423  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
1424  Q_FOREACH ( QgsMapLayer *layer, mRootGroup->customLayerOrder() )
1425  {
1426  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
1427  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
1428  layerOrderNode.appendChild( mapLayerElem );
1429  }
1430  qgisNode.appendChild( layerOrderNode );
1431 
1432 
1433  // now add the optional extra properties
1434 
1435  dump_( mProperties );
1436 
1437  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
1438 
1439  if ( !mProperties.isEmpty() ) // only worry about properties if we
1440  // actually have any properties
1441  {
1442  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
1443  }
1444 
1445  mMapThemeCollection->writeXml( *doc );
1446 
1447  mLabelingEngineSettings->writeSettingsToProject( this );
1448 
1449  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
1450  qgisNode.appendChild( annotationsElem );
1451 
1452  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
1453  qgisNode.appendChild( layoutElem );
1454 
1455  // now wrap it up and ship it to the project file
1456  doc->normalize(); // XXX I'm not entirely sure what this does
1457 
1458  // Create backup file
1459  if ( QFile::exists( fileName() ) )
1460  {
1461  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
1462  bool ok = true;
1463  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1464  ok &= projectFile.open( QIODevice::ReadOnly );
1465 
1466  QByteArray ba;
1467  while ( ok && !projectFile.atEnd() )
1468  {
1469  ba = projectFile.read( 10240 );
1470  ok &= backupFile.write( ba ) == ba.size();
1471  }
1472 
1473  projectFile.close();
1474  backupFile.close();
1475 
1476  if ( !ok )
1477  {
1478  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1479  return false;
1480  }
1481 
1482  QFileInfo fi( fileName() );
1483  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1484  utime( backupFile.fileName().toUtf8().constData(), &tb );
1485  }
1486 
1487  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1488  {
1489  projectFile.close(); // even though we got an error, let's make
1490  // sure it's closed anyway
1491 
1492  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
1493  return false;
1494  }
1495 
1496  QTemporaryFile tempFile;
1497  bool ok = tempFile.open();
1498  if ( ok )
1499  {
1500  QTextStream projectFileStream( &tempFile );
1501  doc->save( projectFileStream, 2 ); // save as utf-8
1502  ok &= projectFileStream.pos() > -1;
1503 
1504  ok &= tempFile.seek( 0 );
1505 
1506  QByteArray ba;
1507  while ( ok && !tempFile.atEnd() )
1508  {
1509  ba = tempFile.read( 10240 );
1510  ok &= projectFile.write( ba ) == ba.size();
1511  }
1512 
1513  ok &= projectFile.error() == QFile::NoError;
1514 
1515  projectFile.close();
1516  }
1517 
1518  tempFile.close();
1519 
1520  if ( !ok )
1521  {
1522  setError( tr( "Unable to save to file %1. Your project "
1523  "may be corrupted on disk. Try clearing some space on the volume and "
1524  "check file permissions before pressing save again." )
1525  .arg( projectFile.fileName() ) );
1526  return false;
1527  }
1528 
1529  setDirty( false ); // reset to pristine state
1530 
1531  emit projectSaved();
1532 
1533  return true;
1534 }
1535 
1536 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
1537 {
1538  setDirty( true );
1539 
1540  return addKey_( scope, key, &mProperties, value );
1541 }
1542 
1543 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
1544 {
1545  setDirty( true );
1546 
1547  return addKey_( scope, key, &mProperties, value );
1548 }
1549 
1550 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
1551 {
1552  setDirty( true );
1553 
1554  return addKey_( scope, key, &mProperties, value );
1555 }
1556 
1557 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
1558 {
1559  setDirty( true );
1560 
1561  return addKey_( scope, key, &mProperties, value );
1562 }
1563 
1564 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
1565 {
1566  setDirty( true );
1567 
1568  return addKey_( scope, key, &mProperties, value );
1569 }
1570 
1571 QStringList QgsProject::readListEntry( const QString &scope,
1572  const QString &key,
1573  const QStringList &def,
1574  bool *ok ) const
1575 {
1576  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1577 
1578  QVariant value;
1579 
1580  if ( property )
1581  {
1582  value = property->value();
1583 
1584  bool valid = QVariant::StringList == value.type();
1585  if ( ok )
1586  *ok = valid;
1587 
1588  if ( valid )
1589  {
1590  return value.toStringList();
1591  }
1592  }
1593 
1594  return def;
1595 }
1596 
1597 
1598 QString QgsProject::readEntry( const QString &scope,
1599  const QString &key,
1600  const QString &def,
1601  bool *ok ) const
1602 {
1603  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1604 
1605  QVariant value;
1606 
1607  if ( property )
1608  {
1609  value = property->value();
1610 
1611  bool valid = value.canConvert( QVariant::String );
1612  if ( ok )
1613  *ok = valid;
1614 
1615  if ( valid )
1616  return value.toString();
1617  }
1618 
1619  return def;
1620 }
1621 
1622 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
1623  bool *ok ) const
1624 {
1625  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1626 
1627  QVariant value;
1628 
1629  if ( property )
1630  {
1631  value = property->value();
1632  }
1633 
1634  bool valid = value.canConvert( QVariant::Int );
1635 
1636  if ( ok )
1637  {
1638  *ok = valid;
1639  }
1640 
1641  if ( valid )
1642  {
1643  return value.toInt();
1644  }
1645 
1646  return def;
1647 }
1648 
1649 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
1650  double def,
1651  bool *ok ) const
1652 {
1653  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1654  if ( property )
1655  {
1656  QVariant value = property->value();
1657 
1658  bool valid = value.canConvert( QVariant::Double );
1659  if ( ok )
1660  *ok = valid;
1661 
1662  if ( valid )
1663  return value.toDouble();
1664  }
1665 
1666  return def;
1667 }
1668 
1669 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
1670  bool *ok ) const
1671 {
1672  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1673 
1674  if ( property )
1675  {
1676  QVariant value = property->value();
1677 
1678  bool valid = value.canConvert( QVariant::Bool );
1679  if ( ok )
1680  *ok = valid;
1681 
1682  if ( valid )
1683  return value.toBool();
1684  }
1685 
1686  return def;
1687 }
1688 
1689 
1690 bool QgsProject::removeEntry( const QString &scope, const QString &key )
1691 {
1692  removeKey_( scope, key, mProperties );
1693 
1694  setDirty( true );
1695 
1696  return !findKey_( scope, key, mProperties );
1697 }
1698 
1699 
1700 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
1701 {
1702  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1703 
1704  QStringList entries;
1705 
1706  if ( foundProperty )
1707  {
1708  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
1709 
1710  if ( propertyKey )
1711  { propertyKey->entryList( entries ); }
1712  }
1713 
1714  return entries;
1715 }
1716 
1717 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
1718 {
1719  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1720 
1721  QStringList entries;
1722 
1723  if ( foundProperty )
1724  {
1725  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
1726 
1727  if ( propertyKey )
1728  { propertyKey->subkeyList( entries ); }
1729  }
1730 
1731  return entries;
1732 }
1733 
1735 {
1736  dump_( mProperties );
1737 }
1738 
1740 {
1741  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
1742  return QgsPathResolver( absolutePaths ? QString() : fileName() );
1743 }
1744 
1745 QString QgsProject::readPath( const QString &src ) const
1746 {
1747  return pathResolver().readPath( src );
1748 }
1749 
1750 QString QgsProject::writePath( const QString &src ) const
1751 {
1752  return pathResolver().writePath( src );
1753 }
1754 
1755 void QgsProject::setError( const QString &errorMessage )
1756 {
1757  mErrorMessage = errorMessage;
1758 }
1759 
1760 QString QgsProject::error() const
1761 {
1762  return mErrorMessage;
1763 }
1764 
1765 void QgsProject::clearError()
1766 {
1767  setError( QString() );
1768 }
1769 
1771 {
1772  delete mBadLayerHandler;
1773  mBadLayerHandler = handler;
1774 }
1775 
1776 QString QgsProject::layerIsEmbedded( const QString &id ) const
1777 {
1778  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1779  if ( it == mEmbeddedLayers.constEnd() )
1780  {
1781  return QString();
1782  }
1783  return it.value().first;
1784 }
1785 
1786 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1787  bool saveFlag )
1788 {
1789  QgsDebugCall;
1790 
1791  static QString sPrevProjectFilePath;
1792  static QDateTime sPrevProjectFileTimestamp;
1793  static QDomDocument sProjectDocument;
1794 
1795  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1796 
1797  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
1798  {
1799  sPrevProjectFilePath.clear();
1800 
1801  QFile projectFile( projectFilePath );
1802  if ( !projectFile.open( QIODevice::ReadOnly ) )
1803  {
1804  return false;
1805  }
1806 
1807  if ( !sProjectDocument.setContent( &projectFile ) )
1808  {
1809  return false;
1810  }
1811 
1812  sPrevProjectFilePath = projectFilePath;
1813  sPrevProjectFileTimestamp = projectFileTimestamp;
1814  }
1815 
1816  // does project store paths absolute or relative?
1817  bool useAbsolutePaths = true;
1818 
1819  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1820  if ( !propertiesElem.isNull() )
1821  {
1822  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
1823  if ( !absElem.isNull() )
1824  {
1825  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
1826  }
1827  }
1828 
1829  QgsReadWriteContext embeddedContext;
1830  if ( !useAbsolutePaths )
1831  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
1832 
1833  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
1834  if ( projectLayersElem.isNull() )
1835  {
1836  return false;
1837  }
1838 
1839  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
1840  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1841  {
1842  // get layer id
1843  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1844  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
1845  if ( id == layerId )
1846  {
1847  // layer can be embedded only once
1848  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1849  {
1850  return false;
1851  }
1852 
1853  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1854 
1855  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext ) )
1856  {
1857  return true;
1858  }
1859  else
1860  {
1861  mEmbeddedLayers.remove( layerId );
1862  return false;
1863  }
1864  }
1865  }
1866 
1867  return false;
1868 }
1869 
1870 
1871 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1872 {
1873  // open project file, get layer ids in group, add the layers
1874  QFile projectFile( projectFilePath );
1875  if ( !projectFile.open( QIODevice::ReadOnly ) )
1876  {
1877  return nullptr;
1878  }
1879 
1880  QDomDocument projectDocument;
1881  if ( !projectDocument.setContent( &projectFile ) )
1882  {
1883  return nullptr;
1884  }
1885 
1886  QgsReadWriteContext context;
1887  context.setPathResolver( pathResolver() );
1888 
1889  // store identify disabled layers of the embedded project
1890  QSet<QString> embeddedIdentifyDisabledLayers;
1891  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) ).firstChildElement( QStringLiteral( "Identify" ) ).firstChildElement( QStringLiteral( "disabledLayers" ) );
1892  if ( !disabledLayersElem.isNull() )
1893  {
1894  QDomNodeList valueList = disabledLayersElem.elementsByTagName( QStringLiteral( "value" ) );
1895  for ( int i = 0; i < valueList.size(); ++i )
1896  {
1897  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1898  }
1899  }
1900 
1902 
1903  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1904  if ( !layerTreeElem.isNull() )
1905  {
1906  root->readChildrenFromXml( layerTreeElem, context );
1907  }
1908  else
1909  {
1910  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1911  }
1912 
1913  QgsLayerTreeGroup *group = root->findGroup( groupName );
1914  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
1915  {
1916  // embedded groups cannot be embedded again
1917  delete root;
1918  return nullptr;
1919  }
1920 
1921  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1922  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1923  delete root;
1924  root = nullptr;
1925 
1926  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
1927  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
1928 
1929  // set "embedded" to all children + load embedded layers
1930  mLayerTreeRegistryBridge->setEnabled( false );
1931  initializeEmbeddedSubtree( projectFilePath, newGroup );
1932  mLayerTreeRegistryBridge->setEnabled( true );
1933 
1934  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
1935 
1936  // consider the layers might be identify disabled in its project
1937  Q_FOREACH ( const QString &layerId, newGroup->findLayerIds() )
1938  {
1939  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1940  {
1941  thisProjectIdentifyDisabledLayers.append( layerId );
1942  }
1943 
1944  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1945  if ( layer )
1946  {
1947  layer->setItemVisibilityChecked( invisibleLayers.contains( layerId ) );
1948  }
1949  }
1950 
1951  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
1952 
1953  return newGroup;
1954 }
1955 
1956 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group )
1957 {
1958  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1959  {
1960  // all nodes in the subtree will have "embedded" custom property set
1961  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
1962 
1963  if ( QgsLayerTree::isGroup( child ) )
1964  {
1965  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1966  }
1967  else if ( QgsLayerTree::isLayer( child ) )
1968  {
1969  // load the layer into our project
1970  QList<QDomNode> brokenNodes;
1971  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false );
1972  }
1973  }
1974 }
1975 
1977 {
1978  return mEvaluateDefaultValues;
1979 }
1980 
1982 {
1983  const QMap<QString, QgsMapLayer *> layers = mapLayers();
1984  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
1985  for ( ; layerIt != layers.constEnd(); ++layerIt )
1986  {
1987  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
1988  if ( vl )
1989  {
1991  }
1992  }
1993 
1994  mEvaluateDefaultValues = evaluateDefaultValues;
1995 }
1996 
1998 {
1999  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2001 }
2002 
2004 {
2005  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2006 }
2007 
2009 {
2010  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2011  if ( !distanceUnitString.isEmpty() )
2012  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2013 
2014  //fallback to QGIS default measurement unit
2015  QgsSettings s;
2016  bool ok = false;
2017  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2018  return ok ? type : QgsUnitTypes::DistanceMeters;
2019 }
2020 
2022 {
2023  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2024 }
2025 
2027 {
2028  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2029  if ( !areaUnitString.isEmpty() )
2030  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2031 
2032  //fallback to QGIS default area unit
2033  QgsSettings s;
2034  bool ok = false;
2035  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2036  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2037 }
2038 
2040 {
2041  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2042 }
2043 
2044 QString QgsProject::homePath() const
2045 {
2046  QFileInfo pfi( fileName() );
2047  if ( !pfi.exists() )
2048  return QString();
2049 
2050  return pfi.canonicalPath();
2051 }
2052 
2054 {
2055  return mRelationManager;
2056 }
2057 
2059 {
2060  return mLayoutManager.get();
2061 }
2062 
2064 {
2065  return mLayoutManager.get();
2066 }
2067 
2069 {
2070  return mRootGroup;
2071 }
2072 
2074 {
2075  return mMapThemeCollection.get();
2076 }
2077 
2079 {
2080  return mAnnotationManager.get();
2081 }
2082 
2084 {
2085  return mAnnotationManager.get();
2086 }
2087 
2088 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2089 {
2090  QStringList currentLayers = nonIdentifiableLayers();
2091 
2092  QStringList newLayers;
2093  Q_FOREACH ( QgsMapLayer *l, layers )
2094  {
2095  newLayers << l->id();
2096  }
2097 
2098  if ( newLayers == currentLayers )
2099  return;
2100 
2101  QStringList disabledLayerIds;
2102 
2103  Q_FOREACH ( QgsMapLayer *l, layers )
2104  {
2105  disabledLayerIds << l->id();
2106  }
2107 
2108  setNonIdentifiableLayers( disabledLayerIds );
2109 }
2110 
2111 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2112 {
2113  writeEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ), layerIds );
2114 
2115  emit nonIdentifiableLayersChanged( layerIds );
2116 }
2117 
2118 QStringList QgsProject::nonIdentifiableLayers() const
2119 {
2120  return readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
2121 }
2122 
2124 {
2125  return mAutoTransaction;
2126 }
2127 
2129 {
2130  if ( autoTransaction != mAutoTransaction )
2131  {
2132  mAutoTransaction = autoTransaction;
2133 
2134  if ( autoTransaction )
2135  onMapLayersAdded( mapLayers().values() );
2136  else
2137  cleanTransactionGroups( true );
2138  }
2139 }
2140 
2141 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2142 {
2143  return mTransactionGroups;
2144 }
2145 
2146 
2147 //
2148 // QgsMapLayerStore methods
2149 //
2150 
2151 
2153 {
2154  return mLayerStore->count();
2155 }
2156 
2157 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
2158 {
2159  return mLayerStore->mapLayer( layerId );
2160 }
2161 
2162 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
2163 {
2164  return mLayerStore->mapLayersByName( layerName );
2165 }
2166 
2167 bool QgsProject::unzip( const QString &filename )
2168 {
2169  clearError();
2170  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2171 
2172  // unzip the archive
2173  if ( !archive->unzip( filename ) )
2174  {
2175  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
2176  return false;
2177  }
2178 
2179  // test if zip provides a .qgs file
2180  if ( archive->projectFile().isEmpty() )
2181  {
2182  setError( tr( "Zip archive does not provide a project file" ) );
2183  return false;
2184  }
2185 
2186  // load auxiliary storage
2187  if ( !archive->auxiliaryStorageFile().isEmpty() )
2188  {
2189  // database file is already a copy as it's been unzipped. So we don't open
2190  // auxiliary storage in copy mode in this case
2191  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
2192  }
2193  else
2194  {
2195  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
2196  }
2197 
2198  // read the project file
2199  if ( ! readProjectFile( archive->projectFile() ) )
2200  {
2201  setError( tr( "Cannot read unzipped qgs project file" ) );
2202  return false;
2203  }
2204 
2205  // keep the archive and remove the temporary .qgs file
2206  mArchive = std::move( archive );
2207  mArchive->clearProjectFile();
2208 
2209  return true;
2210 }
2211 
2212 bool QgsProject::zip( const QString &filename )
2213 {
2214  clearError();
2215 
2216  // save the current project in a temporary .qgs file
2217  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2218  const QString baseName = QFileInfo( filename ).baseName();
2219  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
2220  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
2221 
2222  bool writeOk = false;
2223  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2224  {
2225  writeOk = writeProjectFile( qgsFile.fileName() );
2226  qgsFile.close();
2227  }
2228 
2229  // stop here with an error message
2230  if ( ! writeOk )
2231  {
2232  setError( tr( "Unable to write temporary qgs file" ) );
2233  return false;
2234  }
2235 
2236  // save auxiliary storage
2237  const QFileInfo info( qgsFile );
2238  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
2239 
2240  if ( ! saveAuxiliaryStorage( asFileName ) )
2241  {
2242  setError( tr( "Unable to save auxiliary storage" ) );
2243  return false;
2244  }
2245 
2246  // create the archive
2247  archive->addFile( qgsFile.fileName() );
2248  archive->addFile( asFileName );
2249 
2250  // zip
2251  if ( !archive->zip( filename ) )
2252  {
2253  setError( tr( "Unable to perform zip" ) );
2254  return false;
2255  }
2256 
2257  return true;
2258 }
2259 
2261 {
2262  return QgsZipUtils::isZipFile( mFile.fileName() );
2263 }
2264 
2265 QList<QgsMapLayer *> QgsProject::addMapLayers(
2266  const QList<QgsMapLayer *> &layers,
2267  bool addToLegend,
2268  bool takeOwnership )
2269 {
2270  QList<QgsMapLayer *> myResultList = mLayerStore->addMapLayers( layers, takeOwnership );
2271  if ( !myResultList.isEmpty() )
2272  {
2273  if ( addToLegend )
2274  emit legendLayersAdded( myResultList );
2275  }
2276 
2277  if ( mAuxiliaryStorage )
2278  {
2279  for ( QgsMapLayer *mlayer : myResultList )
2280  {
2281  if ( mlayer->type() != QgsMapLayer::VectorLayer )
2282  continue;
2283 
2284  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
2285  if ( vl )
2286  {
2287  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
2288  }
2289  }
2290  }
2291 
2292  return myResultList;
2293 }
2294 
2295 QgsMapLayer *
2297  bool addToLegend,
2298  bool takeOwnership )
2299 {
2300  QList<QgsMapLayer *> addedLayers;
2301  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
2302  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
2303 }
2304 
2305 void QgsProject::removeMapLayers( const QStringList &layerIds )
2306 {
2307  mLayerStore->removeMapLayers( layerIds );
2308 }
2309 
2310 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
2311 {
2312  mLayerStore->removeMapLayers( layers );
2313 }
2314 
2315 void QgsProject::removeMapLayer( const QString &layerId )
2316 {
2317  mLayerStore->removeMapLayer( layerId );
2318 }
2319 
2321 {
2322  mLayerStore->removeMapLayer( layer );
2323 }
2324 
2326 {
2327  return mLayerStore->takeMapLayer( layer );
2328 }
2329 
2331 {
2332  mLayerStore->removeAllMapLayers();
2333 }
2334 
2336 {
2337  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2338  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
2339  for ( ; it != layers.constEnd(); ++it )
2340  {
2341  it.value()->reload();
2342  }
2343 }
2344 
2345 QMap<QString, QgsMapLayer *> QgsProject::mapLayers() const
2346 {
2347  return mLayerStore->mapLayers();
2348 }
2349 
2350 
2352 {
2353  QgsSettings settings;
2354  QgsCoordinateReferenceSystem defaultCrs;
2355  if ( settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "useProject" )
2356  || settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "prompt" ) )
2357  {
2358  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
2359  // (since "prompt" has no meaning here - the prompt will always be shown, it's just deciding on the default choice in the prompt!)
2360  defaultCrs = crs();
2361  }
2362  else
2363  {
2364  // global crs
2365  QString layerDefaultCrs = settings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), GEO_EPSG_CRS_AUTHID ).toString();
2366  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
2367  }
2368 
2369  return defaultCrs;
2370 }
2371 
2373 {
2374  mTrustLayerMetadata = trust;
2375 
2376  auto layers = mapLayers();
2377  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
2378  {
2379  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
2380  if ( vl )
2381  {
2382  vl->setReadExtentFromXml( trust );
2383  }
2384  }
2385 }
2386 
2387 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
2388 {
2389  for ( QgsMapLayer *l : mapLayers().values() )
2390  {
2391  if ( l->type() != QgsMapLayer::VectorLayer )
2392  continue;
2393 
2394  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( l );
2395  if ( vl && vl->auxiliaryLayer() )
2396  {
2397  vl->auxiliaryLayer()->save();
2398  }
2399  }
2400 
2401  if ( !filename.isEmpty() )
2402  {
2403  return mAuxiliaryStorage->saveAs( filename );
2404  }
2405  else
2406  {
2407  return mAuxiliaryStorage->saveAs( *this );
2408  }
2409 }
2410 
2412 {
2413  return mAuxiliaryStorage.get();
2414 }
2415 
2417 {
2418  return mAuxiliaryStorage.get();
2419 }
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:414
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
Return error message from previous read/write.
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:409
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:56
void layersAdded(const QList< QgsMapLayer *> &layers)
Emitted when one or more layers were added to the registry.
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 loadingLayer(const QString &)
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer *> &layers)
A list of layers with which intersections should be avoided.
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:520
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
This class is a composition of two QSettings instances:
Definition: qgssettings.h:55
virtual 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:642
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.
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
static Q_INVOKABLE AreaUnit decodeAreaUnit(const QString &string, bool *ok=0)
Decodes an areal unit from a string.
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:124
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.
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.
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:419
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.
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.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
void reloadAllLayers()
Reload all registered layer&#39;s provider data caches, synchronising the layer with any changes in the d...
virtual 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
Return 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.
QStringList nonIdentifiableLayers() const
Get 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...
QgsMapLayerStore * layerStore()
Returns a pointer to the project&#39;s internal layer store.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
void clear()
Remove any relation managed by this class.
static QString extension()
Returns the extension used for auxiliary databases.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
QgsProjectVersion getVersion(const QDomDocument &doc)
Return the version string found in the given DOM document.
Definition: qgsproject.cpp:619
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
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.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
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.
bool read()
Reads the project from its currently associated file (see fileName() ).
Definition: qgsproject.cpp:775
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()
Get 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:198
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
Return path resolver object with considering whether the project uses absolute or relative paths and ...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:38
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
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.
void readProject(const QDomDocument &)
emitted when 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
Return 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:451
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.
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:81
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:467
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.
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.
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Definition: qgsproject.cpp:267
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
Manages storage of a set of compositions.
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.
void setProviderProperty(ProviderProperty property, const QVariant &value)
Allows setting arbitrary properties on the provider.
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:36
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:80
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.
QgsProject(QObject *parent=0)
Create a new QgsProject.
Definition: qgsproject.cpp:328
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
Return project&#39;s home path.
bool readLayerXml(const QDomElement &layerElement, const QgsReadWriteContext &context)
Sets state from Dom document.
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
Return pointer to the root (invisible) node of the project&#39;s layer tree.
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed...
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
void writeProject(QDomDocument &)
emitted when project is being written
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:626
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.
const int USER_CRS_START_ID
Magick number that determines whether a projection crsid is a system (srs.db) or user (~/...
Definition: qgis.h:427
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
QFileInfo fileInfo() const
Returns QFileInfo object for the project&#39;s associated file.
Definition: qgsproject.cpp:441
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 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.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), const Section section=NoSection) const
Returns the value for setting key.
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:383
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 setTitle(const QString &title)
Sets the project&#39;s title.
Definition: qgsproject.cpp:392
QgsVectorDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
QgsCoordinateReferenceSystem crs() const
Returns the project&#39;s native coordinate reference system.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
An Abstract Base Class for QGIS project property hierarchys.
QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
void homePathChanged()
Emitted when the home path of the project changes.
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
void subkeyList(QStringList &entries) const
Return 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...
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:403
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.
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
Return the provider type for this layer.
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.
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:122
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.
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in Dom node.
virtual QgsLayerTree * clone() const override
Return a clone of the group.
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
Return 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:557
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()
Clear the project - removes all settings and resets it back to an empty, default state.
Definition: qgsproject.cpp:474
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *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.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=0)
Decodes a distance unit from a string.
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)
Set 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 ...
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project&#39;s global labeling engine settings.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.