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