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