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