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