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