QGIS API Documentation  3.0.2-Girona (307d082)
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(), Qgis::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  return mTransformContext;
477 }
478 
480 {
481  mTransformContext = context;
483 }
484 
486 {
487  mFile.setFileName( QString() );
488  mProperties.clearKeys();
489  mTitle.clear();
490  mAutoTransaction = false;
491  mEvaluateDefaultValues = false;
492  mDirty = false;
493  mTrustLayerMetadata = false;
494  mCustomVariables.clear();
495 
497  context.readSettings();
498  setTransformContext( context );
499 
500  mEmbeddedLayers.clear();
501  mRelationManager->clear();
502  mAnnotationManager->clear();
503  mLayoutManager->clear();
504  mSnappingConfig.reset();
505  emit snappingConfigChanged( mSnappingConfig );
506 
507  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
509 
510  mLabelingEngineSettings->clear();
511 
512  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
513  mArchive->clear();
514 
516 
517  // reset some default project properties
518  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
519  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
520  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
521  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
522 
523  //copy default units to project
524  QgsSettings s;
525  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
526  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
527 
529  mRootGroup->clear();
530 
531  setDirty( false );
532 }
533 
534 // basically a debugging tool to dump property list values
535 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
536 {
537  QgsDebugMsg( "current properties:" );
538  topQgsPropertyKey.dump();
539 }
540 
541 
572 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
573 {
574  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
575 
576  if ( propertiesElem.isNull() ) // no properties found, so we're done
577  {
578  return;
579  }
580 
581  QDomNodeList scopes = propertiesElem.childNodes();
582 
583  if ( scopes.count() < 1 )
584  {
585  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
586  return;
587  }
588 
589  if ( ! project_properties.readXml( propertiesElem ) )
590  {
591  QgsDebugMsg( "Project_properties.readXml() failed" );
592  }
593 }
594 
595 
600 static void _getTitle( const QDomDocument &doc, QString &title )
601 {
602  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
603 
604  title.clear(); // by default the title will be empty
605 
606  if ( !nl.count() )
607  {
608  QgsDebugMsg( "unable to find title element" );
609  return;
610  }
611 
612  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element OK
613 
614  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
615  {
616  QgsDebugMsg( "unable to find title element" );
617  return;
618  }
619 
620  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
621 
622  if ( !titleTextNode.isText() )
623  {
624  QgsDebugMsg( "unable to find title element" );
625  return;
626  }
627 
628  QDomText titleText = titleTextNode.toText();
629 
630  title = titleText.data();
631 
632 }
633 
634 QgsProjectVersion getVersion( const QDomDocument &doc )
635 {
636  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
637 
638  if ( !nl.count() )
639  {
640  QgsDebugMsg( " unable to find qgis element in project file" );
641  return QgsProjectVersion( 0, 0, 0, QString() );
642  }
643 
644  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
645 
646  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
647  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
648  return projectVersion;
649 }
650 
651 
653 {
654  return mSnappingConfig;
655 }
656 
658 {
659  if ( mSnappingConfig == snappingConfig )
660  return;
661 
662  mSnappingConfig = snappingConfig;
663  setDirty();
664  emit snappingConfigChanged( mSnappingConfig );
665 }
666 
667 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes )
668 {
669  // Layer order is set by the restoring the legend settings from project file.
670  // This is done on the 'readProject( ... )' signal
671 
672  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
673 
674  // process the map layer nodes
675 
676  if ( 0 == nl.count() ) // if we have no layers to process, bail
677  {
678  return true; // Decided to return "true" since it's
679  // possible for there to be a project with no
680  // layers; but also, more imporantly, this
681  // would cause the tests/qgsproject to fail
682  // since the test suite doesn't currently
683  // support test layers
684  }
685 
686  bool returnStatus = true;
687 
688  emit layerLoaded( 0, nl.count() );
689 
690  // order layers based on their dependencies
691  QgsLayerDefinition::DependencySorter depSorter( doc );
692  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
693  return false;
694 
695  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
696 
697  int i = 0;
698  Q_FOREACH ( const QDomNode &node, sortedLayerNodes )
699  {
700  QDomElement element = node.toElement();
701 
702  QString name = node.namedItem( QStringLiteral( "layername" ) ).toElement().text();
703  if ( !name.isNull() )
704  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
705 
706  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
707  {
708  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes );
709  continue;
710  }
711  else
712  {
713  QgsReadWriteContext context;
714  context.setPathResolver( pathResolver() );
715  if ( !addLayer( element, brokenNodes, context ) )
716  {
717  returnStatus = false;
718  }
719  }
720  emit layerLoaded( i + 1, nl.count() );
721  i++;
722  }
723 
724  return returnStatus;
725 }
726 
727 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, const QgsReadWriteContext &context )
728 {
729  QString type = layerElem.attribute( QStringLiteral( "type" ) );
730  QgsDebugMsgLevel( "Layer type is " + type, 4 );
731  QgsMapLayer *mapLayer = nullptr;
732 
733  if ( type == QLatin1String( "vector" ) )
734  {
735  mapLayer = new QgsVectorLayer;
736 
737  // apply specific settings to vector layer
738  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer ) )
739  {
740  vl->setReadExtentFromXml( mTrustLayerMetadata );
741  }
742  }
743  else if ( type == QLatin1String( "raster" ) )
744  {
745  mapLayer = new QgsRasterLayer;
746  }
747  else if ( type == QLatin1String( "plugin" ) )
748  {
749  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
750  mapLayer = QgsApplication::pluginLayerRegistry()->createLayer( typeName );
751  }
752 
753  if ( !mapLayer )
754  {
755  QgsDebugMsg( "Unable to create layer" );
756 
757  return false;
758  }
759 
760  Q_CHECK_PTR( mapLayer ); // NOLINT
761 
762  // have the layer restore state that is stored in Dom node
763  if ( mapLayer->readLayerXml( layerElem, context ) && mapLayer->isValid() )
764  {
765  emit readMapLayer( mapLayer, layerElem );
766 
767  QList<QgsMapLayer *> myLayers;
768  myLayers << mapLayer;
769  addMapLayers( myLayers );
770 
771  return true;
772  }
773  else
774  {
775  delete mapLayer;
776 
777  QgsDebugMsg( "Unable to load " + type + " layer" );
778  brokenNodes.push_back( layerElem );
779  return false;
780  }
781 }
782 
783 bool QgsProject::read( const QString &filename )
784 {
785  mFile.setFileName( filename );
786 
787  return read();
788 }
789 
791 {
792  QString filename = mFile.fileName();
793  bool rc;
794 
795  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
796  {
797  rc = unzip( mFile.fileName() );
798  }
799  else
800  {
801  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
802  rc = readProjectFile( mFile.fileName() );
803  }
804 
805  mFile.setFileName( filename );
806  return rc;
807 }
808 
809 bool QgsProject::readProjectFile( const QString &filename )
810 {
811  QFile projectFile( filename );
812  clearError();
813 
814  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
815 
816  if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
817  {
818  projectFile.close();
819 
820  setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
821 
822  return false;
823  }
824 
825  // location of problem associated with errorMsg
826  int line, column;
827  QString errorMsg;
828 
829  if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
830  {
831  // want to make this class as GUI independent as possible; so commented out
832 #if 0
833  QMessageBox::critical( 0, tr( "Read Project File" ),
834  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
835 #endif
836 
837  QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
838  .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
839 
840  QgsDebugMsg( errorString );
841 
842  projectFile.close();
843 
844  setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
845 
846  return false;
847  }
848 
849  projectFile.close();
850 
851 
852  QgsDebugMsg( "Opened document " + projectFile.fileName() );
853  QgsDebugMsg( "Project title: " + mTitle );
854 
855  // get project version string, if any
856  QgsProjectVersion fileVersion = getVersion( *doc );
857  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
858 
859  if ( thisVersion > fileVersion )
860  {
861  QgsLogger::warning( "Loading a file that was saved with an older "
862  "version of qgis (saved in " + fileVersion.text() +
863  ", loaded in " + Qgis::QGIS_VERSION +
864  "). Problems may occur." );
865 
866  QgsProjectFileTransform projectFile( *doc, fileVersion );
867 
868  // Shows a warning when an old project file is read.
869  emit oldProjectVersionWarning( fileVersion.text() );
870  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
871 
872  projectFile.updateRevision( thisVersion );
873  }
874 
875  // start new project, just keep the file name and auxiliary storage
876  QString fileName = mFile.fileName();
877  std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
878  clear();
879  mAuxiliaryStorage = std::move( aStorage );
880  mFile.setFileName( fileName );
881 
882  // now get any properties
883  _getProperties( *doc, mProperties );
884 
885  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
886 
887  dump_( mProperties );
888 
889  // now get project title
890  _getTitle( *doc, mTitle );
891 
892  QgsReadWriteContext context;
893  context.setPathResolver( pathResolver() );
894 
895  //crs
896  QgsCoordinateReferenceSystem projectCrs;
897  if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
898  {
899  // first preference - dedicated projectCrs node
900  QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
901  if ( !srsNode.isNull() )
902  {
903  projectCrs.readXml( srsNode );
904  }
905 
906  if ( !projectCrs.isValid() )
907  {
908  QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
909  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
910 
911  // try the CRS
912  if ( currentCRS >= 0 )
913  {
914  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
915  }
916 
917  // if that didn't produce a match, try the proj.4 string
918  if ( !projCrsString.isEmpty() && ( !projectCrs.isValid() || projectCrs.toProj4() != projCrsString ) )
919  {
920  projectCrs = QgsCoordinateReferenceSystem::fromProj4( projCrsString );
921  }
922 
923  // last just take the given id
924  if ( !projectCrs.isValid() )
925  {
926  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
927  }
928  }
929  }
930  mCrs = projectCrs;
931 
932  QStringList datumErrors;
933  if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) )
934  {
935  emit missingDatumTransforms( datumErrors );
936  }
938 
939  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
940  if ( nl.count() )
941  {
942  QDomElement transactionElement = nl.at( 0 ).toElement();
943  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
944  mAutoTransaction = true;
945  }
946 
947  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
948  if ( nl.count() )
949  {
950  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
951  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
952  mEvaluateDefaultValues = true;
953  }
954 
955  nl = doc->elementsByTagName( QStringLiteral( "trust" ) );
956  if ( nl.count() )
957  {
958  QDomElement trustElement = nl.at( 0 ).toElement();
959  if ( trustElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
960  mTrustLayerMetadata = true;
961  }
962 
963  // read the layer tree from project file
964 
965  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
966 
967  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
968  if ( !layerTreeElem.isNull() )
969  {
970  mRootGroup->readChildrenFromXml( layerTreeElem, context );
971  }
972  else
973  {
974  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
975  }
976 
977  mLayerTreeRegistryBridge->setEnabled( false );
978 
979  // get the map layers
980  QList<QDomNode> brokenNodes;
981  bool clean = _getMapLayers( *doc, brokenNodes );
982 
983  // review the integrity of the retrieved map layers
984  if ( !clean )
985  {
986  QgsDebugMsg( "Unable to get map layers from project file." );
987 
988  if ( !brokenNodes.isEmpty() )
989  {
990  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
991  }
992 
993  // we let a custom handler decide what to do with missing layers
994  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
995  mBadLayerHandler->handleBadLayers( brokenNodes );
996  }
997 
998  // Resolve references to other layers
999  // Needs to be done here once all dependent layers are loaded
1000  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1001  for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); it++ )
1002  {
1003  it.value()->resolveReferences( this );
1004  }
1005 
1006  mLayerTreeRegistryBridge->setEnabled( true );
1007 
1008  // load embedded groups and layers
1009  loadEmbeddedNodes( mRootGroup );
1010 
1011  // now that layers are loaded, we can resolve layer tree's references to the layers
1012  mRootGroup->resolveReferences( this );
1013 
1014 
1015  if ( !layerTreeElem.isNull() )
1016  {
1017  mRootGroup->readLayerOrderFromXml( layerTreeElem );
1018  }
1019 
1020  // Load pre 3.0 configuration
1021  QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1022  if ( !layerTreeCanvasElem.isNull( ) )
1023  {
1024  mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1025  }
1026 
1027  // make sure the are just valid layers
1029 
1030  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1031 
1032  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1034  mMapThemeCollection->readXml( *doc );
1035 
1036  mLabelingEngineSettings->readSettingsFromProject( this );
1038 
1039  mAnnotationManager->readXml( doc->documentElement(), context );
1040  mLayoutManager->readXml( doc->documentElement(), *doc );
1041 
1042  // reassign change dependencies now that all layers are loaded
1043  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1044  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1045  {
1046  it.value()->setDependencies( it.value()->dependencies() );
1047  }
1048 
1049  mSnappingConfig.readProject( *doc );
1050 
1051  //add variables defined in project file
1052  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1053  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1054 
1055  mCustomVariables.clear();
1056  if ( variableNames.length() == variableValues.length() )
1057  {
1058  for ( int i = 0; i < variableNames.length(); ++i )
1059  {
1060  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1061  }
1062  }
1063  else
1064  {
1065  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1066  }
1067  emit customVariablesChanged();
1068  emit crsChanged();
1069  emit ellipsoidChanged( ellipsoid() );
1070 
1071  // read the project: used by map canvas and legend
1072  emit readProject( *doc );
1073  emit snappingConfigChanged( mSnappingConfig );
1074 
1075  // if all went well, we're allegedly in pristine state
1076  if ( clean )
1077  setDirty( false );
1078 
1080 
1081  return true;
1082 }
1083 
1084 
1085 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group )
1086 {
1087 
1088  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1089  {
1090  if ( QgsLayerTree::isGroup( child ) )
1091  {
1092  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
1093  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1094  {
1095  // make sure to convert the path from relative to absolute
1096  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
1097  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
1098  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList() );
1099  if ( newGroup )
1100  {
1101  QList<QgsLayerTreeNode *> clonedChildren;
1102  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
1103  clonedChildren << newGroupChild->clone();
1104  delete newGroup;
1105 
1106  childGroup->insertChildNodes( 0, clonedChildren );
1107  }
1108  }
1109  else
1110  {
1111  loadEmbeddedNodes( childGroup );
1112  }
1113  }
1114  else if ( QgsLayerTree::isLayer( child ) )
1115  {
1116  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1117  {
1118  QList<QDomNode> brokenNodes;
1119  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes );
1120  }
1121  }
1122 
1123  }
1124 }
1125 
1126 QVariantMap QgsProject::customVariables() const
1127 {
1128  return mCustomVariables;
1129 }
1130 
1131 void QgsProject::setCustomVariables( const QVariantMap &variables )
1132 {
1133  if ( variables == mCustomVariables )
1134  return;
1135 
1136  //write variable to project
1137  QStringList variableNames;
1138  QStringList variableValues;
1139 
1140  QVariantMap::const_iterator it = variables.constBegin();
1141  for ( ; it != variables.constEnd(); ++it )
1142  {
1143  variableNames << it.key();
1144  variableValues << it.value().toString();
1145  }
1146 
1147  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1148  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1149 
1150  mCustomVariables = variables;
1151 
1152  emit customVariablesChanged();
1153 }
1154 
1156 {
1157  *mLabelingEngineSettings = settings;
1159 }
1160 
1162 {
1163  return *mLabelingEngineSettings;
1164 }
1165 
1167 {
1168  return mLayerStore.get();
1169 }
1170 
1172 {
1173  return mLayerStore.get();
1174 }
1175 
1176 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1177 {
1178  QList<QgsVectorLayer *> layers;
1179  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1180  Q_FOREACH ( const QString &layerId, layerIds )
1181  {
1182  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1183  layers << vlayer;
1184  }
1185  return layers;
1186 }
1187 
1188 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1189 {
1190  QStringList list;
1191  Q_FOREACH ( QgsVectorLayer *layer, layers )
1192  list << layer->id();
1193  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1195 }
1196 
1198 {
1199  QgsExpressionContext context;
1200 
1203 
1204  return context;
1205 }
1206 
1207 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1208 {
1209  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1210 
1211  bool tgChanged = false;
1212 
1213  Q_FOREACH ( QgsMapLayer *layer, layers )
1214  {
1215  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1216  if ( vlayer )
1217  {
1218  if ( autoTransaction() )
1219  {
1220  if ( QgsTransaction::supportsTransaction( vlayer ) )
1221  {
1222  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1223  QString key = vlayer->providerType();
1224 
1225  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1226 
1227  if ( !tg )
1228  {
1229  tg = new QgsTransactionGroup();
1230  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1231  tgChanged = true;
1232  }
1233  tg->addLayer( vlayer );
1234  }
1235  }
1237  }
1238 
1239  if ( tgChanged )
1240  emit transactionGroupsChanged();
1241 
1242  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1243 
1244  // check if we have to update connections for layers with dependencies
1245  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1246  {
1247  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1248  if ( deps.contains( layer->id() ) )
1249  {
1250  // reconnect to change signals
1251  it.value()->setDependencies( deps );
1252  }
1253  }
1254  }
1255 
1256  if ( mSnappingConfig.addLayers( layers ) )
1257  emit snappingConfigChanged( mSnappingConfig );
1258 }
1259 
1260 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1261 {
1262  if ( mSnappingConfig.removeLayers( layers ) )
1263  emit snappingConfigChanged( mSnappingConfig );
1264 }
1265 
1266 void QgsProject::cleanTransactionGroups( bool force )
1267 {
1268  bool changed = false;
1269  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1270  {
1271  if ( tg.value()->isEmpty() || force )
1272  {
1273  delete tg.value();
1274  tg = mTransactionGroups.erase( tg );
1275  changed = true;
1276  }
1277  else
1278  {
1279  ++tg;
1280  }
1281  }
1282  if ( changed )
1283  emit transactionGroupsChanged();
1284 }
1285 
1286 bool QgsProject::readLayer( const QDomNode &layerNode )
1287 {
1288  QgsReadWriteContext context;
1289  context.setPathResolver( pathResolver() );
1290  QList<QDomNode> brokenNodes;
1291  if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
1292  {
1293  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1294  // added layer for joins
1295  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1296  Q_FOREACH ( QgsVectorLayer *layer, vectorLayers )
1297  {
1298  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1299  layer->resolveReferences( this );
1300  }
1301 
1302  return true;
1303  }
1304  return false;
1305 }
1306 
1307 bool QgsProject::write( const QString &filename )
1308 {
1309  mFile.setFileName( filename );
1310 
1311  return write();
1312 }
1313 
1315 {
1316  if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1317  {
1318  return zip( mFile.fileName() );
1319  }
1320  else
1321  {
1322  // write project file even if the auxiliary storage is not correctly
1323  // saved
1324  const bool asOk = saveAuxiliaryStorage();
1325  const bool writeOk = writeProjectFile( mFile.fileName() );
1326 
1327  // errors raised during writing project file are more important
1328  if ( !asOk && writeOk )
1329  setError( tr( "Unable to save auxiliary storage" ) );
1330 
1331  return asOk && writeOk;
1332  }
1333 }
1334 
1335 bool QgsProject::writeProjectFile( const QString &filename )
1336 {
1337  QFile projectFile( filename );
1338  clearError();
1339 
1340  // if we have problems creating or otherwise writing to the project file,
1341  // let's find out up front before we go through all the hand-waving
1342  // necessary to create all the Dom objects
1343  QFileInfo myFileInfo( projectFile );
1344  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1345  {
1346  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1347  .arg( projectFile.fileName() ) );
1348  return false;
1349  }
1350 
1351  QgsReadWriteContext context;
1352  context.setPathResolver( pathResolver() );
1353 
1354  QDomImplementation DomImplementation;
1355  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1356 
1357  QDomDocumentType documentType =
1358  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1359  QStringLiteral( "SYSTEM" ) );
1360  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1361 
1362  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1363  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1364  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1365 
1366  doc->appendChild( qgisNode );
1367 
1368  // title
1369  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1370  qgisNode.appendChild( titleNode );
1371 
1372  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1373  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? "1" : "0" );
1374  qgisNode.appendChild( transactionNode );
1375 
1376  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1377  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? "1" : "0" );
1378  qgisNode.appendChild( evaluateDefaultValuesNode );
1379 
1380  QDomElement trustNode = doc->createElement( QStringLiteral( "trust" ) );
1381  trustNode.setAttribute( QStringLiteral( "active" ), mTrustLayerMetadata ? "1" : "0" );
1382  qgisNode.appendChild( trustNode );
1383 
1384  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1385  titleNode.appendChild( titleText );
1386 
1387  // write project CRS
1388  QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
1389  mCrs.writeXml( srsNode, *doc );
1390  qgisNode.appendChild( srsNode );
1391 
1392  // write layer tree - make sure it is without embedded subgroups
1393  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1395  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
1396 
1397  clonedRoot->writeXml( qgisNode, context );
1398  delete clonedRoot;
1399 
1400  mSnappingConfig.writeProject( *doc );
1401 
1402  // let map canvas and legend write their information
1403  emit writeProject( *doc );
1404 
1405  // within top level node save list of layers
1406  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
1407 
1408  // Iterate over layers in zOrder
1409  // Call writeXml() on each
1410  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1411 
1412  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
1413  while ( li != layers.end() )
1414  {
1415  QgsMapLayer *ml = li.value();
1416 
1417  if ( ml )
1418  {
1419  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1420  if ( emIt == mEmbeddedLayers.constEnd() )
1421  {
1422  // general layer metadata
1423  QDomElement maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1424 
1425  ml->writeLayerXml( maplayerElem, *doc, context );
1426 
1427  emit writeMapLayer( ml, maplayerElem, *doc );
1428 
1429  projectLayersNode.appendChild( maplayerElem );
1430  }
1431  else
1432  {
1433  // layer defined in an external project file
1434  // only save embedded layer if not managed by a legend group
1435  if ( emIt.value().second )
1436  {
1437  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1438  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
1439  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
1440  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
1441  projectLayersNode.appendChild( mapLayerElem );
1442  }
1443  }
1444  }
1445  li++;
1446  }
1447 
1448  qgisNode.appendChild( projectLayersNode );
1449 
1450  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
1451  Q_FOREACH ( QgsMapLayer *layer, mRootGroup->customLayerOrder() )
1452  {
1453  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
1454  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
1455  layerOrderNode.appendChild( mapLayerElem );
1456  }
1457  qgisNode.appendChild( layerOrderNode );
1458 
1459 
1460  // now add the optional extra properties
1461 
1462  dump_( mProperties );
1463 
1464  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
1465 
1466  if ( !mProperties.isEmpty() ) // only worry about properties if we
1467  // actually have any properties
1468  {
1469  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
1470  }
1471 
1472  mMapThemeCollection->writeXml( *doc );
1473 
1474  mLabelingEngineSettings->writeSettingsToProject( this );
1475 
1476  mTransformContext.writeXml( qgisNode, context );
1477 
1478  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
1479  qgisNode.appendChild( annotationsElem );
1480 
1481  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
1482  qgisNode.appendChild( layoutElem );
1483 
1484  // now wrap it up and ship it to the project file
1485  doc->normalize(); // XXX I'm not entirely sure what this does
1486 
1487  // Create backup file
1488  if ( QFile::exists( fileName() ) )
1489  {
1490  QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
1491  bool ok = true;
1492  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1493  ok &= projectFile.open( QIODevice::ReadOnly );
1494 
1495  QByteArray ba;
1496  while ( ok && !projectFile.atEnd() )
1497  {
1498  ba = projectFile.read( 10240 );
1499  ok &= backupFile.write( ba ) == ba.size();
1500  }
1501 
1502  projectFile.close();
1503  backupFile.close();
1504 
1505  if ( !ok )
1506  {
1507  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1508  return false;
1509  }
1510 
1511  QFileInfo fi( fileName() );
1512  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1513  utime( backupFile.fileName().toUtf8().constData(), &tb );
1514  }
1515 
1516  if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1517  {
1518  projectFile.close(); // even though we got an error, let's make
1519  // sure it's closed anyway
1520 
1521  setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
1522  return false;
1523  }
1524 
1525  QTemporaryFile tempFile;
1526  bool ok = tempFile.open();
1527  if ( ok )
1528  {
1529  QTextStream projectFileStream( &tempFile );
1530  doc->save( projectFileStream, 2 ); // save as utf-8
1531  ok &= projectFileStream.pos() > -1;
1532 
1533  ok &= tempFile.seek( 0 );
1534 
1535  QByteArray ba;
1536  while ( ok && !tempFile.atEnd() )
1537  {
1538  ba = tempFile.read( 10240 );
1539  ok &= projectFile.write( ba ) == ba.size();
1540  }
1541 
1542  ok &= projectFile.error() == QFile::NoError;
1543 
1544  projectFile.close();
1545  }
1546 
1547  tempFile.close();
1548 
1549  if ( !ok )
1550  {
1551  setError( tr( "Unable to save to file %1. Your project "
1552  "may be corrupted on disk. Try clearing some space on the volume and "
1553  "check file permissions before pressing save again." )
1554  .arg( projectFile.fileName() ) );
1555  return false;
1556  }
1557 
1558  setDirty( false ); // reset to pristine state
1559 
1560  emit projectSaved();
1561 
1562  return true;
1563 }
1564 
1565 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
1566 {
1567  setDirty( true );
1568 
1569  return addKey_( scope, key, &mProperties, value );
1570 }
1571 
1572 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
1573 {
1574  setDirty( true );
1575 
1576  return addKey_( scope, key, &mProperties, value );
1577 }
1578 
1579 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
1580 {
1581  setDirty( true );
1582 
1583  return addKey_( scope, key, &mProperties, value );
1584 }
1585 
1586 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
1587 {
1588  setDirty( true );
1589 
1590  return addKey_( scope, key, &mProperties, value );
1591 }
1592 
1593 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
1594 {
1595  setDirty( true );
1596 
1597  return addKey_( scope, key, &mProperties, value );
1598 }
1599 
1600 QStringList QgsProject::readListEntry( const QString &scope,
1601  const QString &key,
1602  const QStringList &def,
1603  bool *ok ) const
1604 {
1605  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1606 
1607  QVariant value;
1608 
1609  if ( property )
1610  {
1611  value = property->value();
1612 
1613  bool valid = QVariant::StringList == value.type();
1614  if ( ok )
1615  *ok = valid;
1616 
1617  if ( valid )
1618  {
1619  return value.toStringList();
1620  }
1621  }
1622 
1623  return def;
1624 }
1625 
1626 
1627 QString QgsProject::readEntry( const QString &scope,
1628  const QString &key,
1629  const QString &def,
1630  bool *ok ) const
1631 {
1632  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1633 
1634  QVariant value;
1635 
1636  if ( property )
1637  {
1638  value = property->value();
1639 
1640  bool valid = value.canConvert( QVariant::String );
1641  if ( ok )
1642  *ok = valid;
1643 
1644  if ( valid )
1645  return value.toString();
1646  }
1647 
1648  return def;
1649 }
1650 
1651 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
1652  bool *ok ) const
1653 {
1654  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1655 
1656  QVariant value;
1657 
1658  if ( property )
1659  {
1660  value = property->value();
1661  }
1662 
1663  bool valid = value.canConvert( QVariant::Int );
1664 
1665  if ( ok )
1666  {
1667  *ok = valid;
1668  }
1669 
1670  if ( valid )
1671  {
1672  return value.toInt();
1673  }
1674 
1675  return def;
1676 }
1677 
1678 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
1679  double def,
1680  bool *ok ) const
1681 {
1682  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1683  if ( property )
1684  {
1685  QVariant value = property->value();
1686 
1687  bool valid = value.canConvert( QVariant::Double );
1688  if ( ok )
1689  *ok = valid;
1690 
1691  if ( valid )
1692  return value.toDouble();
1693  }
1694 
1695  return def;
1696 }
1697 
1698 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
1699  bool *ok ) const
1700 {
1701  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1702 
1703  if ( property )
1704  {
1705  QVariant value = property->value();
1706 
1707  bool valid = value.canConvert( QVariant::Bool );
1708  if ( ok )
1709  *ok = valid;
1710 
1711  if ( valid )
1712  return value.toBool();
1713  }
1714 
1715  return def;
1716 }
1717 
1718 
1719 bool QgsProject::removeEntry( const QString &scope, const QString &key )
1720 {
1721  removeKey_( scope, key, mProperties );
1722 
1723  setDirty( true );
1724 
1725  return !findKey_( scope, key, mProperties );
1726 }
1727 
1728 
1729 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
1730 {
1731  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1732 
1733  QStringList entries;
1734 
1735  if ( foundProperty )
1736  {
1737  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
1738 
1739  if ( propertyKey )
1740  { propertyKey->entryList( entries ); }
1741  }
1742 
1743  return entries;
1744 }
1745 
1746 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
1747 {
1748  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1749 
1750  QStringList entries;
1751 
1752  if ( foundProperty )
1753  {
1754  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
1755 
1756  if ( propertyKey )
1757  { propertyKey->subkeyList( entries ); }
1758  }
1759 
1760  return entries;
1761 }
1762 
1764 {
1765  dump_( mProperties );
1766 }
1767 
1769 {
1770  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
1771  return QgsPathResolver( absolutePaths ? QString() : fileName() );
1772 }
1773 
1774 QString QgsProject::readPath( const QString &src ) const
1775 {
1776  return pathResolver().readPath( src );
1777 }
1778 
1779 QString QgsProject::writePath( const QString &src ) const
1780 {
1781  return pathResolver().writePath( src );
1782 }
1783 
1784 void QgsProject::setError( const QString &errorMessage )
1785 {
1786  mErrorMessage = errorMessage;
1787 }
1788 
1789 QString QgsProject::error() const
1790 {
1791  return mErrorMessage;
1792 }
1793 
1794 void QgsProject::clearError()
1795 {
1796  setError( QString() );
1797 }
1798 
1800 {
1801  delete mBadLayerHandler;
1802  mBadLayerHandler = handler;
1803 }
1804 
1805 QString QgsProject::layerIsEmbedded( const QString &id ) const
1806 {
1807  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1808  if ( it == mEmbeddedLayers.constEnd() )
1809  {
1810  return QString();
1811  }
1812  return it.value().first;
1813 }
1814 
1815 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1816  bool saveFlag )
1817 {
1818  QgsDebugCall;
1819 
1820  static QString sPrevProjectFilePath;
1821  static QDateTime sPrevProjectFileTimestamp;
1822  static QDomDocument sProjectDocument;
1823 
1824  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1825 
1826  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
1827  {
1828  sPrevProjectFilePath.clear();
1829 
1830  QFile projectFile( projectFilePath );
1831  if ( !projectFile.open( QIODevice::ReadOnly ) )
1832  {
1833  return false;
1834  }
1835 
1836  if ( !sProjectDocument.setContent( &projectFile ) )
1837  {
1838  return false;
1839  }
1840 
1841  sPrevProjectFilePath = projectFilePath;
1842  sPrevProjectFileTimestamp = projectFileTimestamp;
1843  }
1844 
1845  // does project store paths absolute or relative?
1846  bool useAbsolutePaths = true;
1847 
1848  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1849  if ( !propertiesElem.isNull() )
1850  {
1851  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
1852  if ( !absElem.isNull() )
1853  {
1854  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
1855  }
1856  }
1857 
1858  QgsReadWriteContext embeddedContext;
1859  if ( !useAbsolutePaths )
1860  embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
1861 
1862  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
1863  if ( projectLayersElem.isNull() )
1864  {
1865  return false;
1866  }
1867 
1868  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
1869  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1870  {
1871  // get layer id
1872  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1873  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
1874  if ( id == layerId )
1875  {
1876  // layer can be embedded only once
1877  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1878  {
1879  return false;
1880  }
1881 
1882  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1883 
1884  if ( addLayer( mapLayerElem, brokenNodes, embeddedContext ) )
1885  {
1886  return true;
1887  }
1888  else
1889  {
1890  mEmbeddedLayers.remove( layerId );
1891  return false;
1892  }
1893  }
1894  }
1895 
1896  return false;
1897 }
1898 
1899 
1900 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1901 {
1902  // open project file, get layer ids in group, add the layers
1903  QFile projectFile( projectFilePath );
1904  if ( !projectFile.open( QIODevice::ReadOnly ) )
1905  {
1906  return nullptr;
1907  }
1908 
1909  QDomDocument projectDocument;
1910  if ( !projectDocument.setContent( &projectFile ) )
1911  {
1912  return nullptr;
1913  }
1914 
1915  QgsReadWriteContext context;
1916  context.setPathResolver( pathResolver() );
1917 
1918  // store identify disabled layers of the embedded project
1919  QSet<QString> embeddedIdentifyDisabledLayers;
1920  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) ).firstChildElement( QStringLiteral( "Identify" ) ).firstChildElement( QStringLiteral( "disabledLayers" ) );
1921  if ( !disabledLayersElem.isNull() )
1922  {
1923  QDomNodeList valueList = disabledLayersElem.elementsByTagName( QStringLiteral( "value" ) );
1924  for ( int i = 0; i < valueList.size(); ++i )
1925  {
1926  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1927  }
1928  }
1929 
1931 
1932  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1933  if ( !layerTreeElem.isNull() )
1934  {
1935  root->readChildrenFromXml( layerTreeElem, context );
1936  }
1937  else
1938  {
1939  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1940  }
1941 
1942  QgsLayerTreeGroup *group = root->findGroup( groupName );
1943  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
1944  {
1945  // embedded groups cannot be embedded again
1946  delete root;
1947  return nullptr;
1948  }
1949 
1950  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1951  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1952  delete root;
1953  root = nullptr;
1954 
1955  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
1956  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
1957 
1958  // set "embedded" to all children + load embedded layers
1959  mLayerTreeRegistryBridge->setEnabled( false );
1960  initializeEmbeddedSubtree( projectFilePath, newGroup );
1961  mLayerTreeRegistryBridge->setEnabled( true );
1962 
1963  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
1964 
1965  // consider the layers might be identify disabled in its project
1966  Q_FOREACH ( const QString &layerId, newGroup->findLayerIds() )
1967  {
1968  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1969  {
1970  thisProjectIdentifyDisabledLayers.append( layerId );
1971  }
1972 
1973  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1974  if ( layer )
1975  {
1976  layer->setItemVisibilityChecked( invisibleLayers.contains( layerId ) );
1977  }
1978  }
1979 
1980  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
1981 
1982  return newGroup;
1983 }
1984 
1985 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group )
1986 {
1987  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1988  {
1989  // all nodes in the subtree will have "embedded" custom property set
1990  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
1991 
1992  if ( QgsLayerTree::isGroup( child ) )
1993  {
1994  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1995  }
1996  else if ( QgsLayerTree::isLayer( child ) )
1997  {
1998  // load the layer into our project
1999  QList<QDomNode> brokenNodes;
2000  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false );
2001  }
2002  }
2003 }
2004 
2006 {
2007  return mEvaluateDefaultValues;
2008 }
2009 
2011 {
2012  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2013  QMap<QString, QgsMapLayer *>::const_iterator layerIt = layers.constBegin();
2014  for ( ; layerIt != layers.constEnd(); ++layerIt )
2015  {
2016  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() );
2017  if ( vl )
2018  {
2020  }
2021  }
2022 
2023  mEvaluateDefaultValues = evaluateDefaultValues;
2024 }
2025 
2027 {
2028  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
2030 }
2031 
2033 {
2034  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
2035 }
2036 
2038 {
2039  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
2040  if ( !distanceUnitString.isEmpty() )
2041  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2042 
2043  //fallback to QGIS default measurement unit
2044  QgsSettings s;
2045  bool ok = false;
2046  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
2047  return ok ? type : QgsUnitTypes::DistanceMeters;
2048 }
2049 
2051 {
2052  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2053 }
2054 
2056 {
2057  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
2058  if ( !areaUnitString.isEmpty() )
2059  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2060 
2061  //fallback to QGIS default area unit
2062  QgsSettings s;
2063  bool ok = false;
2064  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
2065  return ok ? type : QgsUnitTypes::AreaSquareMeters;
2066 }
2067 
2069 {
2070  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
2071 }
2072 
2073 QString QgsProject::homePath() const
2074 {
2075  QFileInfo pfi( fileName() );
2076  if ( !pfi.exists() )
2077  return QString();
2078 
2079  return pfi.canonicalPath();
2080 }
2081 
2083 {
2084  return mRelationManager;
2085 }
2086 
2088 {
2089  return mLayoutManager.get();
2090 }
2091 
2093 {
2094  return mLayoutManager.get();
2095 }
2096 
2098 {
2099  return mRootGroup;
2100 }
2101 
2103 {
2104  return mMapThemeCollection.get();
2105 }
2106 
2108 {
2109  return mAnnotationManager.get();
2110 }
2111 
2113 {
2114  return mAnnotationManager.get();
2115 }
2116 
2117 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2118 {
2119  QStringList currentLayers = nonIdentifiableLayers();
2120 
2121  QStringList newLayers;
2122  Q_FOREACH ( QgsMapLayer *l, layers )
2123  {
2124  newLayers << l->id();
2125  }
2126 
2127  if ( newLayers == currentLayers )
2128  return;
2129 
2130  QStringList disabledLayerIds;
2131 
2132  Q_FOREACH ( QgsMapLayer *l, layers )
2133  {
2134  disabledLayerIds << l->id();
2135  }
2136 
2137  setNonIdentifiableLayers( disabledLayerIds );
2138 }
2139 
2140 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2141 {
2142  writeEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ), layerIds );
2143 
2144  emit nonIdentifiableLayersChanged( layerIds );
2145 }
2146 
2147 QStringList QgsProject::nonIdentifiableLayers() const
2148 {
2149  return readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
2150 }
2151 
2153 {
2154  return mAutoTransaction;
2155 }
2156 
2158 {
2159  if ( autoTransaction != mAutoTransaction )
2160  {
2161  mAutoTransaction = autoTransaction;
2162 
2163  if ( autoTransaction )
2164  onMapLayersAdded( mapLayers().values() );
2165  else
2166  cleanTransactionGroups( true );
2167  }
2168 }
2169 
2170 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2171 {
2172  return mTransactionGroups;
2173 }
2174 
2175 
2176 //
2177 // QgsMapLayerStore methods
2178 //
2179 
2180 
2182 {
2183  return mLayerStore->count();
2184 }
2185 
2186 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
2187 {
2188  return mLayerStore->mapLayer( layerId );
2189 }
2190 
2191 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
2192 {
2193  return mLayerStore->mapLayersByName( layerName );
2194 }
2195 
2196 bool QgsProject::unzip( const QString &filename )
2197 {
2198  clearError();
2199  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2200 
2201  // unzip the archive
2202  if ( !archive->unzip( filename ) )
2203  {
2204  setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
2205  return false;
2206  }
2207 
2208  // test if zip provides a .qgs file
2209  if ( archive->projectFile().isEmpty() )
2210  {
2211  setError( tr( "Zip archive does not provide a project file" ) );
2212  return false;
2213  }
2214 
2215  // load auxiliary storage
2216  if ( !archive->auxiliaryStorageFile().isEmpty() )
2217  {
2218  // database file is already a copy as it's been unzipped. So we don't open
2219  // auxiliary storage in copy mode in this case
2220  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( archive->auxiliaryStorageFile(), false ) );
2221  }
2222  else
2223  {
2224  mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
2225  }
2226 
2227  // read the project file
2228  if ( ! readProjectFile( archive->projectFile() ) )
2229  {
2230  setError( tr( "Cannot read unzipped qgs project file" ) );
2231  return false;
2232  }
2233 
2234  // keep the archive and remove the temporary .qgs file
2235  mArchive = std::move( archive );
2236  mArchive->clearProjectFile();
2237 
2238  return true;
2239 }
2240 
2241 bool QgsProject::zip( const QString &filename )
2242 {
2243  clearError();
2244 
2245  // save the current project in a temporary .qgs file
2246  std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
2247  const QString baseName = QFileInfo( filename ).baseName();
2248  const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
2249  QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
2250 
2251  bool writeOk = false;
2252  if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2253  {
2254  writeOk = writeProjectFile( qgsFile.fileName() );
2255  qgsFile.close();
2256  }
2257 
2258  // stop here with an error message
2259  if ( ! writeOk )
2260  {
2261  setError( tr( "Unable to write temporary qgs file" ) );
2262  return false;
2263  }
2264 
2265  // save auxiliary storage
2266  const QFileInfo info( qgsFile );
2267  const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + "." + QgsAuxiliaryStorage::extension();
2268 
2269  if ( ! saveAuxiliaryStorage( asFileName ) )
2270  {
2271  setError( tr( "Unable to save auxiliary storage" ) );
2272  return false;
2273  }
2274 
2275  // create the archive
2276  archive->addFile( qgsFile.fileName() );
2277  archive->addFile( asFileName );
2278 
2279  // zip
2280  if ( !archive->zip( filename ) )
2281  {
2282  setError( tr( "Unable to perform zip" ) );
2283  return false;
2284  }
2285 
2286  return true;
2287 }
2288 
2290 {
2291  return QgsZipUtils::isZipFile( mFile.fileName() );
2292 }
2293 
2294 QList<QgsMapLayer *> QgsProject::addMapLayers(
2295  const QList<QgsMapLayer *> &layers,
2296  bool addToLegend,
2297  bool takeOwnership )
2298 {
2299  const QList<QgsMapLayer *> myResultList = mLayerStore->addMapLayers( layers, takeOwnership );
2300  if ( !myResultList.isEmpty() )
2301  {
2302  if ( addToLegend )
2303  emit legendLayersAdded( myResultList );
2304  }
2305 
2306  if ( mAuxiliaryStorage )
2307  {
2308  for ( QgsMapLayer *mlayer : myResultList )
2309  {
2310  if ( mlayer->type() != QgsMapLayer::VectorLayer )
2311  continue;
2312 
2313  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
2314  if ( vl )
2315  {
2316  vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
2317  }
2318  }
2319  }
2320 
2321  return myResultList;
2322 }
2323 
2324 QgsMapLayer *
2326  bool addToLegend,
2327  bool takeOwnership )
2328 {
2329  QList<QgsMapLayer *> addedLayers;
2330  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
2331  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
2332 }
2333 
2334 void QgsProject::removeMapLayers( const QStringList &layerIds )
2335 {
2336  mLayerStore->removeMapLayers( layerIds );
2337 }
2338 
2339 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
2340 {
2341  mLayerStore->removeMapLayers( layers );
2342 }
2343 
2344 void QgsProject::removeMapLayer( const QString &layerId )
2345 {
2346  mLayerStore->removeMapLayer( layerId );
2347 }
2348 
2350 {
2351  mLayerStore->removeMapLayer( layer );
2352 }
2353 
2355 {
2356  return mLayerStore->takeMapLayer( layer );
2357 }
2358 
2360 {
2361  mLayerStore->removeAllMapLayers();
2362 }
2363 
2365 {
2366  QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2367  QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
2368  for ( ; it != layers.constEnd(); ++it )
2369  {
2370  it.value()->reload();
2371  }
2372 }
2373 
2374 QMap<QString, QgsMapLayer *> QgsProject::mapLayers() const
2375 {
2376  return mLayerStore->mapLayers();
2377 }
2378 
2379 
2381 {
2382  QgsSettings settings;
2383  QgsCoordinateReferenceSystem defaultCrs;
2384  if ( settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "useProject" )
2385  || settings.value( QStringLiteral( "/Projections/defaultBehavior" ), QStringLiteral( "prompt" ) ).toString() == QStringLiteral( "prompt" ) )
2386  {
2387  // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
2388  // (since "prompt" has no meaning here - the prompt will always be shown, it's just deciding on the default choice in the prompt!)
2389  defaultCrs = crs();
2390  }
2391  else
2392  {
2393  // global crs
2394  QString layerDefaultCrs = settings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), GEO_EPSG_CRS_AUTHID ).toString();
2395  defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
2396  }
2397 
2398  return defaultCrs;
2399 }
2400 
2402 {
2403  mTrustLayerMetadata = trust;
2404 
2405  auto layers = mapLayers();
2406  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
2407  {
2408  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
2409  if ( vl )
2410  {
2411  vl->setReadExtentFromXml( trust );
2412  }
2413  }
2414 }
2415 
2416 bool QgsProject::saveAuxiliaryStorage( const QString &filename )
2417 {
2418  const QMap<QString, QgsMapLayer *> layers = mapLayers();
2419  for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
2420  {
2421  if ( it.value()->type() != QgsMapLayer::VectorLayer )
2422  continue;
2423 
2424  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
2425  if ( vl && vl->auxiliaryLayer() )
2426  {
2427  vl->auxiliaryLayer()->save();
2428  }
2429  }
2430 
2431  if ( !filename.isEmpty() )
2432  {
2433  return mAuxiliaryStorage->saveAs( filename );
2434  }
2435  else
2436  {
2437  return mAuxiliaryStorage->saveAs( *this );
2438  }
2439 }
2440 
2442 {
2443  return mAuxiliaryStorage.get();
2444 }
2445 
2447 {
2448  return mAuxiliaryStorage.get();
2449 }
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 Q_INVOKABLE AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
Definition: qgslayertree.h:75
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer *> &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Base class for all map layer types.
Definition: qgsmaplayer.h: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:535
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:57
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:657
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:38
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
Definition: qgslayertree.h:64
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
Class used to work with layer dependencies stored in a XML project or layer definition file...
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
QString toProj4() const
Returns a Proj4 string representation of this CRS.
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp: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.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
void reloadAllLayers()
Reload all registered layer&#39;s provider data caches, synchronising the layer with any changes in the d...
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
void legendLayersAdded(const QList< QgsMapLayer *> &layers)
Emitted, when a layer was added to the registry and the legend.
bool isValid() const
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
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning)
add a message to the instance (and create it if necessary)
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.
QgsCoordinateTransformContext transformContext() const
Returns a copy of the project&#39;s coordinate transform context, which stores various information regard...
Definition: qgsproject.cpp:474
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
void clear()
Remove any relation managed by this class.
QgsProject(QObject *parent=nullptr)
Create a new QgsProject.
Definition: qgsproject.cpp:328
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:634
const QString GEO_NONE
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.cpp:71
void fileNameChanged()
Emitted when the file name of the project changes.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:114
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context&#39;s state from a DOM element.
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.
void readSettings()
Reads the context&#39;s state from application settings.
void missingDatumTransforms(const QStringList &missingTransforms)
Emitted when datum transforms stored in the project are not available locally.
bool read()
Reads the project from its currently associated file (see fileName() ).
Definition: qgsproject.cpp:790
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:39
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:82
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
~QgsProject() override
Definition: qgsproject.cpp:369
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.
Contains information about the context in which a coordinate transform is executed.
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Definition: qgsproject.cpp:267
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
Manages storage of a set of layouts.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the store.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
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:37
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
const QgsLayoutManager * layoutManager() const
Returns the project&#39;s layout manager, which manages compositions within the project.
QStringList makeKeyTokens_(const QString &scope, const QString &key)
Take the given scope and key and convert them to a string list of key tokens that will be used to nav...
Definition: qgsproject.cpp: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.
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 writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context&#39;s state to a DOM element.
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed...
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the registry.
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:649
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
void oldProjectVersionWarning(const QString &)
emitted when an old project file is read.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
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 setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project&#39;s coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:479
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...
void transformContextChanged()
Emitted when the project transformContext() is changed.
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.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
void setName(const QString &name)
The name of the property is used as identifier.
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
AreaUnit
Units of area.
Definition: qgsunittypes.h:69
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QStringList entryList(const QString &scope, const QString &key) const
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:572
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:485
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.
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.