QGIS API Documentation  2.99.0-Master (5169e0d)
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 "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  , mLabelingEngineSettings( new QgsLabelingEngineSettings )
332  , mAutoTransaction( false )
333  , mEvaluateDefaultValues( false )
334  , mDirty( false )
335 {
336  mProperties.setName( QStringLiteral( "properties" ) );
337  clear();
338 
339  // bind the layer tree to the map layer registry.
340  // whenever layers are added to or removed from the registry,
341  // layer tree will be updated
342  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
343  connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
344  connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
345  connect( this, static_cast < void ( QgsProject::* )( const QList<QgsMapLayer *> & ) >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
346 }
347 
348 
350 {
351  delete mBadLayerHandler;
352  delete mRelationManager;
353  delete mLayerTreeRegistryBridge;
354  delete mRootGroup;
355 
357 }
358 
359 
361 {
362  if ( !sProject )
363  {
364  sProject = new QgsProject;
365  }
366  return sProject;
367 }
368 
369 void QgsProject::setTitle( const QString &title )
370 {
371  if ( title == mTitle )
372  return;
373 
374  mTitle = title;
375 
376  setDirty( true );
377 }
378 
379 
380 QString QgsProject::title() const
381 {
382  return mTitle;
383 }
384 
385 
387 {
388  return mDirty;
389 }
390 
391 void QgsProject::setDirty( bool b )
392 {
393  mDirty = b;
394 }
395 
396 void QgsProject::setFileName( const QString &name )
397 {
398  if ( name == mFile.fileName() )
399  return;
400 
401  QString oldHomePath = homePath();
402 
403  mFile.setFileName( name );
404  emit fileNameChanged();
405 
406  QString newHomePath = homePath();
407  if ( newHomePath != oldHomePath )
408  emit homePathChanged();
409 
410  setDirty( true );
411 }
412 
413 QString QgsProject::fileName() const
414 {
415  return mFile.fileName();
416 }
417 
418 QFileInfo QgsProject::fileInfo() const
419 {
420  return QFileInfo( mFile );
421 }
422 
424 {
425  return mCrs;
426 }
427 
429 {
430  mCrs = crs;
431  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ), crs.toProj4() );
432  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), static_cast< int >( crs.srsid() ) );
433  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ), crs.authid() );
434  writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
435  setDirty( true );
436  emit crsChanged();
437 }
438 
439 QString QgsProject::ellipsoid() const
440 {
441  if ( !crs().isValid() )
442  return GEO_NONE;
443 
444  return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), GEO_NONE );
445 }
446 
447 void QgsProject::setEllipsoid( const QString &ellipsoid )
448 {
449  writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
450  setDirty( true );
451  emit ellipsoidChanged( ellipsoid );
452 }
453 
455 {
456  mFile.setFileName( QString() );
457  mProperties.clearKeys();
458  mTitle.clear();
459  mAutoTransaction = false;
460  mEvaluateDefaultValues = false;
461  mDirty = false;
462  mCustomVariables.clear();
463 
464  mEmbeddedLayers.clear();
465  mRelationManager->clear();
466  mAnnotationManager->clear();
467  mLayoutManager->clear();
468  mSnappingConfig.reset();
469  emit snappingConfigChanged( mSnappingConfig );
470 
471  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
473 
474  mRootGroup->clear();
475 
476  mLabelingEngineSettings->clear();
478 
479  // reset some default project properties
480  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
481  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
482  writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
483  writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
484 
485  //copy default units to project
486  QgsSettings s;
487  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString() );
488  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString() );
489 
490  setDirty( false );
491 }
492 
493 // basically a debugging tool to dump property list values
494 void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
495 {
496  QgsDebugMsg( "current properties:" );
497  topQgsPropertyKey.dump();
498 }
499 
500 
531 void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
532 {
533  QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
534 
535  if ( propertiesElem.isNull() ) // no properties found, so we're done
536  {
537  return;
538  }
539 
540  QDomNodeList scopes = propertiesElem.childNodes();
541 
542  if ( scopes.count() < 1 )
543  {
544  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
545  return;
546  }
547 
548  if ( ! project_properties.readXml( propertiesElem ) )
549  {
550  QgsDebugMsg( "Project_properties.readXml() failed" );
551  }
552 }
553 
554 
559 static void _getTitle( const QDomDocument &doc, QString &title )
560 {
561  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "title" ) );
562 
563  title = QLatin1String( "" ); // by default the title will be empty
564 
565  if ( !nl.count() )
566  {
567  QgsDebugMsg( "unable to find title element" );
568  return;
569  }
570 
571  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element ok
572 
573  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
574  {
575  QgsDebugMsg( "unable to find title element" );
576  return;
577  }
578 
579  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
580 
581  if ( !titleTextNode.isText() )
582  {
583  QgsDebugMsg( "unable to find title element" );
584  return;
585  }
586 
587  QDomText titleText = titleTextNode.toText();
588 
589  title = titleText.data();
590 
591 }
592 
593 QgsProjectVersion getVersion( const QDomDocument &doc )
594 {
595  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
596 
597  if ( !nl.count() )
598  {
599  QgsDebugMsg( " unable to find qgis element in project file" );
600  return QgsProjectVersion( 0, 0, 0, QString() );
601  }
602 
603  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
604 
605  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
606  QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
607  return projectVersion;
608 }
609 
610 
612 {
613  return mSnappingConfig;
614 }
615 
617 {
618  if ( mSnappingConfig == snappingConfig )
619  return;
620 
621  mSnappingConfig = snappingConfig;
622  setDirty();
623  emit snappingConfigChanged( mSnappingConfig );
624 }
625 
626 bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes )
627 {
628  // Layer order is set by the restoring the legend settings from project file.
629  // This is done on the 'readProject( ... )' signal
630 
631  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "maplayer" ) );
632 
633  // process the map layer nodes
634 
635  if ( 0 == nl.count() ) // if we have no layers to process, bail
636  {
637  return true; // Decided to return "true" since it's
638  // possible for there to be a project with no
639  // layers; but also, more imporantly, this
640  // would cause the tests/qgsproject to fail
641  // since the test suite doesn't currently
642  // support test layers
643  }
644 
645  bool returnStatus = true;
646 
647  emit layerLoaded( 0, nl.count() );
648 
649  // order layers based on their dependencies
650  QgsLayerDefinition::DependencySorter depSorter( doc );
651  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
652  return false;
653 
654  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
655 
656  int i = 0;
657  Q_FOREACH ( const QDomNode &node, sortedLayerNodes )
658  {
659  QDomElement element = node.toElement();
660 
661  QString name = node.namedItem( QStringLiteral( "layername" ) ).toElement().text();
662  if ( !name.isNull() )
663  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
664 
665  if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
666  {
667  createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes );
668  continue;
669  }
670  else
671  {
672  if ( !addLayer( element, brokenNodes ) )
673  {
674  returnStatus = false;
675  }
676  }
677  emit layerLoaded( i + 1, nl.count() );
678  i++;
679  }
680 
681  return returnStatus;
682 }
683 
684 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes )
685 {
686  QString type = layerElem.attribute( QStringLiteral( "type" ) );
687  QgsDebugMsgLevel( "Layer type is " + type, 4 );
688  QgsMapLayer *mapLayer = nullptr;
689 
690  if ( type == QLatin1String( "vector" ) )
691  {
692  mapLayer = new QgsVectorLayer;
693  }
694  else if ( type == QLatin1String( "raster" ) )
695  {
696  mapLayer = new QgsRasterLayer;
697  }
698  else if ( type == QLatin1String( "plugin" ) )
699  {
700  QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
701  mapLayer = QgsApplication::pluginLayerRegistry()->createLayer( typeName );
702  }
703 
704  if ( !mapLayer )
705  {
706  QgsDebugMsg( "Unable to create layer" );
707 
708  return false;
709  }
710 
711  Q_CHECK_PTR( mapLayer ); // NOLINT
712 
713  // have the layer restore state that is stored in Dom node
714  if ( mapLayer->readLayerXml( layerElem, pathResolver() ) && mapLayer->isValid() )
715  {
716  emit readMapLayer( mapLayer, layerElem );
717 
718  QList<QgsMapLayer *> myLayers;
719  myLayers << mapLayer;
720  addMapLayers( myLayers );
721 
722  return true;
723  }
724  else
725  {
726  delete mapLayer;
727 
728  QgsDebugMsg( "Unable to load " + type + " layer" );
729  brokenNodes.push_back( layerElem );
730  return false;
731  }
732 }
733 
734 bool QgsProject::read( const QString &filename )
735 {
736  mFile.setFileName( filename );
737 
738  return read();
739 }
740 
742 {
743  clearError();
744 
745  std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
746 
747  if ( !mFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
748  {
749  mFile.close();
750 
751  setError( tr( "Unable to open %1" ).arg( mFile.fileName() ) );
752 
753  return false;
754  }
755 
756  // location of problem associated with errorMsg
757  int line, column;
758  QString errorMsg;
759 
760  if ( !doc->setContent( &mFile, &errorMsg, &line, &column ) )
761  {
762  // want to make this class as GUI independent as possible; so commented out
763 #if 0
764  QMessageBox::critical( 0, tr( "Project File Read Error" ),
765  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
766 #endif
767 
768  QString errorString = tr( "Project file read error: %1 at line %2 column %3" )
769  .arg( errorMsg ).arg( line ).arg( column );
770 
771  QgsDebugMsg( errorString );
772 
773  mFile.close();
774 
775  setError( tr( "%1 for file %2" ).arg( errorString, mFile.fileName() ) );
776 
777  return false;
778  }
779 
780  mFile.close();
781 
782 
783  QgsDebugMsg( "Opened document " + mFile.fileName() );
784  QgsDebugMsg( "Project title: " + mTitle );
785 
786  // get project version string, if any
787  QgsProjectVersion fileVersion = getVersion( *doc );
788  QgsProjectVersion thisVersion( Qgis::QGIS_VERSION );
789 
790  if ( thisVersion > fileVersion )
791  {
792  QgsLogger::warning( "Loading a file that was saved with an older "
793  "version of qgis (saved in " + fileVersion.text() +
794  ", loaded in " + Qgis::QGIS_VERSION +
795  "). Problems may occur." );
796 
797  QgsProjectFileTransform projectFile( *doc, fileVersion );
798 
799  // Shows a warning when an old project file is read.
800  emit oldProjectVersionWarning( fileVersion.text() );
801  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
802 
803  projectFile.updateRevision( thisVersion );
804  }
805 
806  // start new project, just keep the file name
807  QString fileName = mFile.fileName();
808  clear();
809  mFile.setFileName( fileName );
810 
811  // now get any properties
812  _getProperties( *doc, mProperties );
813 
814  QgsDebugMsg( QString::number( mProperties.count() ) + " properties read" );
815 
816  dump_( mProperties );
817 
818  // now get project title
819  _getTitle( *doc, mTitle );
820 
821  //crs
822  QgsCoordinateReferenceSystem projectCrs;
823  if ( QgsProject::instance()->readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
824  {
825  long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
826  if ( currentCRS != -1 )
827  {
828  projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
829  }
830  }
831  mCrs = projectCrs;
832  emit crsChanged();
833 
834  QDomNodeList nl = doc->elementsByTagName( QStringLiteral( "autotransaction" ) );
835  if ( nl.count() )
836  {
837  QDomElement transactionElement = nl.at( 0 ).toElement();
838  if ( transactionElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
839  mAutoTransaction = true;
840  }
841 
842  nl = doc->elementsByTagName( QStringLiteral( "evaluateDefaultValues" ) );
843  if ( nl.count() )
844  {
845  QDomElement evaluateDefaultValuesElement = nl.at( 0 ).toElement();
846  if ( evaluateDefaultValuesElement.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
847  mEvaluateDefaultValues = true;
848  }
849 
850  // read the layer tree from project file
851 
852  mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
853 
854  QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
855  if ( !layerTreeElem.isNull() )
856  {
857  mRootGroup->readChildrenFromXml( layerTreeElem );
858  }
859  else
860  {
861  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
862  }
863 
864  mLayerTreeRegistryBridge->setEnabled( false );
865 
866  // get the map layers
867  QList<QDomNode> brokenNodes;
868  bool clean = _getMapLayers( *doc, brokenNodes );
869 
870  // review the integrity of the retrieved map layers
871  if ( !clean )
872  {
873  QgsDebugMsg( "Unable to get map layers from project file." );
874 
875  if ( !brokenNodes.isEmpty() )
876  {
877  QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
878  }
879 
880  // we let a custom handler decide what to do with missing layers
881  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
882  mBadLayerHandler->handleBadLayers( brokenNodes );
883  }
884 
885  // Resolve references to other vector layers
886  // Needs to be done here once all dependent layers are loaded
887  for ( QMap<QString, QgsMapLayer *>::iterator it = mMapLayers.begin(); it != mMapLayers.end(); it++ )
888  {
889  if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() ) )
890  vl->resolveReferences( this );
891  }
892 
893  mLayerTreeRegistryBridge->setEnabled( true );
894 
895  // load embedded groups and layers
896  loadEmbeddedNodes( mRootGroup );
897 
898  // now that layers are loaded, we can resolve layer tree's references to the layers
899  mRootGroup->resolveReferences( this );
900 
901 
902  if ( !layerTreeElem.isNull() )
903  {
904  mRootGroup->readLayerOrderFromXml( layerTreeElem );
905  }
906  else
907  {
908  // Load pre 3.0 configuration
909  QDomElement elem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
910  mRootGroup->readLayerOrderFromXml( elem );
911  }
912 
913  // make sure the are just valid layers
915 
916  mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
917 
918  mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
920  mMapThemeCollection->readXml( *doc );
921 
922  mLabelingEngineSettings->readSettingsFromProject( this );
924 
925  mAnnotationManager->readXml( doc->documentElement(), *doc );
926  mLayoutManager->readXml( doc->documentElement(), *doc );
927 
928  // reassign change dependencies now that all layers are loaded
929  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
930  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
931  {
932  it.value()->setDependencies( it.value()->dependencies() );
933  }
934 
935  mSnappingConfig.readProject( *doc );
936  emit snappingConfigChanged( mSnappingConfig );
937 
938  //add variables defined in project file
939  QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
940  QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
941 
942  mCustomVariables.clear();
943  if ( variableNames.length() == variableValues.length() )
944  {
945  for ( int i = 0; i < variableNames.length(); ++i )
946  {
947  mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
948  }
949  }
950  else
951  {
952  QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
953  }
954  emit customVariablesChanged();
955 
956  // read the project: used by map canvas and legend
957  emit readProject( *doc );
958 
959  // if all went well, we're allegedly in pristine state
960  if ( clean )
961  setDirty( false );
962 
964  emit crsChanged();
965  emit ellipsoidChanged( ellipsoid() );
966 
967  return true;
968 }
969 
970 
971 void QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group )
972 {
973  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
974  {
975  if ( QgsLayerTree::isGroup( child ) )
976  {
977  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
978  if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
979  {
980  // make sure to convert the path from relative to absolute
981  QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
982  childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
983 
984  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList() );
985  if ( newGroup )
986  {
987  QList<QgsLayerTreeNode *> clonedChildren;
988  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
989  clonedChildren << newGroupChild->clone();
990  delete newGroup;
991 
992  childGroup->insertChildNodes( 0, clonedChildren );
993  }
994  }
995  else
996  {
997  loadEmbeddedNodes( childGroup );
998  }
999  }
1000  else if ( QgsLayerTree::isLayer( child ) )
1001  {
1002  if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
1003  {
1004  QList<QDomNode> brokenNodes;
1005  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( QStringLiteral( "embedded_project" ) ).toString(), brokenNodes );
1006  }
1007  }
1008 
1009  }
1010 }
1011 
1012 QVariantMap QgsProject::customVariables() const
1013 {
1014  return mCustomVariables;
1015 }
1016 
1017 void QgsProject::setCustomVariables( const QVariantMap &variables )
1018 {
1019  if ( variables == mCustomVariables )
1020  return;
1021 
1022  //write variable to project
1023  QStringList variableNames;
1024  QStringList variableValues;
1025 
1026  QVariantMap::const_iterator it = variables.constBegin();
1027  for ( ; it != variables.constEnd(); ++it )
1028  {
1029  variableNames << it.key();
1030  variableValues << it.value().toString();
1031  }
1032 
1033  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
1034  writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
1035 
1036  mCustomVariables = variables;
1037 
1038  emit customVariablesChanged();
1039 }
1040 
1042 {
1043  *mLabelingEngineSettings = settings;
1045 }
1046 
1048 {
1049  return *mLabelingEngineSettings;
1050 }
1051 
1052 QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
1053 {
1054  QList<QgsVectorLayer *> layers;
1055  QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
1056  Q_FOREACH ( const QString &layerId, layerIds )
1057  {
1058  if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
1059  layers << vlayer;
1060  }
1061  return layers;
1062 }
1063 
1064 void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
1065 {
1066  QStringList list;
1067  Q_FOREACH ( QgsVectorLayer *layer, layers )
1068  list << layer->id();
1069  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
1071 }
1072 
1074 {
1075  QgsExpressionContext context;
1076 
1079 
1080  return context;
1081 }
1082 
1083 void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
1084 {
1085  QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1086 
1087  bool tgChanged = false;
1088 
1089  Q_FOREACH ( QgsMapLayer *layer, layers )
1090  {
1091  QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
1092  if ( vlayer )
1093  {
1094  if ( autoTransaction() )
1095  {
1096  if ( QgsTransaction::supportsTransaction( vlayer ) )
1097  {
1098  QString connString = QgsDataSourceUri( vlayer->source() ).connectionInfo();
1099  QString key = vlayer->providerType();
1100 
1101  QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
1102 
1103  if ( !tg )
1104  {
1105  tg = new QgsTransactionGroup();
1106  mTransactionGroups.insert( qMakePair( key, connString ), tg );
1107  tgChanged = true;
1108  }
1109  tg->addLayer( vlayer );
1110  }
1111  }
1113  }
1114 
1115  if ( tgChanged )
1116  emit transactionGroupsChanged();
1117 
1118  connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
1119 
1120  // check if we have to update connections for layers with dependencies
1121  for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); it++ )
1122  {
1123  QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
1124  if ( deps.contains( layer->id() ) )
1125  {
1126  // reconnect to change signals
1127  it.value()->setDependencies( deps );
1128  }
1129  }
1130  }
1131 
1132  if ( mSnappingConfig.addLayers( layers ) )
1133  emit snappingConfigChanged( mSnappingConfig );
1134 }
1135 
1136 void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
1137 {
1138  if ( mSnappingConfig.removeLayers( layers ) )
1139  emit snappingConfigChanged( mSnappingConfig );
1140 }
1141 
1142 void QgsProject::cleanTransactionGroups( bool force )
1143 {
1144  bool changed = false;
1145  for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
1146  {
1147  if ( tg.value()->isEmpty() || force )
1148  {
1149  delete tg.value();
1150  tg = mTransactionGroups.erase( tg );
1151  changed = true;
1152  }
1153  else
1154  {
1155  ++tg;
1156  }
1157  }
1158  if ( changed )
1159  emit transactionGroupsChanged();
1160 }
1161 
1162 bool QgsProject::readLayer( const QDomNode &layerNode )
1163 {
1164  QList<QDomNode> brokenNodes;
1165  if ( addLayer( layerNode.toElement(), brokenNodes ) )
1166  {
1167  // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
1168  // added layer for joins
1169  QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
1170  Q_FOREACH ( QgsVectorLayer *layer, vectorLayers )
1171  {
1172  // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
1173  layer->resolveReferences( this );
1174  }
1175 
1176  return true;
1177  }
1178  return false;
1179 }
1180 
1181 bool QgsProject::write( const QString &filename )
1182 {
1183  mFile.setFileName( filename );
1184 
1185  return write();
1186 }
1187 
1189 {
1190  clearError();
1191 
1192  // if we have problems creating or otherwise writing to the project file,
1193  // let's find out up front before we go through all the hand-waving
1194  // necessary to create all the Dom objects
1195  QFileInfo myFileInfo( mFile );
1196  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1197  {
1198  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1199  .arg( mFile.fileName() ) );
1200  return false;
1201  }
1202 
1203  QDomImplementation DomImplementation;
1204  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1205 
1206  QDomDocumentType documentType =
1207  DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
1208  QStringLiteral( "SYSTEM" ) );
1209  std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
1210 
1211  QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
1212  qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
1213  qgisNode.setAttribute( QStringLiteral( "version" ), QStringLiteral( "%1" ).arg( Qgis::QGIS_VERSION ) );
1214 
1215  doc->appendChild( qgisNode );
1216 
1217  // title
1218  QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
1219  qgisNode.appendChild( titleNode );
1220 
1221  QDomElement transactionNode = doc->createElement( QStringLiteral( "autotransaction" ) );
1222  transactionNode.setAttribute( QStringLiteral( "active" ), mAutoTransaction ? "1" : "0" );
1223  qgisNode.appendChild( transactionNode );
1224 
1225  QDomElement evaluateDefaultValuesNode = doc->createElement( QStringLiteral( "evaluateDefaultValues" ) );
1226  evaluateDefaultValuesNode.setAttribute( QStringLiteral( "active" ), mEvaluateDefaultValues ? "1" : "0" );
1227  qgisNode.appendChild( evaluateDefaultValuesNode );
1228 
1229  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1230  titleNode.appendChild( titleText );
1231 
1232  // write layer tree - make sure it is without embedded subgroups
1233  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1235  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
1236  clonedRoot->writeXml( qgisNode );
1237  delete clonedRoot;
1238 
1239  mSnappingConfig.writeProject( *doc );
1240 
1241  // let map canvas and legend write their information
1242  emit writeProject( *doc );
1243 
1244  // within top level node save list of layers
1245  const QMap<QString, QgsMapLayer *> &layers = mapLayers();
1246 
1247  // Iterate over layers in zOrder
1248  // Call writeXml() on each
1249  QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
1250 
1251  QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
1252  while ( li != layers.end() )
1253  {
1254  QgsMapLayer *ml = li.value();
1255 
1256  if ( ml )
1257  {
1258  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1259  if ( emIt == mEmbeddedLayers.constEnd() )
1260  {
1261  // general layer metadata
1262  QDomElement maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1263 
1264  ml->writeLayerXml( maplayerElem, *doc, pathResolver() );
1265 
1266  emit writeMapLayer( ml, maplayerElem, *doc );
1267 
1268  projectLayersNode.appendChild( maplayerElem );
1269  }
1270  else
1271  {
1272  // layer defined in an external project file
1273  // only save embedded layer if not managed by a legend group
1274  if ( emIt.value().second )
1275  {
1276  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
1277  mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
1278  mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
1279  mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
1280  projectLayersNode.appendChild( mapLayerElem );
1281  }
1282  }
1283  }
1284  li++;
1285  }
1286 
1287  qgisNode.appendChild( projectLayersNode );
1288 
1289  QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
1290  Q_FOREACH ( QgsMapLayer *layer, mRootGroup->customLayerOrder() )
1291  {
1292  QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
1293  mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
1294  layerOrderNode.appendChild( mapLayerElem );
1295  }
1296  qgisNode.appendChild( layerOrderNode );
1297 
1298 
1299  // now add the optional extra properties
1300 
1301  dump_( mProperties );
1302 
1303  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ) );
1304 
1305  if ( !mProperties.isEmpty() ) // only worry about properties if we
1306  // actually have any properties
1307  {
1308  mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
1309  }
1310 
1311  mMapThemeCollection->writeXml( *doc );
1312 
1313  mLabelingEngineSettings->writeSettingsToProject( this );
1314 
1315  QDomElement annotationsElem = mAnnotationManager->writeXml( *doc );
1316  qgisNode.appendChild( annotationsElem );
1317 
1318  QDomElement layoutElem = mLayoutManager->writeXml( *doc );
1319  qgisNode.appendChild( layoutElem );
1320 
1321  // now wrap it up and ship it to the project file
1322  doc->normalize(); // XXX I'm not entirely sure what this does
1323 
1324  // Create backup file
1325  if ( QFile::exists( fileName() ) )
1326  {
1327  QFile backupFile( fileName() + '~' );
1328  bool ok = true;
1329  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1330  ok &= mFile.open( QIODevice::ReadOnly );
1331 
1332  QByteArray ba;
1333  while ( ok && !mFile.atEnd() )
1334  {
1335  ba = mFile.read( 10240 );
1336  ok &= backupFile.write( ba ) == ba.size();
1337  }
1338 
1339  mFile.close();
1340  backupFile.close();
1341 
1342  if ( !ok )
1343  {
1344  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1345  return false;
1346  }
1347 
1348  QFileInfo fi( fileName() );
1349  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1350  utime( backupFile.fileName().toUtf8().constData(), &tb );
1351  }
1352 
1353  if ( !mFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1354  {
1355  mFile.close(); // even though we got an error, let's make
1356  // sure it's closed anyway
1357 
1358  setError( tr( "Unable to save to file %1" ).arg( mFile.fileName() ) );
1359  return false;
1360  }
1361 
1362  QTemporaryFile tempFile;
1363  bool ok = tempFile.open();
1364  if ( ok )
1365  {
1366  QTextStream projectFileStream( &tempFile );
1367  doc->save( projectFileStream, 2 ); // save as utf-8
1368  ok &= projectFileStream.pos() > -1;
1369 
1370  ok &= tempFile.seek( 0 );
1371 
1372  QByteArray ba;
1373  while ( ok && !tempFile.atEnd() )
1374  {
1375  ba = tempFile.read( 10240 );
1376  ok &= mFile.write( ba ) == ba.size();
1377  }
1378 
1379  ok &= mFile.error() == QFile::NoError;
1380 
1381  mFile.close();
1382  }
1383 
1384  tempFile.close();
1385 
1386  if ( !ok )
1387  {
1388  setError( tr( "Unable to save to file %1. Your project "
1389  "may be corrupted on disk. Try clearing some space on the volume and "
1390  "check file permissions before pressing save again." )
1391  .arg( mFile.fileName() ) );
1392  return false;
1393  }
1394 
1395  setDirty( false ); // reset to pristine state
1396 
1397  emit projectSaved();
1398 
1399  return true;
1400 }
1401 
1402 bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
1403 {
1404  setDirty( true );
1405 
1406  return addKey_( scope, key, &mProperties, value );
1407 }
1408 
1409 bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
1410 {
1411  setDirty( true );
1412 
1413  return addKey_( scope, key, &mProperties, value );
1414 }
1415 
1416 bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
1417 {
1418  setDirty( true );
1419 
1420  return addKey_( scope, key, &mProperties, value );
1421 }
1422 
1423 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
1424 {
1425  setDirty( true );
1426 
1427  return addKey_( scope, key, &mProperties, value );
1428 }
1429 
1430 bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
1431 {
1432  setDirty( true );
1433 
1434  return addKey_( scope, key, &mProperties, value );
1435 }
1436 
1437 QStringList QgsProject::readListEntry( const QString &scope,
1438  const QString &key,
1439  const QStringList &def,
1440  bool *ok ) const
1441 {
1442  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1443 
1444  QVariant value;
1445 
1446  if ( property )
1447  {
1448  value = property->value();
1449 
1450  bool valid = QVariant::StringList == value.type();
1451  if ( ok )
1452  *ok = valid;
1453 
1454  if ( valid )
1455  {
1456  return value.toStringList();
1457  }
1458  }
1459 
1460  return def;
1461 }
1462 
1463 
1464 QString QgsProject::readEntry( const QString &scope,
1465  const QString &key,
1466  const QString &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  bool valid = value.canConvert( QVariant::String );
1478  if ( ok )
1479  *ok = valid;
1480 
1481  if ( valid )
1482  return value.toString();
1483  }
1484 
1485  return def;
1486 }
1487 
1488 int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
1489  bool *ok ) const
1490 {
1491  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1492 
1493  QVariant value;
1494 
1495  if ( property )
1496  {
1497  value = property->value();
1498  }
1499 
1500  bool valid = value.canConvert( QVariant::Int );
1501 
1502  if ( ok )
1503  {
1504  *ok = valid;
1505  }
1506 
1507  if ( valid )
1508  {
1509  return value.toInt();
1510  }
1511 
1512  return def;
1513 }
1514 
1515 double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
1516  double def,
1517  bool *ok ) const
1518 {
1519  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1520  if ( property )
1521  {
1522  QVariant value = property->value();
1523 
1524  bool valid = value.canConvert( QVariant::Double );
1525  if ( ok )
1526  *ok = valid;
1527 
1528  if ( valid )
1529  return value.toDouble();
1530  }
1531 
1532  return def;
1533 }
1534 
1535 bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
1536  bool *ok ) const
1537 {
1538  QgsProjectProperty *property = findKey_( scope, key, mProperties );
1539 
1540  if ( property )
1541  {
1542  QVariant value = property->value();
1543 
1544  bool valid = value.canConvert( QVariant::Bool );
1545  if ( ok )
1546  *ok = valid;
1547 
1548  if ( valid )
1549  return value.toBool();
1550  }
1551 
1552  return def;
1553 }
1554 
1555 
1556 bool QgsProject::removeEntry( const QString &scope, const QString &key )
1557 {
1558  removeKey_( scope, key, mProperties );
1559 
1560  setDirty( true );
1561 
1562  return !findKey_( scope, key, mProperties );
1563 }
1564 
1565 
1566 QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
1567 {
1568  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1569 
1570  QStringList entries;
1571 
1572  if ( foundProperty )
1573  {
1574  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
1575 
1576  if ( propertyKey )
1577  { propertyKey->entryList( entries ); }
1578  }
1579 
1580  return entries;
1581 }
1582 
1583 QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
1584 {
1585  QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
1586 
1587  QStringList entries;
1588 
1589  if ( foundProperty )
1590  {
1591  QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
1592 
1593  if ( propertyKey )
1594  { propertyKey->subkeyList( entries ); }
1595  }
1596 
1597  return entries;
1598 }
1599 
1601 {
1602  dump_( mProperties );
1603 }
1604 
1606 {
1607  bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
1608  return QgsPathResolver( absolutePaths ? QString() : fileName() );
1609 }
1610 
1611 QString QgsProject::readPath( const QString &src ) const
1612 {
1613  return pathResolver().readPath( src );
1614 }
1615 
1616 QString QgsProject::writePath( const QString &src ) const
1617 {
1618  return pathResolver().writePath( src );
1619 }
1620 
1621 void QgsProject::setError( const QString &errorMessage )
1622 {
1623  mErrorMessage = errorMessage;
1624 }
1625 
1626 QString QgsProject::error() const
1627 {
1628  return mErrorMessage;
1629 }
1630 
1631 void QgsProject::clearError()
1632 {
1633  setError( QString() );
1634 }
1635 
1637 {
1638  delete mBadLayerHandler;
1639  mBadLayerHandler = handler;
1640 }
1641 
1642 QString QgsProject::layerIsEmbedded( const QString &id ) const
1643 {
1644  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1645  if ( it == mEmbeddedLayers.constEnd() )
1646  {
1647  return QString();
1648  }
1649  return it.value().first;
1650 }
1651 
1652 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1653  bool saveFlag )
1654 {
1655  QgsDebugCall;
1656 
1657  static QString sPrevProjectFilePath;
1658  static QDateTime sPrevProjectFileTimestamp;
1659  static QDomDocument sProjectDocument;
1660 
1661  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1662 
1663  if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
1664  {
1665  sPrevProjectFilePath.clear();
1666 
1667  QFile projectFile( projectFilePath );
1668  if ( !projectFile.open( QIODevice::ReadOnly ) )
1669  {
1670  return false;
1671  }
1672 
1673  if ( !sProjectDocument.setContent( &projectFile ) )
1674  {
1675  return false;
1676  }
1677 
1678  sPrevProjectFilePath = projectFilePath;
1679  sPrevProjectFileTimestamp = projectFileTimestamp;
1680  }
1681 
1682  // does project store paths absolute or relative?
1683  bool useAbsolutePaths = true;
1684 
1685  QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1686  if ( !propertiesElem.isNull() )
1687  {
1688  QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
1689  if ( !absElem.isNull() )
1690  {
1691  useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
1692  }
1693  }
1694 
1695  QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
1696  if ( projectLayersElem.isNull() )
1697  {
1698  return false;
1699  }
1700 
1701  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( QStringLiteral( "maplayer" ) );
1702  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1703  {
1704  // get layer id
1705  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1706  QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
1707  if ( id == layerId )
1708  {
1709  // layer can be embedded only once
1710  if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1711  {
1712  return false;
1713  }
1714 
1715  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1716 
1717  // change datasource path from relative to absolute if necessary
1718  // see also QgsMapLayer::readLayerXML
1719  if ( !useAbsolutePaths )
1720  {
1721  QString provider( mapLayerElem.firstChildElement( QStringLiteral( "provider" ) ).text() );
1722  QDomElement dsElem( mapLayerElem.firstChildElement( QStringLiteral( "datasource" ) ) );
1723  QString datasource( dsElem.text() );
1724  if ( provider == QLatin1String( "spatialite" ) )
1725  {
1726  QgsDataSourceUri uri( datasource );
1727  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + uri.database() );
1728  if ( absoluteDs.exists() )
1729  {
1730  uri.setDatabase( absoluteDs.absoluteFilePath() );
1731  datasource = uri.uri();
1732  }
1733  }
1734  else if ( provider == QLatin1String( "ogr" ) )
1735  {
1736  QStringList theURIParts( datasource.split( '|' ) );
1737  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1738  if ( absoluteDs.exists() )
1739  {
1740  theURIParts[0] = absoluteDs.absoluteFilePath();
1741  datasource = theURIParts.join( QStringLiteral( "|" ) );
1742  }
1743  }
1744  else if ( provider == QLatin1String( "gpx" ) )
1745  {
1746  QStringList theURIParts( datasource.split( '?' ) );
1747  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1748  if ( absoluteDs.exists() )
1749  {
1750  theURIParts[0] = absoluteDs.absoluteFilePath();
1751  datasource = theURIParts.join( QStringLiteral( "?" ) );
1752  }
1753  }
1754  else if ( provider == QLatin1String( "delimitedtext" ) )
1755  {
1756  QUrl urlSource( QUrl::fromEncoded( datasource.toLatin1() ) );
1757 
1758  if ( !datasource.startsWith( QLatin1String( "file:" ) ) )
1759  {
1760  QUrl file( QUrl::fromLocalFile( datasource.left( datasource.indexOf( '?' ) ) ) );
1761  urlSource.setScheme( QStringLiteral( "file" ) );
1762  urlSource.setPath( file.path() );
1763  }
1764 
1765  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + urlSource.toLocalFile() );
1766  if ( absoluteDs.exists() )
1767  {
1768  QUrl urlDest = QUrl::fromLocalFile( absoluteDs.absoluteFilePath() );
1769  urlDest.setQueryItems( urlSource.queryItems() );
1770  datasource = QString::fromAscii( urlDest.toEncoded() );
1771  }
1772  }
1773  else
1774  {
1775  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + datasource );
1776  if ( absoluteDs.exists() )
1777  {
1778  datasource = absoluteDs.absoluteFilePath();
1779  }
1780  }
1781 
1782  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1783  dsElem.appendChild( sProjectDocument.createTextNode( datasource ) );
1784  }
1785 
1786  if ( addLayer( mapLayerElem, brokenNodes ) )
1787  {
1788  return true;
1789  }
1790  else
1791  {
1792  mEmbeddedLayers.remove( layerId );
1793  return false;
1794  }
1795  }
1796  }
1797 
1798  return false;
1799 }
1800 
1801 
1802 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1803 {
1804  // open project file, get layer ids in group, add the layers
1805  QFile projectFile( projectFilePath );
1806  if ( !projectFile.open( QIODevice::ReadOnly ) )
1807  {
1808  return nullptr;
1809  }
1810 
1811  QDomDocument projectDocument;
1812  if ( !projectDocument.setContent( &projectFile ) )
1813  {
1814  return nullptr;
1815  }
1816 
1817  // store identify disabled layers of the embedded project
1818  QSet<QString> embeddedIdentifyDisabledLayers;
1819  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) ).firstChildElement( QStringLiteral( "Identify" ) ).firstChildElement( QStringLiteral( "disabledLayers" ) );
1820  if ( !disabledLayersElem.isNull() )
1821  {
1822  QDomNodeList valueList = disabledLayersElem.elementsByTagName( QStringLiteral( "value" ) );
1823  for ( int i = 0; i < valueList.size(); ++i )
1824  {
1825  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1826  }
1827  }
1828 
1830 
1831  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1832  if ( !layerTreeElem.isNull() )
1833  {
1834  root->readChildrenFromXml( layerTreeElem );
1835  }
1836  else
1837  {
1838  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1839  }
1840 
1841  QgsLayerTreeGroup *group = root->findGroup( groupName );
1842  if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
1843  {
1844  // embedded groups cannot be embedded again
1845  delete root;
1846  return nullptr;
1847  }
1848 
1849  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1850  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1851  delete root;
1852  root = nullptr;
1853 
1854  newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
1855  newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
1856 
1857  // set "embedded" to all children + load embedded layers
1858  mLayerTreeRegistryBridge->setEnabled( false );
1859  initializeEmbeddedSubtree( projectFilePath, newGroup );
1860  mLayerTreeRegistryBridge->setEnabled( true );
1861 
1862  QStringList thisProjectIdentifyDisabledLayers = nonIdentifiableLayers();
1863 
1864  // consider the layers might be identify disabled in its project
1865  Q_FOREACH ( const QString &layerId, newGroup->findLayerIds() )
1866  {
1867  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1868  {
1869  thisProjectIdentifyDisabledLayers.append( layerId );
1870  }
1871 
1872  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1873  if ( layer )
1874  {
1875  layer->setItemVisibilityChecked( invisibleLayers.contains( layerId ) );
1876  }
1877  }
1878 
1879  setNonIdentifiableLayers( thisProjectIdentifyDisabledLayers );
1880 
1881  return newGroup;
1882 }
1883 
1884 void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group )
1885 {
1886  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1887  {
1888  // all nodes in the subtree will have "embedded" custom property set
1889  child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
1890 
1891  if ( QgsLayerTree::isGroup( child ) )
1892  {
1893  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1894  }
1895  else if ( QgsLayerTree::isLayer( child ) )
1896  {
1897  // load the layer into our project
1898  QList<QDomNode> brokenNodes;
1899  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false );
1900  }
1901  }
1902 }
1903 
1905 {
1906  return mEvaluateDefaultValues;
1907 }
1908 
1910 {
1911  Q_FOREACH ( QgsMapLayer *layer, mapLayers().values() )
1912  {
1913  QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layer );
1914  if ( vl )
1915  {
1917  }
1918  }
1919 
1920  mEvaluateDefaultValues = evaluateDefaultValues;
1921 }
1922 
1924 {
1925  writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
1927 }
1928 
1930 {
1931  return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
1932 }
1933 
1935 {
1936  QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
1937  if ( !distanceUnitString.isEmpty() )
1938  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
1939 
1940  //fallback to QGIS default measurement unit
1941  QgsSettings s;
1942  bool ok = false;
1943  QgsUnitTypes::DistanceUnit type = QgsUnitTypes::decodeDistanceUnit( s.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
1944  return ok ? type : QgsUnitTypes::DistanceMeters;
1945 }
1946 
1948 {
1949  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( unit ) );
1950 }
1951 
1953 {
1954  QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
1955  if ( !areaUnitString.isEmpty() )
1956  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
1957 
1958  //fallback to QGIS default area unit
1959  QgsSettings s;
1960  bool ok = false;
1961  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
1962  return ok ? type : QgsUnitTypes::AreaSquareMeters;
1963 }
1964 
1966 {
1967  writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( unit ) );
1968 }
1969 
1970 QString QgsProject::homePath() const
1971 {
1972  QFileInfo pfi( fileName() );
1973  if ( !pfi.exists() )
1974  return QString::null;
1975 
1976  return pfi.canonicalPath();
1977 }
1978 
1980 {
1981  return mRelationManager;
1982 }
1983 
1985 {
1986  return mLayoutManager.get();
1987 }
1988 
1990 {
1991  return mLayoutManager.get();
1992 }
1993 
1995 {
1996  return mRootGroup;
1997 }
1998 
2000 {
2001  return mMapThemeCollection.get();
2002 }
2003 
2005 {
2006  return mAnnotationManager.get();
2007 }
2008 
2009 void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
2010 {
2011  QStringList currentLayers = nonIdentifiableLayers();
2012 
2013  QStringList newLayers;
2014  Q_FOREACH ( QgsMapLayer *l, layers )
2015  {
2016  newLayers << l->id();
2017  }
2018 
2019  if ( newLayers == currentLayers )
2020  return;
2021 
2022  QStringList disabledLayerIds;
2023 
2024  Q_FOREACH ( QgsMapLayer *l, layers )
2025  {
2026  disabledLayerIds << l->id();
2027  }
2028 
2029  setNonIdentifiableLayers( disabledLayerIds );
2030 }
2031 
2032 void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
2033 {
2034  writeEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ), layerIds );
2035 
2036  emit nonIdentifiableLayersChanged( layerIds );
2037 }
2038 
2039 QStringList QgsProject::nonIdentifiableLayers() const
2040 {
2041  return readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
2042 }
2043 
2045 {
2046  return mAutoTransaction;
2047 }
2048 
2050 {
2051  if ( autoTransaction != mAutoTransaction )
2052  {
2053  mAutoTransaction = autoTransaction;
2054 
2055  if ( autoTransaction )
2056  onMapLayersAdded( mapLayers().values() );
2057  else
2058  cleanTransactionGroups( true );
2059  }
2060 }
2061 
2062 QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
2063 {
2064  return mTransactionGroups;
2065 }
2066 
2067 
2068 //
2069 // QgsMapLayerRegistry methods
2070 //
2071 
2072 
2074 {
2075  return mMapLayers.size();
2076 }
2077 
2078 QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
2079 {
2080  return mMapLayers.value( layerId );
2081 }
2082 
2083 QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
2084 {
2085  QList<QgsMapLayer *> myResultList;
2086  Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
2087  {
2088  if ( layer->name() == layerName )
2089  {
2090  myResultList << layer;
2091  }
2092  }
2093  return myResultList;
2094 }
2095 
2096 QList<QgsMapLayer *> QgsProject::addMapLayers(
2097  const QList<QgsMapLayer *> &layers,
2098  bool addToLegend,
2099  bool takeOwnership )
2100 {
2101  QList<QgsMapLayer *> myResultList;
2102  Q_FOREACH ( QgsMapLayer *myLayer, layers )
2103  {
2104  if ( !myLayer || !myLayer->isValid() )
2105  {
2106  QgsDebugMsg( "Cannot add invalid layers" );
2107  continue;
2108  }
2109  //check the layer is not already registered!
2110  if ( !mMapLayers.contains( myLayer->id() ) )
2111  {
2112  mMapLayers[myLayer->id()] = myLayer;
2113  myResultList << mMapLayers[myLayer->id()];
2114  if ( takeOwnership )
2115  {
2116  myLayer->setParent( this );
2117  }
2118  connect( myLayer, &QObject::destroyed, this, &QgsProject::onMapLayerDeleted );
2119  emit layerWasAdded( myLayer );
2120  }
2121  }
2122  if ( !myResultList.isEmpty() )
2123  {
2124  emit layersAdded( myResultList );
2125 
2126  if ( addToLegend )
2127  emit legendLayersAdded( myResultList );
2128  }
2129  return myResultList;
2130 }
2131 
2132 QgsMapLayer *
2134  bool addToLegend,
2135  bool takeOwnership )
2136 {
2137  QList<QgsMapLayer *> addedLayers;
2138  addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
2139  return addedLayers.isEmpty() ? nullptr : addedLayers[0];
2140 }
2141 
2142 void QgsProject::removeMapLayers( const QStringList &layerIds )
2143 {
2144  QList<QgsMapLayer *> layers;
2145  Q_FOREACH ( const QString &myId, layerIds )
2146  {
2147  layers << mMapLayers.value( myId );
2148  }
2149 
2150  removeMapLayers( layers );
2151 }
2152 
2153 void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
2154 {
2155  if ( layers.isEmpty() )
2156  return;
2157 
2158  QStringList layerIds;
2159  QList<QgsMapLayer *> layerList;
2160 
2161  Q_FOREACH ( QgsMapLayer *layer, layers )
2162  {
2163  // check layer and the registry contains it
2164  if ( layer && mMapLayers.contains( layer->id() ) )
2165  {
2166  layerIds << layer->id();
2167  layerList << layer;
2168  }
2169  }
2170 
2171  if ( layerIds.isEmpty() )
2172  return;
2173 
2174  emit layersWillBeRemoved( layerIds );
2175  emit layersWillBeRemoved( layerList );
2176 
2177  Q_FOREACH ( QgsMapLayer *lyr, layerList )
2178  {
2179  QString myId( lyr->id() );
2180  emit layerWillBeRemoved( myId );
2181  emit layerWillBeRemoved( lyr );
2182  mMapLayers.remove( myId );
2183  if ( lyr->parent() == this )
2184  {
2185  delete lyr;
2186  }
2187  emit layerRemoved( myId );
2188  }
2189 
2190  emit layersRemoved( layerIds );
2191 }
2192 
2193 void QgsProject::removeMapLayer( const QString &layerId )
2194 {
2195  removeMapLayers( QList<QgsMapLayer *>() << mMapLayers.value( layerId ) );
2196 }
2197 
2199 {
2200  if ( layer )
2201  removeMapLayers( QList<QgsMapLayer *>() << layer );
2202 }
2203 
2205 {
2206  emit removeAll();
2207  // now let all observers know to clear themselves,
2208  // and then consequently any of their map legends
2209  removeMapLayers( mMapLayers.keys() );
2210  mMapLayers.clear();
2211 }
2212 
2214 {
2215  Q_FOREACH ( QgsMapLayer *layer, mMapLayers )
2216  {
2217  layer->reload();
2218  }
2219 }
2220 
2221 void QgsProject::onMapLayerDeleted( QObject *obj )
2222 {
2223  QString id = mMapLayers.key( static_cast<QgsMapLayer *>( obj ) );
2224 
2225  if ( !id.isNull() )
2226  {
2227  QgsDebugMsg( QString( "Map layer deleted without unregistering! %1" ).arg( id ) );
2228  mMapLayers.remove( id );
2229  }
2230 }
2231 
2232 QMap<QString, QgsMapLayer *> QgsProject::mapLayers() const
2233 {
2234  return mMapLayers;
2235 }
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:391
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:386
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:53
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:494
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:54
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:616
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:338
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:396
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.
void labelingEngineSettingsChanged()
Emitted when global configuration of the labeling engine changes.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application&#39;s plugin layer registry, used for managing plugin layer types.
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
void topologicalEditingChanged()
Emitted when the topological editing flag has changed.
int count() const
Returns the number of registered layers.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
void reloadAllLayers()
Reload all registered layer&#39;s provider data caches, synchronising the layer with any changes in the d...
virtual void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
void legendLayersAdded(const QList< QgsMapLayer *> &layers)
Emitted, when a layer was added to the registry and the legend.
bool isValid() const
Return the status of the layer.
void removeKey(const QString &keyName)
Removes the specified key.
void projectSaved()
emitted when the project file has been written and closed
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:593
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:741
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:559
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:37
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.
void setCrs(const QgsCoordinateReferenceSystem &crs)
Sets the project&#39;s native coordinate reference system.
Definition: qgsproject.cpp:428
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:75
void setEllipsoid(const QString &ellipsoid)
Sets the project&#39;s ellipsoid from a proj string representation, e.g., "WGS84".
Definition: qgsproject.cpp:447
void insertChildNodes(int index, const QList< QgsLayerTreeNode *> &nodes)
Insert existing nodes at specified position.
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...
static void logMessage(const QString &message, const QString &tag=QString(), MessageLevel level=QgsMessageLog::WARNING)
add a message to the instance (and create it if necessary)
void writeProject(QDomDocument &)
emitted when project is being written
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:567
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:418
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the registry.
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:360
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:369
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.
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
Definition: qgsmaplayer.h:57
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
Returns the group&#39;s name.
void avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
Container class that allows storage of map themes consisting of visible map layers and layer styles...
QString title() const
Returns the project&#39;s title.
Definition: qgsproject.cpp:380
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.
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.
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)
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
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())
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:531
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.
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:454
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...
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project&#39;s global labeling engine settings.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.