QGIS API Documentation  2.14.0-Essen
qgsproject.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsproject.cpp - description
3  -------------------
4  begin : July 23, 2004
5  copyright : (C) 2004 by Mark Coletti
6  email : mcoletti at gmail.com
7 ***************************************************************************/
8 
9 /***************************************************************************
10  * *
11  * This program is free software; you can redistribute it and/or modify *
12  * it under the terms of the GNU General Public License as published by *
13  * the Free Software Foundation; either version 2 of the License, or *
14  * (at your option) any later version. *
15  * *
16  ***************************************************************************/
17 
18 #include "qgsproject.h"
19 
20 #include "qgsdatasourceuri.h"
21 #include "qgsexception.h"
22 #include "qgslayertree.h"
23 #include "qgslayertreeutils.h"
25 #include "qgslogger.h"
26 #include "qgsmaplayerregistry.h"
27 #include "qgsmessagelog.h"
28 #include "qgspluginlayer.h"
29 #include "qgspluginlayerregistry.h"
31 #include "qgsprojectproperty.h"
32 #include "qgsprojectversion.h"
33 #include "qgsrasterlayer.h"
34 #include "qgsrectangle.h"
35 #include "qgsrelationmanager.h"
36 #include "qgsvectorlayer.h"
38 #include "qgslayerdefinition.h"
39 #include "qgsunittypes.h"
40 
41 #include <QApplication>
42 #include <QFileInfo>
43 #include <QDomNode>
44 #include <QObject>
45 #include <QTextStream>
46 #include <QTemporaryFile>
47 #include <QDir>
48 #include <QUrl>
49 #include <QSettings>
50 
51 #ifdef Q_OS_UNIX
52 #include <utime.h>
53 #elif _MSC_VER
54 #include <sys/utime.h>
55 #endif
56 
57 // canonical project instance
58 QgsProject *QgsProject::theProject_ = nullptr;
59 
68 static
69 QStringList makeKeyTokens_( QString const &scope, QString const &key )
70 {
71  QStringList keyTokens = QStringList( scope );
72  keyTokens += key.split( '/', QString::SkipEmptyParts );
73 
74  // be sure to include the canonical root node
75  keyTokens.push_front( "properties" );
76 
77  //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.
78  for ( int i = 0; i < keyTokens.size(); ++i )
79  {
80  QString keyToken = keyTokens.at( i );
81 
82  //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
83  //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
84  QString nameCharRegexp = QString( "[^: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]" );
85  QString nameStartCharRegexp = QString( "^[^: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]" );
86 
87  if ( keyToken.contains( QRegExp( nameCharRegexp ) ) || keyToken.contains( QRegExp( nameStartCharRegexp ) ) )
88  {
89 
90  QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
91  QgsMessageLog::logMessage( errorString, QString::null, QgsMessageLog::CRITICAL );
92 
93  }
94 
95  }
96 
97  return keyTokens;
98 } // makeKeyTokens_
99 
100 
101 
102 
112 static
113 QgsProperty *findKey_( QString const &scope,
114  QString const &key,
115  QgsPropertyKey &rootProperty )
116 {
117  QgsPropertyKey *currentProperty = &rootProperty;
118  QgsProperty *nextProperty; // link to next property down hiearchy
119 
120  QStringList keySequence = makeKeyTokens_( scope, key );
121 
122  while ( !keySequence.isEmpty() )
123  {
124  // if the current head of the sequence list matches the property name,
125  // then traverse down the property hierarchy
126  if ( keySequence.first() == currentProperty->name() )
127  {
128  // remove front key since we're traversing down a level
129  keySequence.pop_front();
130 
131  if ( 1 == keySequence.count() )
132  {
133  // if we have only one key name left, then return the key found
134  return currentProperty->find( keySequence.front() );
135  }
136  else if ( keySequence.isEmpty() )
137  {
138  // if we're out of keys then the current property is the one we
139  // want; i.e., we're in the rate case of being at the top-most
140  // property node
141  return currentProperty;
142  }
143  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
144  {
145  if ( nextProperty->isKey() )
146  {
147  currentProperty = static_cast<QgsPropertyKey*>( nextProperty );
148  }
149  else if ( nextProperty->isValue() && 1 == keySequence.count() )
150  {
151  // it may be that this may be one of several property value
152  // nodes keyed by QDict string; if this is the last remaining
153  // key token and the next property is a value node, then
154  // that's the situation, so return the currentProperty
155  return currentProperty;
156  }
157  else
158  {
159  // QgsPropertyValue not Key, so return null
160  return nullptr;
161  }
162  }
163  else
164  {
165  // if the next key down isn't found
166  // then the overall key sequence doesn't exist
167  return nullptr;
168  }
169  }
170  else
171  {
172  return nullptr;
173  }
174  }
175 
176  return nullptr;
177 } // findKey_
178 
179 
180 
188 static
189 QgsProperty *addKey_( QString const &scope,
190  QString const &key,
191  QgsPropertyKey *rootProperty,
192  const QVariant& value )
193 {
194  QStringList keySequence = makeKeyTokens_( scope, key );
195 
196  // cursor through property key/value hierarchy
197  QgsPropertyKey *currentProperty = rootProperty;
198  QgsProperty *nextProperty; // link to next property down hiearchy
199  QgsPropertyKey* newPropertyKey;
200 
201  while ( ! keySequence.isEmpty() )
202  {
203  // if the current head of the sequence list matches the property name,
204  // then traverse down the property hierarchy
205  if ( keySequence.first() == currentProperty->name() )
206  {
207  // remove front key since we're traversing down a level
208  keySequence.pop_front();
209 
210  // if key sequence has one last element, then we use that as the
211  // name to store the value
212  if ( 1 == keySequence.count() )
213  {
214  currentProperty->setValue( keySequence.front(), value );
215  return currentProperty;
216  }
217  // we're at the top element if popping the keySequence element
218  // will leave it empty; in that case, just add the key
219  else if ( keySequence.isEmpty() )
220  {
221  currentProperty->setValue( value );
222 
223  return currentProperty;
224  }
225  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
226  {
227  currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
228 
229  if ( currentProperty )
230  {
231  continue;
232  }
233  else // QgsPropertyValue not Key, so return null
234  {
235  return nullptr;
236  }
237  }
238  else // the next subkey doesn't exist, so add it
239  {
240  if (( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
241  {
242  currentProperty = newPropertyKey;
243  }
244  continue;
245  }
246  }
247  else
248  {
249  return nullptr;
250  }
251  }
252 
253  return nullptr;
254 
255 } // addKey_
256 
257 
258 
259 static
260 void removeKey_( QString const &scope,
261  QString const &key,
262  QgsPropertyKey &rootProperty )
263 {
264  QgsPropertyKey *currentProperty = &rootProperty;
265 
266  QgsProperty *nextProperty = nullptr; // link to next property down hiearchy
267  QgsPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hiearchy
268 
269  QStringList keySequence = makeKeyTokens_( scope, key );
270 
271  while ( ! keySequence.isEmpty() )
272  {
273  // if the current head of the sequence list matches the property name,
274  // then traverse down the property hierarchy
275  if ( keySequence.first() == currentProperty->name() )
276  {
277  // remove front key since we're traversing down a level
278  keySequence.pop_front();
279 
280  // if we have only one key name left, then try to remove the key
281  // with that name
282  if ( 1 == keySequence.count() )
283  {
284  currentProperty->removeKey( keySequence.front() );
285  }
286  // if we're out of keys then the current property is the one we
287  // want to remove, but we can't delete it directly; we need to
288  // delete it from the parent property key container
289  else if ( keySequence.isEmpty() )
290  {
291  previousQgsPropertyKey->removeKey( currentProperty->name() );
292  }
293  else if (( nextProperty = currentProperty->find( keySequence.first() ) ) )
294  {
295  previousQgsPropertyKey = currentProperty;
296  currentProperty = dynamic_cast<QgsPropertyKey*>( nextProperty );
297 
298  if ( currentProperty )
299  {
300  continue;
301  }
302  else // QgsPropertyValue not Key, so return null
303  {
304  return;
305  }
306  }
307  else // if the next key down isn't found
308  { // then the overall key sequence doesn't exist
309  return;
310  }
311  }
312  else
313  {
314  return;
315  }
316  }
317 
318 } // void removeKey_
319 
320 
321 
323 {
324  QFile file; // current physical project file
325  QgsPropertyKey properties_; // property hierarchy
326  QString title; // project title
327  bool dirty; // project has been modified since it has been read or saved
328 
329  Imp()
330  : title()
331  , dirty( false )
332  { // top property node is the root
333  // "properties" that contains all plug-in
334  // and extra property keys and values
335  properties_.name() = "properties"; // root property node always this value
336  }
337 
340  void clear()
341  {
342  // QgsDebugMsg( "Clearing project properties Impl->clear();" );
343 
344  file.setFileName( QString() );
345  properties_.clearKeys();
346  title.clear();
347  dirty = false;
348  }
349 
350 }; // struct QgsProject::Imp
351 
352 
353 
354 QgsProject::QgsProject()
355  : imp_( new QgsProject::Imp )
356  , mBadLayerHandler( new QgsProjectBadLayerDefaultHandler() )
357  , mRelationManager( new QgsRelationManager( this ) )
358  , mRootGroup( new QgsLayerTreeGroup )
359 {
360  clear();
361 
362  // bind the layer tree to the map layer registry.
363  // whenever layers are added to or removed from the registry,
364  // layer tree will be updated
365  mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this );
366 } // QgsProject ctor
367 
368 
369 
371 {
372  delete mBadLayerHandler;
373  delete mRelationManager;
374  delete mRootGroup;
375 
376  // note that QScopedPointer automatically deletes imp_ when it's destroyed
377 } // QgsProject dtor
378 
379 
380 
382 {
383  if ( !theProject_ )
384  {
385  theProject_ = new QgsProject;
386  }
387  return theProject_;
388 } // QgsProject *instance()
389 
391 {
392  imp_->title = title;
393 
394  dirty( true );
395 }
396 
397 
399 {
400  return imp_->title;
401 } // QgsProject::title() const
402 
403 
405 {
406  return imp_->dirty;
407 } // bool QgsProject::isDirty()
408 
409 
410 void QgsProject::dirty( bool b )
411 {
412  imp_->dirty = b;
413 } // bool QgsProject::isDirty()
414 
415 void QgsProject::setDirty( bool b )
416 {
417  dirty( b );
418 }
419 
420 
421 
423 {
424  imp_->file.setFileName( name );
425 
426  dirty( true );
427 } // void QgsProject::setFileName( QString const &name )
428 
429 
430 
432 {
433  return imp_->file.fileName();
434 }
435 
437 {
438  return QFileInfo( imp_->file );
439 }
440 
442 {
443  imp_->clear();
444  mEmbeddedLayers.clear();
445  mRelationManager->clear();
446 
447  mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
448 
449  mRootGroup->removeAllChildren();
450 
451  // reset some default project properties
452  // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
453  writeEntry( "PositionPrecision", "/Automatic", true );
454  writeEntry( "PositionPrecision", "/DecimalPlaces", 2 );
455  writeEntry( "Paths", "/Absolute", false );
456 
457  //copy default units to project
458  QSettings s;
459  writeEntry( "Measurement", "/DistanceUnits", s.value( "/qgis/measure/displayunits" ).toString() );
460  writeEntry( "Measurement", "/AreaUnits", s.value( "/qgis/measure/areaunits" ).toString() );
461 
462  setDirty( false );
463 }
464 
465 // basically a debugging tool to dump property list values
466 static void dump_( QgsPropertyKey const &topQgsPropertyKey )
467 {
468  QgsDebugMsg( "current properties:" );
469  topQgsPropertyKey.dump();
470 } // dump_
471 
472 
503 static
504 void
505 _getProperties( QDomDocument const &doc, QgsPropertyKey &project_properties )
506 {
507  QDomNodeList properties = doc.elementsByTagName( "properties" );
508 
509  if ( properties.count() > 1 )
510  {
511  QgsDebugMsg( "there appears to be more than one ``properties'' XML tag ... bailing" );
512  return;
513  }
514  else if ( properties.count() < 1 ) // no properties found, so we're done
515  {
516  return;
517  }
518 
519  // item(0) because there should only be ONE "properties" node
520  QDomNodeList scopes = properties.item( 0 ).childNodes();
521 
522  if ( scopes.count() < 1 )
523  {
524  QgsDebugMsg( "empty ``properties'' XML tag ... bailing" );
525  return;
526  }
527 
528  QDomNode propertyNode = properties.item( 0 );
529 
530  if ( ! project_properties.readXML( propertyNode ) )
531  {
532  QgsDebugMsg( "Project_properties.readXML() failed" );
533  }
534 } // _getProperties
535 
536 
537 
538 
550 static void _getTitle( QDomDocument const &doc, QString &title )
551 {
552  QDomNodeList nl = doc.elementsByTagName( "title" );
553 
554  title = ""; // by default the title will be empty
555 
556  if ( !nl.count() )
557  {
558  QgsDebugMsg( "unable to find title element" );
559  return;
560  }
561 
562  QDomNode titleNode = nl.item( 0 ); // there should only be one, so zeroth element ok
563 
564  if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
565  {
566  QgsDebugMsg( "unable to find title element" );
567  return;
568  }
569 
570  QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
571 
572  if ( !titleTextNode.isText() )
573  {
574  QgsDebugMsg( "unable to find title element" );
575  return;
576  }
577 
578  QDomText titleText = titleTextNode.toText();
579 
580  title = titleText.data();
581 
582 } // _getTitle
583 
584 
590 {
591  QDomNodeList nl = doc.elementsByTagName( "qgis" );
592 
593  if ( !nl.count() )
594  {
595  QgsDebugMsg( " unable to find qgis element in project file" );
596  return QgsProjectVersion( 0, 0, 0, QString() );
597  }
598 
599  QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element ok
600 
601  QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
602  QgsProjectVersion projectVersion( qgisElement.attribute( "version" ) );
603  return projectVersion;
604 } // _getVersion
605 
606 
607 
655 QPair< bool, QList<QDomNode> > QgsProject::_getMapLayers( QDomDocument const &doc )
656 {
657  // Layer order is set by the restoring the legend settings from project file.
658  // This is done on the 'readProject( ... )' signal
659 
660  QDomNodeList nl = doc.elementsByTagName( "maplayer" );
661 
662  QList<QDomNode> brokenNodes; // a list of Dom nodes corresponding to layers
663  // that we were unable to load; this could be
664  // because the layers were removed or
665  // re-located after the project was last saved
666 
667  // process the map layer nodes
668 
669  if ( 0 == nl.count() ) // if we have no layers to process, bail
670  {
671  return qMakePair( true, brokenNodes ); // Decided to return "true" since it's
672  // possible for there to be a project with no
673  // layers; but also, more imporantly, this
674  // would cause the tests/qgsproject to fail
675  // since the test suite doesn't currently
676  // support test layers
677  }
678 
679  bool returnStatus = true;
680 
681  emit layerLoaded( 0, nl.count() );
682 
683  // order layers based on their dependencies
684  QgsLayerDefinition::DependencySorter depSorter( doc );
685  if ( depSorter.hasCycle() || depSorter.hasMissingDependency() )
686  return qMakePair( false, QList<QDomNode>() );
687 
688  QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
689 
690  // Collect vector layers with joins.
691  // They need to refresh join caches and symbology infos after all layers are loaded
693  int i = 0;
694  Q_FOREACH ( const QDomNode& node, sortedLayerNodes )
695  {
696  QDomElement element = node.toElement();
697 
698  QString name = node.namedItem( "layername" ).toElement().text();
699  if ( !name.isNull() )
700  emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
701 
702  if ( element.attribute( "embedded" ) == "1" )
703  {
704  createEmbeddedLayer( element.attribute( "id" ), readPath( element.attribute( "project" ) ), brokenNodes, vLayerList );
705  continue;
706  }
707  else
708  {
709  if ( !addLayer( element, brokenNodes, vLayerList ) )
710  {
711  returnStatus = false;
712  }
713  }
714  emit layerLoaded( i + 1, nl.count() );
715  i++;
716  }
717 
718  // Update field map of layers with joins and create join caches if necessary
719  // Needs to be done here once all dependent layers are loaded
720  QList< QPair< QgsVectorLayer*, QDomElement > >::iterator vIt = vLayerList.begin();
721  for ( ; vIt != vLayerList.end(); ++vIt )
722  {
723  vIt->first->createJoinCaches();
724  vIt->first->updateFields();
725  }
726 
727  QSet<QgsVectorLayer *> notified;
728  for ( vIt = vLayerList.begin(); vIt != vLayerList.end(); ++vIt )
729  {
730  if ( notified.contains( vIt->first ) )
731  continue;
732 
733  notified << vIt->first;
734  emit readMapLayer( vIt->first, vIt->second );
735  }
736 
737 
738 
739  return qMakePair( returnStatus, brokenNodes );
740 } // _getMapLayers
741 
742 bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList )
743 {
744  QString type = layerElem.attribute( "type" );
745  QgsDebugMsg( "Layer type is " + type );
746  QgsMapLayer *mapLayer = nullptr;
747 
748  if ( type == "vector" )
749  {
750  mapLayer = new QgsVectorLayer;
751  }
752  else if ( type == "raster" )
753  {
754  mapLayer = new QgsRasterLayer;
755  }
756  else if ( type == "plugin" )
757  {
758  QString typeName = layerElem.attribute( "name" );
759  mapLayer = QgsPluginLayerRegistry::instance()->createLayer( typeName );
760  }
761 
762  if ( !mapLayer )
763  {
764  QgsDebugMsg( "Unable to create layer" );
765 
766  return false;
767  }
768 
769  Q_CHECK_PTR( mapLayer );
770 
771  // have the layer restore state that is stored in Dom node
772  if ( mapLayer->readLayerXML( layerElem ) && mapLayer->isValid() )
773  {
774  // postpone readMapLayer signal for vector layers with joins
775  QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer*>( mapLayer );
776  if ( !vLayer || vLayer->vectorJoins().isEmpty() )
777  emit readMapLayer( mapLayer, layerElem );
778  else
779  vectorLayerList.push_back( qMakePair( vLayer, layerElem ) );
780 
781  QList<QgsMapLayer *> myLayers;
782  myLayers << mapLayer;
784 
785  return true;
786  }
787  else
788  {
789  delete mapLayer;
790 
791  QgsDebugMsg( "Unable to load " + type + " layer" );
792  brokenNodes.push_back( layerElem );
793  return false;
794  }
795 }
796 
797 
802 {
803  imp_->file.setFileName( file.filePath() );
804 
805  return read();
806 } // QgsProject::read
807 
808 
809 
814 {
815  clearError();
816 
817  QScopedPointer<QDomDocument> doc( new QDomDocument( "qgis" ) );
818 
819  if ( !imp_->file.open( QIODevice::ReadOnly | QIODevice::Text ) )
820  {
821  imp_->file.close();
822 
823  setError( tr( "Unable to open %1" ).arg( imp_->file.fileName() ) );
824 
825  return false;
826  }
827 
828  // location of problem associated with errorMsg
829  int line, column;
830  QString errorMsg;
831 
832  if ( !doc->setContent( &imp_->file, &errorMsg, &line, &column ) )
833  {
834  // want to make this class as GUI independent as possible; so commented out
835 #if 0
836  QMessageBox::critical( 0, tr( "Project File Read Error" ),
837  tr( "%1 at line %2 column %3" ).arg( errorMsg ).arg( line ).arg( column ) );
838 #endif
839 
840  QString errorString = tr( "Project file read error: %1 at line %2 column %3" )
841  .arg( errorMsg ).arg( line ).arg( column );
842 
843  QgsDebugMsg( errorString );
844 
845  imp_->file.close();
846 
847  setError( tr( "%1 for file %2" ).arg( errorString, imp_->file.fileName() ) );
848 
849  return false;
850  }
851 
852  imp_->file.close();
853 
854 
855  QgsDebugMsg( "Opened document " + imp_->file.fileName() );
856  QgsDebugMsg( "Project title: " + imp_->title );
857 
858  // get project version string, if any
859  QgsProjectVersion fileVersion = _getVersion( *doc );
860  QgsProjectVersion thisVersion( QGis::QGIS_VERSION );
861 
862  if ( thisVersion > fileVersion )
863  {
864  QgsLogger::warning( "Loading a file that was saved with an older "
865  "version of qgis (saved in " + fileVersion.text() +
866  ", loaded in " + QGis::QGIS_VERSION +
867  "). Problems may occur." );
868 
869  QgsProjectFileTransform projectFile( *doc, fileVersion );
870 
872  emit oldProjectVersionWarning( fileVersion.text() );
873  QgsDebugMsg( "Emitting oldProjectVersionWarning(oldVersion)." );
874 
875  projectFile.updateRevision( thisVersion );
876  }
877 
878  // start new project, just keep the file name
879  QString fileName = imp_->file.fileName();
880  clear();
881  imp_->file.setFileName( fileName );
882 
883  // now get any properties
884  _getProperties( *doc, imp_->properties_ );
885 
886  QgsDebugMsg( QString::number( imp_->properties_.count() ) + " properties read" );
887 
888  dump_( imp_->properties_ );
889 
890  // now get project title
891  _getTitle( *doc, imp_->title );
892 
893  // read the layer tree from project file
894 
895  mRootGroup->setCustomProperty( "loading", 1 );
896 
897  QDomElement layerTreeElem = doc->documentElement().firstChildElement( "layer-tree-group" );
898  if ( !layerTreeElem.isNull() )
899  {
900  mRootGroup->readChildrenFromXML( layerTreeElem );
901  }
902  else
903  {
904  QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( "legend" ) );
905  }
906 
907  QgsDebugMsg( "Loaded layer tree:\n " + mRootGroup->dump() );
908 
909  mLayerTreeRegistryBridge->setEnabled( false );
910 
911  // get the map layers
912  QPair< bool, QList<QDomNode> > getMapLayersResults = _getMapLayers( *doc );
913 
914  // review the integrity of the retrieved map layers
915  bool clean = getMapLayersResults.first;
916 
917  if ( !clean )
918  {
919  QgsDebugMsg( "Unable to get map layers from project file." );
920 
921  if ( ! getMapLayersResults.second.isEmpty() )
922  {
923  QgsDebugMsg( "there are " + QString::number( getMapLayersResults.second.size() ) + " broken layers" );
924  }
925 
926  // we let a custom handler to decide what to do with missing layers
927  // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
928  mBadLayerHandler->handleBadLayers( getMapLayersResults.second, *doc );
929  }
930 
931  mLayerTreeRegistryBridge->setEnabled( true );
932 
933  // load embedded groups and layers
934  loadEmbeddedNodes( mRootGroup );
935 
936  // make sure the are just valid layers
938 
939  mRootGroup->removeCustomProperty( "loading" );
940 
941  mVisibilityPresetCollection.reset( new QgsVisibilityPresetCollection() );
942  mVisibilityPresetCollection->readXML( *doc );
943 
944  // read the project: used by map canvas and legend
945  emit readProject( *doc );
946 
947  // if all went well, we're allegedly in pristine state
948  if ( clean )
949  dirty( false );
950 
951  return true;
952 
953 } // QgsProject::read
954 
955 
957 {
958  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
959  {
960  if ( QgsLayerTree::isGroup( child ) )
961  {
962  QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
963  if ( childGroup->customProperty( "embedded" ).toInt() )
964  {
965  // make sure to convert the path from relative to absolute
966  QString projectPath = readPath( childGroup->customProperty( "embedded_project" ).toString() );
967  childGroup->setCustomProperty( "embedded_project", projectPath );
968 
969  QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( "embedded-invisible-layers" ).toStringList() );
970  if ( newGroup )
971  {
972  QList<QgsLayerTreeNode*> clonedChildren;
973  Q_FOREACH ( QgsLayerTreeNode *newGroupChild, newGroup->children() )
974  clonedChildren << newGroupChild->clone();
975  delete newGroup;
976 
977  childGroup->insertChildNodes( 0, clonedChildren );
978  }
979  }
980  else
981  {
982  loadEmbeddedNodes( childGroup );
983  }
984  }
985  else if ( QgsLayerTree::isLayer( child ) )
986  {
987  if ( child->customProperty( "embedded" ).toInt() )
988  {
989  QList<QDomNode> brokenNodes;
991  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), child->customProperty( "embedded_project" ).toString(), brokenNodes, vectorLayerList );
992  }
993  }
994 
995  }
996 }
997 
998 
999 bool QgsProject::read( QDomNode &layerNode )
1000 {
1001  QList<QDomNode> brokenNodes;
1003  return addLayer( layerNode.toElement(), brokenNodes, vectorLayerList );
1004 } // QgsProject::read( QDomNode &layerNode )
1005 
1006 
1007 
1009 {
1010  imp_->file.setFileName( file.filePath() );
1011 
1012  return write();
1013 } // QgsProject::write( QFileInfo const &file )
1014 
1015 
1017 {
1018  clearError();
1019 
1020  // if we have problems creating or otherwise writing to the project file,
1021  // let's find out up front before we go through all the hand-waving
1022  // necessary to create all the Dom objects
1023  QFileInfo myFileInfo( imp_->file );
1024  if ( myFileInfo.exists() && !myFileInfo.isWritable() )
1025  {
1026  setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
1027  .arg( imp_->file.fileName() ) );
1028  return false;
1029  }
1030 
1031  QDomImplementation DomImplementation;
1032  DomImplementation.setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
1033 
1034  QDomDocumentType documentType =
1035  DomImplementation.createDocumentType( "qgis", "http://mrcc.com/qgis.dtd",
1036  "SYSTEM" );
1037  QScopedPointer<QDomDocument> doc( new QDomDocument( documentType ) );
1038 
1039  QDomElement qgisNode = doc->createElement( "qgis" );
1040  qgisNode.setAttribute( "projectname", title() );
1041  qgisNode.setAttribute( "version", QString( "%1" ).arg( QGis::QGIS_VERSION ) );
1042 
1043  doc->appendChild( qgisNode );
1044 
1045  // title
1046  QDomElement titleNode = doc->createElement( "title" );
1047  qgisNode.appendChild( titleNode );
1048 
1049  QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
1050  titleNode.appendChild( titleText );
1051 
1052  // write layer tree - make sure it is without embedded subgroups
1053  QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
1055  QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ) ); // convert absolute paths to relative paths if required
1056  clonedRoot->writeXML( qgisNode );
1057  delete clonedRoot;
1058 
1059  // let map canvas and legend write their information
1060  emit writeProject( *doc );
1061 
1062  // within top level node save list of layers
1064 
1065  // Iterate over layers in zOrder
1066  // Call writeXML() on each
1067  QDomElement projectLayersNode = doc->createElement( "projectlayers" );
1068 
1070  while ( li != layers.end() )
1071  {
1072  QgsMapLayer *ml = li.value();
1073 
1074  if ( ml )
1075  {
1076  QString externalProjectFile = layerIsEmbedded( ml->id() );
1077  QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
1078  if ( emIt == mEmbeddedLayers.constEnd() )
1079  {
1080  // general layer metadata
1081  QDomElement maplayerElem = doc->createElement( "maplayer" );
1082 
1083  ml->writeLayerXML( maplayerElem, *doc );
1084 
1085  emit writeMapLayer( ml, maplayerElem, *doc );
1086 
1087  projectLayersNode.appendChild( maplayerElem );
1088  }
1089  else
1090  {
1091  // layer defined in an external project file
1092  // only save embedded layer if not managed by a legend group
1093  if ( emIt.value().second )
1094  {
1095  QDomElement mapLayerElem = doc->createElement( "maplayer" );
1096  mapLayerElem.setAttribute( "embedded", 1 );
1097  mapLayerElem.setAttribute( "project", writePath( emIt.value().first ) );
1098  mapLayerElem.setAttribute( "id", ml->id() );
1099  projectLayersNode.appendChild( mapLayerElem );
1100  }
1101  }
1102  }
1103  li++;
1104  }
1105 
1106  qgisNode.appendChild( projectLayersNode );
1107 
1108  // now add the optional extra properties
1109 
1110  dump_( imp_->properties_ );
1111 
1112  QgsDebugMsg( QString( "there are %1 property scopes" ).arg( static_cast<int>( imp_->properties_.count() ) ) );
1113 
1114  if ( !imp_->properties_.isEmpty() ) // only worry about properties if we
1115  // actually have any properties
1116  {
1117  imp_->properties_.writeXML( "properties", qgisNode, *doc );
1118  }
1119 
1120  mVisibilityPresetCollection->writeXML( *doc );
1121 
1122  // now wrap it up and ship it to the project file
1123  doc->normalize(); // XXX I'm not entirely sure what this does
1124 
1125  // Create backup file
1126  if ( QFile::exists( fileName() ) )
1127  {
1128  QFile backupFile( fileName() + '~' );
1129  bool ok = true;
1130  ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
1131  ok &= imp_->file.open( QIODevice::ReadOnly );
1132 
1133  QByteArray ba;
1134  while ( ok && !imp_->file.atEnd() )
1135  {
1136  ba = imp_->file.read( 10240 );
1137  ok &= backupFile.write( ba ) == ba.size();
1138  }
1139 
1140  imp_->file.close();
1141  backupFile.close();
1142 
1143  if ( !ok )
1144  {
1145  setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
1146  return false;
1147  }
1148 
1149  QFileInfo fi( fileName() );
1150  struct utimbuf tb = { fi.lastRead().toTime_t(), fi.lastModified().toTime_t() };
1151  utime( backupFile.fileName().toUtf8().constData(), &tb );
1152  }
1153 
1154  if ( !imp_->file.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
1155  {
1156  imp_->file.close(); // even though we got an error, let's make
1157  // sure it's closed anyway
1158 
1159  setError( tr( "Unable to save to file %1" ).arg( imp_->file.fileName() ) );
1160  return false;
1161  }
1162 
1163  QTemporaryFile tempFile;
1164  bool ok = tempFile.open();
1165  if ( ok )
1166  {
1167  QTextStream projectFileStream( &tempFile );
1168  doc->save( projectFileStream, 2 ); // save as utf-8
1169  ok &= projectFileStream.pos() > -1;
1170 
1171  ok &= tempFile.seek( 0 );
1172 
1173  QByteArray ba;
1174  while ( ok && !tempFile.atEnd() )
1175  {
1176  ba = tempFile.read( 10240 );
1177  ok &= imp_->file.write( ba ) == ba.size();
1178  }
1179 
1180  ok &= imp_->file.error() == QFile::NoError;
1181 
1182  imp_->file.close();
1183  }
1184 
1185  tempFile.close();
1186 
1187  if ( !ok )
1188  {
1189  setError( tr( "Unable to save to file %1. Your project "
1190  "may be corrupted on disk. Try clearing some space on the volume and "
1191  "check file permissions before pressing save again." )
1192  .arg( imp_->file.fileName() ) );
1193  return false;
1194  }
1195 
1196  dirty( false ); // reset to pristine state
1197 
1198  emit projectSaved();
1199 
1200  return true;
1201 } // QgsProject::write
1202 
1203 
1204 
1206 {
1207  clear();
1208 
1209  dirty( true );
1210 } // QgsProject::clearProperties()
1211 
1212 
1213 
1214 bool
1215 QgsProject::writeEntry( QString const &scope, const QString &key, bool value )
1216 {
1217  dirty( true );
1218 
1219  return addKey_( scope, key, &imp_->properties_, value );
1220 } // QgsProject::writeEntry ( ..., bool value )
1221 
1222 
1223 bool
1224 QgsProject::writeEntry( QString const &scope, const QString &key,
1225  double value )
1226 {
1227  dirty( true );
1228 
1229  return addKey_( scope, key, &imp_->properties_, value );
1230 } // QgsProject::writeEntry ( ..., double value )
1231 
1232 
1233 bool
1234 QgsProject::writeEntry( QString const &scope, const QString &key, int value )
1235 {
1236  dirty( true );
1237 
1238  return addKey_( scope, key, &imp_->properties_, value );
1239 } // QgsProject::writeEntry ( ..., int value )
1240 
1241 
1242 bool
1243 QgsProject::writeEntry( QString const &scope, const QString &key,
1244  const QString &value )
1245 {
1246  dirty( true );
1247 
1248  return addKey_( scope, key, &imp_->properties_, value );
1249 } // QgsProject::writeEntry ( ..., const QString &value )
1250 
1251 
1252 bool
1253 QgsProject::writeEntry( QString const &scope, const QString &key,
1254  const QStringList &value )
1255 {
1256  dirty( true );
1257 
1258  return addKey_( scope, key, &imp_->properties_, value );
1259 } // QgsProject::writeEntry ( ..., const QStringList &value )
1260 
1263  const QString &key,
1264  const QStringList& def,
1265  bool *ok ) const
1266 {
1267  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1268 
1269  QVariant value;
1270 
1271  if ( property )
1272  {
1273  value = property->value();
1274 
1275  bool valid = QVariant::StringList == value.type();
1276  if ( ok )
1277  *ok = valid;
1278 
1279  if ( valid )
1280  {
1281  return value.toStringList();
1282  }
1283  }
1284 
1285  return def;
1286 } // QgsProject::readListEntry
1287 
1288 
1289 QString
1291  const QString &key,
1292  const QString &def,
1293  bool *ok ) const
1294 {
1295  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1296 
1297  QVariant value;
1298 
1299  if ( property )
1300  {
1301  value = property->value();
1302 
1303  bool valid = value.canConvert( QVariant::String );
1304  if ( ok )
1305  *ok = valid;
1306 
1307  if ( valid )
1308  return value.toString();
1309  }
1310 
1311  return def;
1312 } // QgsProject::readEntry
1313 
1314 
1315 int
1316 QgsProject::readNumEntry( QString const &scope, const QString &key, int def,
1317  bool *ok ) const
1318 {
1319  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1320 
1321  QVariant value;
1322 
1323  if ( property )
1324  {
1325  value = property->value();
1326  }
1327 
1328  bool valid = value.canConvert( QVariant::String );
1329 
1330  if ( ok )
1331  {
1332  *ok = valid;
1333  }
1334 
1335  if ( valid )
1336  {
1337  return value.toInt();
1338  }
1339 
1340  return def;
1341 } // QgsProject::readNumEntry
1342 
1343 
1344 double
1345 QgsProject::readDoubleEntry( QString const &scope, const QString &key,
1346  double def,
1347  bool *ok ) const
1348 {
1349  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1350  if ( property )
1351  {
1352  QVariant value = property->value();
1353 
1354  bool valid = value.canConvert( QVariant::Double );
1355  if ( ok )
1356  *ok = valid;
1357 
1358  if ( valid )
1359  return value.toDouble();
1360  }
1361 
1362  return def;
1363 } // QgsProject::readDoubleEntry
1364 
1365 
1366 bool
1367 QgsProject::readBoolEntry( QString const &scope, const QString &key, bool def,
1368  bool *ok ) const
1369 {
1370  QgsProperty *property = findKey_( scope, key, imp_->properties_ );
1371 
1372  if ( property )
1373  {
1374  QVariant value = property->value();
1375 
1376  bool valid = value.canConvert( QVariant::Bool );
1377  if ( ok )
1378  *ok = valid;
1379 
1380  if ( valid )
1381  return value.toBool();
1382  }
1383 
1384  return def;
1385 } // QgsProject::readBoolEntry
1386 
1387 
1388 bool QgsProject::removeEntry( QString const &scope, const QString &key )
1389 {
1390  removeKey_( scope, key, imp_->properties_ );
1391 
1392  dirty( true );
1393 
1394  return !findKey_( scope, key, imp_->properties_ );
1395 } // QgsProject::removeEntry
1396 
1397 
1398 
1399 QStringList QgsProject::entryList( QString const &scope, QString const &key ) const
1400 {
1401  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1402 
1403  QStringList entries;
1404 
1405  if ( foundProperty )
1406  {
1407  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1408 
1409  if ( propertyKey )
1410  { propertyKey->entryList( entries ); }
1411  }
1412 
1413  return entries;
1414 } // QgsProject::entryList
1415 
1416 
1417 QStringList QgsProject::subkeyList( QString const &scope, QString const &key ) const
1418 {
1419  QgsProperty *foundProperty = findKey_( scope, key, imp_->properties_ );
1420 
1421  QStringList entries;
1422 
1423  if ( foundProperty )
1424  {
1425  QgsPropertyKey *propertyKey = dynamic_cast<QgsPropertyKey*>( foundProperty );
1426 
1427  if ( propertyKey )
1428  { propertyKey->subkeyList( entries ); }
1429  }
1430 
1431  return entries;
1432 
1433 } // QgsProject::subkeyList
1434 
1435 
1436 
1438 {
1439  dump_( imp_->properties_ );
1440 } // QgsProject::dumpProperties
1441 
1442 
1443 // return the absolute path from a filename read from project file
1445 {
1446  if ( readBoolEntry( "Paths", "/Absolute", false ) )
1447  {
1448  return src;
1449  }
1450 
1451  // if this is a VSIFILE, remove the VSI prefix and append to final result
1452  QString vsiPrefix = qgsVsiPrefix( src );
1453  if ( ! vsiPrefix.isEmpty() )
1454  {
1455  // unfortunately qgsVsiPrefix returns prefix also for files like "/x/y/z.gz"
1456  // so we need to check if we really have the prefix
1457  if ( src.startsWith( "/vsi", Qt::CaseInsensitive ) )
1458  src.remove( 0, vsiPrefix.size() );
1459  else
1460  vsiPrefix.clear();
1461  }
1462 
1463  // relative path should always start with ./ or ../
1464  if ( !src.startsWith( "./" ) && !src.startsWith( "../" ) )
1465  {
1466 #if defined(Q_OS_WIN)
1467  if ( src.startsWith( "\\\\" ) ||
1468  src.startsWith( "//" ) ||
1469  ( src[0].isLetter() && src[1] == ':' ) )
1470  {
1471  // UNC or absolute path
1472  return vsiPrefix + src;
1473  }
1474 #else
1475  if ( src[0] == '/' )
1476  {
1477  // absolute path
1478  return vsiPrefix + src;
1479  }
1480 #endif
1481 
1482  // so this one isn't absolute, but also doesn't start // with ./ or ../.
1483  // That means that it was saved with an earlier version of "relative path support",
1484  // where the source file had to exist and only the project directory was stripped
1485  // from the filename.
1486  QString home = homePath();
1487  if ( home.isNull() )
1488  return vsiPrefix + src;
1489 
1490  QFileInfo fi( home + '/' + src );
1491 
1492  if ( !fi.exists() )
1493  {
1494  return vsiPrefix + src;
1495  }
1496  else
1497  {
1498  return vsiPrefix + fi.canonicalFilePath();
1499  }
1500  }
1501 
1502  QString srcPath = src;
1503  QString projPath = fileName();
1504 
1505  if ( projPath.isEmpty() )
1506  {
1507  return vsiPrefix + src;
1508  }
1509 
1510 #if defined(Q_OS_WIN)
1511  srcPath.replace( '\\', '/' );
1512  projPath.replace( '\\', '/' );
1513 
1514  bool uncPath = projPath.startsWith( "//" );
1515 #endif
1516 
1517  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1518  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1519 
1520 #if defined(Q_OS_WIN)
1521  if ( uncPath )
1522  {
1523  projElems.insert( 0, "" );
1524  projElems.insert( 0, "" );
1525  }
1526 #endif
1527 
1528  // remove project file element
1529  projElems.removeLast();
1530 
1531  // append source path elements
1532  projElems << srcElems;
1533  projElems.removeAll( "." );
1534 
1535  // resolve ..
1536  int pos;
1537  while (( pos = projElems.indexOf( ".." ) ) > 0 )
1538  {
1539  // remove preceding element and ..
1540  projElems.removeAt( pos - 1 );
1541  projElems.removeAt( pos - 1 );
1542  }
1543 
1544 #if !defined(Q_OS_WIN)
1545  // make path absolute
1546  projElems.prepend( "" );
1547 #endif
1548 
1549  return vsiPrefix + projElems.join( "/" );
1550 }
1551 
1552 // return the absolute or relative path to write it to the project file
1553 QString QgsProject::writePath( const QString& src, const QString& relativeBasePath ) const
1554 {
1555  if ( readBoolEntry( "Paths", "/Absolute", false ) || src.isEmpty() )
1556  {
1557  return src;
1558  }
1559 
1560  QFileInfo srcFileInfo( src );
1561  QFileInfo projFileInfo( fileName() );
1562  QString srcPath = srcFileInfo.exists() ? srcFileInfo.canonicalFilePath() : src;
1563  QString projPath = projFileInfo.canonicalFilePath();
1564 
1565  if ( !relativeBasePath.isNull() )
1566  {
1567  projPath = relativeBasePath;
1568  }
1569 
1570  if ( projPath.isEmpty() )
1571  {
1572  return src;
1573  }
1574 
1575  // if this is a VSIFILE, remove the VSI prefix and append to final result
1576  QString vsiPrefix = qgsVsiPrefix( src );
1577  if ( ! vsiPrefix.isEmpty() )
1578  {
1579  srcPath.remove( 0, vsiPrefix.size() );
1580  }
1581 
1582 #if defined( Q_OS_WIN )
1583  const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
1584 
1585  srcPath.replace( '\\', '/' );
1586 
1587  if ( srcPath.startsWith( "//" ) )
1588  {
1589  // keep UNC prefix
1590  srcPath = "\\\\" + srcPath.mid( 2 );
1591  }
1592 
1593  projPath.replace( '\\', '/' );
1594  if ( projPath.startsWith( "//" ) )
1595  {
1596  // keep UNC prefix
1597  projPath = "\\\\" + projPath.mid( 2 );
1598  }
1599 #else
1600  const Qt::CaseSensitivity cs = Qt::CaseSensitive;
1601 #endif
1602 
1603  QStringList projElems = projPath.split( '/', QString::SkipEmptyParts );
1604  QStringList srcElems = srcPath.split( '/', QString::SkipEmptyParts );
1605 
1606  // remove project file element
1607  projElems.removeLast();
1608 
1609  projElems.removeAll( "." );
1610  srcElems.removeAll( "." );
1611 
1612  // remove common part
1613  int n = 0;
1614  while ( !srcElems.isEmpty() &&
1615  !projElems.isEmpty() &&
1616  srcElems[0].compare( projElems[0], cs ) == 0 )
1617  {
1618  srcElems.removeFirst();
1619  projElems.removeFirst();
1620  n++;
1621  }
1622 
1623  if ( n == 0 )
1624  {
1625  // no common parts; might not even by a file
1626  return src;
1627  }
1628 
1629  if ( !projElems.isEmpty() )
1630  {
1631  // go up to the common directory
1632  for ( int i = 0; i < projElems.size(); i++ )
1633  {
1634  srcElems.insert( 0, ".." );
1635  }
1636  }
1637  else
1638  {
1639  // let it start with . nevertheless,
1640  // so relative path always start with either ./ or ../
1641  srcElems.insert( 0, "." );
1642  }
1643 
1644  return vsiPrefix + srcElems.join( "/" );
1645 }
1646 
1647 void QgsProject::setError( const QString& errorMessage )
1648 {
1649  mErrorMessage = errorMessage;
1650 }
1651 
1653 {
1654  return mErrorMessage;
1655 }
1656 
1658 {
1659  setError( QString() );
1660 }
1661 
1663 {
1664  delete mBadLayerHandler;
1665  mBadLayerHandler = handler;
1666 }
1667 
1669 {
1670  QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
1671  if ( it == mEmbeddedLayers.constEnd() )
1672  {
1673  return QString();
1674  }
1675  return it.value().first;
1676 }
1677 
1678 bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
1679  QList< QPair< QgsVectorLayer*, QDomElement > > &vectorLayerList, bool saveFlag )
1680 {
1681  QgsDebugCall;
1682 
1683  static QString prevProjectFilePath;
1684  static QDateTime prevProjectFileTimestamp;
1685  static QDomDocument projectDocument;
1686 
1687  QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
1688 
1689  if ( projectFilePath != prevProjectFilePath || projectFileTimestamp != prevProjectFileTimestamp )
1690  {
1691  prevProjectFilePath.clear();
1692 
1693  QFile projectFile( projectFilePath );
1694  if ( !projectFile.open( QIODevice::ReadOnly ) )
1695  {
1696  return false;
1697  }
1698 
1699  if ( !projectDocument.setContent( &projectFile ) )
1700  {
1701  return false;
1702  }
1703 
1704  prevProjectFilePath = projectFilePath;
1705  prevProjectFileTimestamp = projectFileTimestamp;
1706  }
1707 
1708  // does project store pathes absolute or relative?
1709  bool useAbsolutePathes = true;
1710 
1711  QDomElement propertiesElem = projectDocument.documentElement().firstChildElement( "properties" );
1712  if ( !propertiesElem.isNull() )
1713  {
1714  QDomElement absElem = propertiesElem.firstChildElement( "Paths" ).firstChildElement( "Absolute" );
1715  if ( !absElem.isNull() )
1716  {
1717  useAbsolutePathes = absElem.text().compare( "true", Qt::CaseInsensitive ) == 0;
1718  }
1719  }
1720 
1721  QDomElement projectLayersElem = projectDocument.documentElement().firstChildElement( "projectlayers" );
1722  if ( projectLayersElem.isNull() )
1723  {
1724  return false;
1725  }
1726 
1727  QDomNodeList mapLayerNodes = projectLayersElem.elementsByTagName( "maplayer" );
1728  for ( int i = 0; i < mapLayerNodes.size(); ++i )
1729  {
1730  // get layer id
1731  QDomElement mapLayerElem = mapLayerNodes.at( i ).toElement();
1732  QString id = mapLayerElem.firstChildElement( "id" ).text();
1733  if ( id == layerId )
1734  {
1735  // layer can be embedded only once
1736  if ( mapLayerElem.attribute( "embedded" ) == "1" )
1737  {
1738  return false;
1739  }
1740 
1741  mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
1742 
1743  // change datasource path from relative to absolute if necessary
1744  // see also QgsMapLayer::readLayerXML
1745  if ( !useAbsolutePathes )
1746  {
1747  QString provider( mapLayerElem.firstChildElement( "provider" ).text() );
1748  QDomElement dsElem( mapLayerElem.firstChildElement( "datasource" ) );
1749  QString datasource( dsElem.text() );
1750  if ( provider == "spatialite" )
1751  {
1752  QgsDataSourceURI uri( datasource );
1753  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + uri.database() );
1754  if ( absoluteDs.exists() )
1755  {
1756  uri.setDatabase( absoluteDs.absoluteFilePath() );
1757  datasource = uri.uri();
1758  }
1759  }
1760  else if ( provider == "ogr" )
1761  {
1762  QStringList theURIParts( datasource.split( '|' ) );
1763  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1764  if ( absoluteDs.exists() )
1765  {
1766  theURIParts[0] = absoluteDs.absoluteFilePath();
1767  datasource = theURIParts.join( "|" );
1768  }
1769  }
1770  else if ( provider == "gpx" )
1771  {
1772  QStringList theURIParts( datasource.split( '?' ) );
1773  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + theURIParts[0] );
1774  if ( absoluteDs.exists() )
1775  {
1776  theURIParts[0] = absoluteDs.absoluteFilePath();
1777  datasource = theURIParts.join( "?" );
1778  }
1779  }
1780  else if ( provider == "delimitedtext" )
1781  {
1782  QUrl urlSource( QUrl::fromEncoded( datasource.toAscii() ) );
1783 
1784  if ( !datasource.startsWith( "file:" ) )
1785  {
1786  QUrl file( QUrl::fromLocalFile( datasource.left( datasource.indexOf( '?' ) ) ) );
1787  urlSource.setScheme( "file" );
1788  urlSource.setPath( file.path() );
1789  }
1790 
1791  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + urlSource.toLocalFile() );
1792  if ( absoluteDs.exists() )
1793  {
1794  QUrl urlDest = QUrl::fromLocalFile( absoluteDs.absoluteFilePath() );
1795  urlDest.setQueryItems( urlSource.queryItems() );
1796  datasource = QString::fromAscii( urlDest.toEncoded() );
1797  }
1798  }
1799  else
1800  {
1801  QFileInfo absoluteDs( QFileInfo( projectFilePath ).absolutePath() + '/' + datasource );
1802  if ( absoluteDs.exists() )
1803  {
1804  datasource = absoluteDs.absoluteFilePath();
1805  }
1806  }
1807 
1808  dsElem.removeChild( dsElem.childNodes().at( 0 ) );
1809  dsElem.appendChild( projectDocument.createTextNode( datasource ) );
1810  }
1811 
1812  if ( addLayer( mapLayerElem, brokenNodes, vectorLayerList ) )
1813  {
1814  return true;
1815  }
1816  else
1817  {
1818  mEmbeddedLayers.remove( layerId );
1819  return false;
1820  }
1821  }
1822  }
1823 
1824  return false;
1825 }
1826 
1827 
1828 QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers )
1829 {
1830  // open project file, get layer ids in group, add the layers
1831  QFile projectFile( projectFilePath );
1832  if ( !projectFile.open( QIODevice::ReadOnly ) )
1833  {
1834  return nullptr;
1835  }
1836 
1837  QDomDocument projectDocument;
1838  if ( !projectDocument.setContent( &projectFile ) )
1839  {
1840  return nullptr;
1841  }
1842 
1843  // store identify disabled layers of the embedded project
1844  QSet<QString> embeddedIdentifyDisabledLayers;
1845  QDomElement disabledLayersElem = projectDocument.documentElement().firstChildElement( "properties" ).firstChildElement( "Identify" ).firstChildElement( "disabledLayers" );
1846  if ( !disabledLayersElem.isNull() )
1847  {
1848  QDomNodeList valueList = disabledLayersElem.elementsByTagName( "value" );
1849  for ( int i = 0; i < valueList.size(); ++i )
1850  {
1851  embeddedIdentifyDisabledLayers.insert( valueList.at( i ).toElement().text() );
1852  }
1853  }
1854 
1856 
1857  QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( "layer-tree-group" );
1858  if ( !layerTreeElem.isNull() )
1859  {
1860  root->readChildrenFromXML( layerTreeElem );
1861  }
1862  else
1863  {
1864  QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( "legend" ) );
1865  }
1866 
1867  QgsLayerTreeGroup *group = root->findGroup( groupName );
1868  if ( !group || group->customProperty( "embedded" ).toBool() )
1869  {
1870  // embedded groups cannot be embedded again
1871  delete root;
1872  return nullptr;
1873  }
1874 
1875  // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
1876  QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
1877  delete root;
1878  root = nullptr;
1879 
1880  newGroup->setCustomProperty( "embedded", 1 );
1881  newGroup->setCustomProperty( "embedded_project", projectFilePath );
1882 
1883  // set "embedded" to all children + load embedded layers
1884  mLayerTreeRegistryBridge->setEnabled( false );
1885  initializeEmbeddedSubtree( projectFilePath, newGroup );
1886  mLayerTreeRegistryBridge->setEnabled( true );
1887 
1888  // consider the layers might be identify disabled in its project
1889  Q_FOREACH ( const QString& layerId, newGroup->findLayerIds() )
1890  {
1891  if ( embeddedIdentifyDisabledLayers.contains( layerId ) )
1892  {
1893  QStringList thisProjectIdentifyDisabledLayers = QgsProject::instance()->readListEntry( "Identify", "/disabledLayers" );
1894  thisProjectIdentifyDisabledLayers.append( layerId );
1895  QgsProject::instance()->writeEntry( "Identify", "/disabledLayers", thisProjectIdentifyDisabledLayers );
1896  }
1897 
1898  QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
1899  if ( layer )
1900  {
1901  layer->setVisible( invisibleLayers.contains( layerId ) ? Qt::Unchecked : Qt::Checked );
1902  }
1903  }
1904 
1905  return newGroup;
1906 }
1907 
1909 {
1910  Q_FOREACH ( QgsLayerTreeNode *child, group->children() )
1911  {
1912  // all nodes in the subtree will have "embedded" custom property set
1913  child->setCustomProperty( "embedded", 1 );
1914 
1915  if ( QgsLayerTree::isGroup( child ) )
1916  {
1917  initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ) );
1918  }
1919  else if ( QgsLayerTree::isLayer( child ) )
1920  {
1921  // load the layer into our project
1922  QList<QDomNode> brokenNodes;
1924  createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, vectorLayerList, false );
1925  }
1926  }
1927 }
1928 
1929 void QgsProject::setSnapSettingsForLayer( const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection )
1930 {
1931  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1932  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1933  int idx = layerIdList.indexOf( layerId );
1934  if ( idx != -1 )
1935  {
1936  layerIdList.removeAt( idx );
1937  enabledList.removeAt( idx );
1938  snapTypeList.removeAt( idx );
1939  toleranceUnitList.removeAt( idx );
1940  toleranceList.removeAt( idx );
1941  avoidIntersectionList.removeOne( layerId );
1942  }
1943 
1944  layerIdList.append( layerId );
1945 
1946  // enabled
1947  enabledList.append( enabled ? "enabled" : "disabled" );
1948 
1949  // snap type
1950  QString typeString;
1951  if ( type == QgsSnapper::SnapToSegment )
1952  {
1953  typeString = "to_segment";
1954  }
1955  else if ( type == QgsSnapper::SnapToVertexAndSegment )
1956  {
1957  typeString = "to_vertex_and_segment";
1958  }
1959  else
1960  {
1961  typeString = "to_vertex";
1962  }
1963  snapTypeList.append( typeString );
1964 
1965  // units
1966  toleranceUnitList.append( QString::number( unit ) );
1967 
1968  // tolerance
1969  toleranceList.append( QString::number( tolerance ) );
1970 
1971  // avoid intersection
1972  if ( avoidIntersection )
1973  {
1974  avoidIntersectionList.append( layerId );
1975  }
1976 
1977  writeEntry( "Digitizing", "/LayerSnappingList", layerIdList );
1978  writeEntry( "Digitizing", "/LayerSnappingEnabledList", enabledList );
1979  writeEntry( "Digitizing", "/LayerSnappingToleranceList", toleranceList );
1980  writeEntry( "Digitizing", "/LayerSnappingToleranceUnitList", toleranceUnitList );
1981  writeEntry( "Digitizing", "/LayerSnapToList", snapTypeList );
1982  writeEntry( "Digitizing", "/AvoidIntersectionsList", avoidIntersectionList );
1983  emit snapSettingsChanged();
1984 }
1985 
1986 bool QgsProject::snapSettingsForLayer( const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance,
1987  bool &avoidIntersection ) const
1988 {
1989  QStringList layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList;
1990  snapSettings( layerIdList, enabledList, snapTypeList, toleranceUnitList, toleranceList, avoidIntersectionList );
1991  int idx = layerIdList.indexOf( layerId );
1992  if ( idx == -1 )
1993  {
1994  return false;
1995  }
1996 
1997  // make sure all lists are long enough
1998  int minListEntries = idx + 1;
1999  if ( layerIdList.size() < minListEntries || enabledList.size() < minListEntries || snapTypeList.size() < minListEntries ||
2000  toleranceUnitList.size() < minListEntries || toleranceList.size() < minListEntries )
2001  {
2002  return false;
2003  }
2004 
2005  // enabled
2006  enabled = enabledList.at( idx ) == "enabled";
2007 
2008  // snap type
2009  QString snapType = snapTypeList.at( idx );
2010  if ( snapType == "to_segment" )
2011  {
2013  }
2014  else if ( snapType == "to_vertex_and_segment" )
2015  {
2017  }
2018  else // to vertex
2019  {
2020  type = QgsSnapper::SnapToVertex;
2021  }
2022 
2023  // units
2024  if ( toleranceUnitList.at( idx ) == "1" )
2025  {
2026  units = QgsTolerance::Pixels;
2027  }
2028  else if ( toleranceUnitList.at( idx ) == "2" )
2029  {
2031  }
2032  else
2033  {
2034  units = QgsTolerance::LayerUnits;
2035  }
2036 
2037  // tolerance
2038  tolerance = toleranceList.at( idx ).toDouble();
2039 
2040  // avoid intersection
2041  avoidIntersection = ( avoidIntersectionList.indexOf( layerId ) != -1 );
2042 
2043  return true;
2044 }
2045 
2046 void QgsProject::snapSettings( QStringList &layerIdList, QStringList &enabledList, QStringList &snapTypeList, QStringList &toleranceUnitList, QStringList &toleranceList,
2047  QStringList &avoidIntersectionList ) const
2048 {
2049  layerIdList = readListEntry( "Digitizing", "/LayerSnappingList" );
2050  enabledList = readListEntry( "Digitizing", "/LayerSnappingEnabledList" );
2051  toleranceList = readListEntry( "Digitizing", "/LayerSnappingToleranceList" );
2052  toleranceUnitList = readListEntry( "Digitizing", "/LayerSnappingToleranceUnitList" );
2053  snapTypeList = readListEntry( "Digitizing", "/LayerSnapToList" );
2054  avoidIntersectionList = readListEntry( "Digitizing", "/AvoidIntersectionsList" );
2055 }
2056 
2058 {
2059  QgsProject::instance()->writeEntry( "Digitizing", "/TopologicalEditing", ( enabled ? 1 : 0 ) );
2060  emit snapSettingsChanged();
2061 }
2062 
2064 {
2065  return ( QgsProject::instance()->readNumEntry( "Digitizing", "/TopologicalEditing", 0 ) > 0 );
2066 }
2067 
2069 {
2070  QString distanceUnitString = QgsProject::instance()->readEntry( "Measurement", "/DistanceUnits", QString() );
2071  if ( !distanceUnitString.isEmpty() )
2072  return QgsUnitTypes::decodeDistanceUnit( distanceUnitString );
2073 
2074  //fallback to QGIS default measurement unit
2075  QSettings s;
2076  bool ok = false;
2077  QGis::UnitType type = QgsUnitTypes::decodeDistanceUnit( s.value( "/qgis/measure/displayunits" ).toString(), &ok );
2078  return ok ? type : QGis::Meters;
2079 }
2080 
2082 {
2083  QString areaUnitString = QgsProject::instance()->readEntry( "Measurement", "/AreaUnits", QString() );
2084  if ( !areaUnitString.isEmpty() )
2085  return QgsUnitTypes::decodeAreaUnit( areaUnitString );
2086 
2087  //fallback to QGIS default area unit
2088  QSettings s;
2089  bool ok = false;
2090  QgsUnitTypes::AreaUnit type = QgsUnitTypes::decodeAreaUnit( s.value( "/qgis/measure/areaunits" ).toString(), &ok );
2091  return ok ? type : QgsUnitTypes::SquareMeters;
2092 }
2093 
2095 {
2096  // just ignore any bad layers
2097 }
2098 
2100 {
2101  QFileInfo pfi( fileName() );
2102  if ( !pfi.exists() )
2103  return QString::null;
2104 
2105  return pfi.canonicalPath();
2106 }
2107 
2109 {
2110  return mRelationManager;
2111 }
2112 
2114 {
2115  return mRootGroup;
2116 }
2117 
2119 {
2120  return mVisibilityPresetCollection.data();
2121 }
QObject * child(const char *objName, const char *inheritsClass, bool recursiveSearch) const
static const char * QGIS_VERSION
Definition: qgis.h:42
bool canConvert(Type t) const
Layer tree group node serves as a container for layers and further groups.
bool topologicalEditing() const
Convenience function to query topological editing status.
static QgsProperty * findKey_(QString const &scope, QString const &key, QgsPropertyKey &rootProperty)
return the property that matches the given key sequence, if any
Definition: qgsproject.cpp:113
QString fromAscii(const char *str, int size)
QDomNodeList elementsByTagName(const QString &tagname) const
QString database() const
Returns the database.
virtual void handleBadLayers(const QList< QDomNode > &layers, const QDomDocument &projectDom) override
Base class for all map layer types.
Definition: qgsmaplayer.h:49
virtual bool seek(qint64 pos)
static void removeInvalidLayers(QgsLayerTreeGroup *group)
Remove layer nodes that refer to invalid layers.
QString writePath(const QString &filename, const QString &relativeBasePath=QString::null) const
Prepare a filename to save it to the project file.
const QList< QgsVectorJoinInfo > vectorJoins() const
QString title() const
Returns title.
Definition: qgsproject.cpp:398
QDomNode item(int index) const
void readChildrenFromXML(QDomElement &element)
Read children from XML and append them to the group.
void loadingLayer(const QString &)
QgsPropertyKey properties_
Definition: qgsproject.cpp:325
QgsPropertyKey * addKey(const QString &keyName)
add the given property key
QGis::UnitType distanceUnits() const
Convenience function to query default distance measurement units for project.
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
qint64 pos() const
QDomNode appendChild(const QDomNode &newChild)
bool snapSettingsForLayer(const QString &layerId, bool &enabled, QgsSnapper::SnappingType &type, QgsTolerance::UnitType &units, double &tolerance, bool &avoidIntersection) const
Convenience function to query snap settings of a layer.
QDateTime lastRead() const
void push_back(const T &value)
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &theMapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
void entryList(QStringList &entries) const
return keys that do not contain other keys
QVariant customProperty(const QString &key, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QString attribute(const QString &name, const QString &defValue) const
static void _getProperties(QDomDocument const &doc, QgsPropertyKey &project_properties)
Restore any optional properties found in "doc" to "properties".
Definition: qgsproject.cpp:505
QgsPropertyValue * setValue(const QString &name, const QVariant &value)
Set the value associated with this key.
QString data() const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
static 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.
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
QList< QPair< QString, QString > > queryItems() const
Class used to work with layer dependencies stored in a XML project or layer definition file...
void removeFirst()
static QGis::UnitType decodeDistanceUnit(const QString &string, bool *ok=0)
Decodes a distance unit from a string.
void setDatabase(const QString &database)
Set database.
const_iterator constBegin() const
const T & at(int i) const
QString fileName() const
int size() const
void removeAt(int i)
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
void setFileName(const QString &name)
Every project has an associated file that contains its XML.
Definition: qgsproject.cpp:422
UnitType
Type of unit of tolerance value from settings.
Definition: qgstolerance.h:33
void setFileName(const QString &name)
void push_front(const T &value)
T value() const
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Key value accessors.
const_iterator constFind(const Key &key) const
QString layerIsEmbedded(const QString &id) const
Returns project file path if layer is embedded from other project file.
void setSnapSettingsForLayer(const QString &layerId, bool enabled, QgsSnapper::SnappingType type, QgsTolerance::UnitType unit, double tolerance, bool avoidIntersection)
Convenience function to set snap settings per layer.
QDomElement documentElement() const
QString join(const QString &separator) const
void setError(const QString &errorMessage)
Set error message from read/write operation.
QString homePath() const
Return project&#39;s home path.
void clear()
Clear project properties when a new project is started.
Definition: qgsproject.cpp:340
bool exists() const
const_iterator insert(const T &value)
QString & remove(int position, int n)
static void _getTitle(QDomDocument const &doc, QString &title)
Get the project title.
Definition: qgsproject.cpp:550
void setDirty(bool b)
Set project as dirty (modified).
Definition: qgsproject.cpp:415
void projectSaved()
emitted when the project file has been written and closed
QDomNodeList childNodes() const
bool writeLayerXML(QDomElement &layerElement, QDomDocument &document, const QString &relativeBasePath=QString::null)
Stores state in Dom node.
void loadEmbeddedNodes(QgsLayerTreeGroup *group)
Definition: qgsproject.cpp:956
QString tr(const char *sourceText, const char *disambiguation, int n)
QString readPath(QString filename) const
Turn filename read from the project file to an absolute path.
virtual void writeXML(QDomElement &parentElement)=0
Write layer tree to XML.
QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group. No type checking is done - use isGroup() to find out whether this operation is ...
Definition: qgslayertree.h:46
int size() const
static QgsProperty * addKey_(QString const &scope, QString const &key, QgsPropertyKey *rootProperty, const QVariant &value)
Add the given key and value.
Definition: qgsproject.cpp:189
bool isNull() const
QString name() const
every key has a name
void clear()
QString filePath() const
QDomElement toElement() const
void setPath(const QString &path)
SnappingType
Snap to vertex, to segment or both.
Definition: qgssnapper.h:66
QgsUnitTypes::AreaUnit areaUnits() const
Convenience function to query default area measurement units for project.
const char * name() const
bool writeEntry(const QString &scope, const QString &key, bool value)
QString canonicalFilePath() const
QString readEntry(const QString &scope, const QString &key, const QString &def=QString::null, bool *ok=nullptr) const
bool isText() const
int count() const
QString number(int n, int base)
int count(const T &value) const
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList, bool saveFlag=true)
Creates a maplayer instance defined in an arbitrary project file.
bool read()
presuming that the caller has already reset the map canvas, map registry, and legend ...
Definition: qgsproject.cpp:813
static QgsProjectVersion _getVersion(QDomDocument const &doc)
Return the version string found in the given Dom document.
Definition: qgsproject.cpp:589
void append(const T &value)
void removeKey(const QString &keyName)
remove the given key
QString & insert(int position, QChar ch)
QStringList subkeyList(const QString &scope, const QString &key) const
Return keys with keys – do not return keys that contain only values.
void initializeEmbeddedSubtree(const QString &projectFilePath, QgsLayerTreeGroup *group)
QVariant property(const char *name) const
const_iterator constEnd() const
QString canonicalPath() const
void pop_front()
QString text() const
int toInt(bool *ok) const
Pixels unit of tolerance.
Definition: qgstolerance.h:40
virtual void clearKeys()
delete any sub-nodes
void setAttribute(const QString &name, const QString &value)
A class to describe the version of a project.
bool isEmpty() const
QDomNodeList elementsByTagName(const QString &tagname) const
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
QString absoluteFilePath() const
bool isEmpty() const
int removeAll(const T &value)
void readProject(const QDomDocument &)
emitted when project is being read
Listens to the updates in map layer registry and does changes in layer tree.
const char * constData() const
QgsPropertyKey node.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
void setScheme(const QString &scheme)
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
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
QString fileName() const
Returns file name.
Definition: qgsproject.cpp:431
This class is a base class for nodes in a layer tree.
QString id() const
Get this layer&#39;s unique ID, this ID is used to access this layer from map layer registry.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key.
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group)
Reads and writes project states.
Definition: qgsproject.h:70
qint64 read(char *data, qint64 maxSize)
void setVisible(Qt::CheckState visible)
T & front()
T & first()
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted, when a layer is being saved.
QString error() const
Return error message from previous read/write.
QString name() const
Get group&#39;s name.
iterator end()
QDateTime lastModified() const
QList< QgsLayerTreeNode * > children()
Get list of children of the node. Children are owned by the parent.
bool isLayer(QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
Definition: qgslayertree.h:40
bool isValid()
Return the status of the layer.
An Abstract Base Class for QGIS project property hierarchies.
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
bool hasChildNodes() const
QgsProperty * find(QString &propertyName)
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position. The nodes must not have a parent yet. The nodes will be owned by this group.
bool write()
QString toLocalFile() const
QDomText createTextNode(const QString &value)
#define QgsDebugCall
Definition: qgslogger.h:32
iterator end()
const T value(const Key &key) const
bool exists() const
iterator find(const Key &key)
Class for storing the component parts of a PostgreSQL/RDBMS datasource URI.
void subkeyList(QStringList &entries) const
return keys that contain other keys
QDomNode namedItem(const QString &name) const
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers)
Create layer group instance defined in an arbitrary project file.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
bool contains(QChar ch, Qt::CaseSensitivity cs) const
void clearError()
Clear error message.
virtual void close()
bool isDirty() const
the dirty flag is true if the project has been modified since the last write()
Definition: qgsproject.cpp:404
bool contains(const T &value) const
uint toTime_t() const
bool isNull() const
Layer unit value.
Definition: qgstolerance.h:38
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
QString & replace(int position, int n, QChar after)
bool addLayer(const QDomElement &layerElem, QList< QDomNode > &brokenNodes, QList< QPair< QgsVectorLayer *, QDomElement > > &vectorLayerList)
Creates layer and adds it to maplayer registry.
Definition: qgsproject.cpp:742
QVariant value(const QString &key, const QVariant &defaultValue) const
void setInvalidDataPolicy(InvalidDataPolicy policy)
void writeProject(QDomDocument &)
emitted when project is being written
void oldProjectVersionWarning(const QString &)
emitted when an old project file is read.
QDomNode firstChild() const
static QgsMapLayerRegistry * instance()
Returns the instance pointer, creating the object on the first call.
QString mid(int position, int n) const
QStringList toStringList() const
Map (project) units.
Definition: qgstolerance.h:42
QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer. No type checking is done - use isLayer() to find out whether this operation is ...
Definition: qgslayertree.h:52
void insert(int i, const T &value)
This class manages a set of relations between layers.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes. Searches recursively the whole sub-tree.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name. Searches recursively the whole sub-tree.
virtual bool atEnd() const
static QgsProject * instance()
access to canonical QgsProject instance
Definition: qgsproject.cpp:381
QDomElement firstChildElement(const QString &tagName) const
QString qgsVsiPrefix(const QString &path)
Definition: qgis.cpp:293
static void removeKey_(QString const &scope, QString const &key, QgsPropertyKey &rootProperty)
Definition: qgsproject.cpp:260
void setTitle(const QString &title)
Set project title.
Definition: qgsproject.cpp:390
StandardButton critical(QWidget *parent, const QString &title, const QString &text, QFlags< QMessageBox::StandardButton > buttons, StandardButton defaultButton)
void removeLast()
const QMap< QString, QgsMapLayer * > & mapLayers()
Retrieve the mapLayers collection (mainly intended for use by projection)
bool isWritable() const
bool toBool() const
UnitType
Map units that qgis supports.
Definition: qgis.h:155
bool readXML(QDomNode &keyNode) override
restores property hierarchy to given Dom node
QStringList entryList(const QString &scope, const QString &key) const
Return keys with values – do not return keys that contain other keys.
QgsLayerTreeLayer * findLayer(const QString &layerId) const
Find layer node representing the map layer specified by its ID. Searches recursively the whole sub-tr...
void setQueryItems(const QList< QPair< QString, QString > > &query)
qint64 write(const char *data, qint64 maxSize)
bool readLayerXML(const QDomElement &layerElement)
Sets state from Dom document.
Container class that allows storage of visibility presets consisting of visible map layers and layer ...
int indexOf(const QRegExp &rx, int from) const
void prepend(const T &value)
double toDouble(bool *ok) const
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
void dumpProperties() const
Dump out current project properties to stderr.
void clearProperties()
removes all project properties
static QgsPluginLayerRegistry * instance()
Means of accessing canonical single instance.
static QStringList makeKeyTokens_(QString const &scope, QString const &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:69
int size() const
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted, after the basic initialization of a layer from the project file is done. ...
QFileInfo fileInfo() const
Returns QFileInfo object for the project&#39;s associated file.
Definition: qgsproject.cpp:436
QgsLayerTreeGroup * layerTreeRoot() const
Return pointer to the root (invisible) node of the project&#39;s layer tree.
QDomText toText() const
Type type() const
Default bad layer handler which ignores any missing layers.
Definition: qgsproject.h:443
int size() const
QString uri(bool expandAuthConfig=true) const
return complete uri
virtual bool isKey() const =0
Returns true if is a QgsPropertyKey.
QDomDocumentType createDocumentType(const QString &qName, const QString &publicId, const QString &systemId)
QgsVisibilityPresetCollection * visibilityPresetCollection()
Returns pointer to the project&#39;s visibility preset collection.
Represents a vector layer which manages a vector based data sets.
int compare(const QString &other) const
QgsRelationManager * relationManager() const
bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
Definition: qgslayertree.h:34
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
bool removeOne(const T &value)
virtual bool isValue() const =0
Returns true if is a QgsPropertyValue.
static void dump_(QgsPropertyKey const &topQgsPropertyKey)
Definition: qgsproject.cpp:466
AreaUnit
Units of area.
Definition: qgsunittypes.h:49
iterator begin()
QgsPluginLayer * createLayer(const QString &typeName, const QString &uri=QString())
Return new layer if corresponding plugin has been found, else return NULL.
QUrl fromEncoded(const QByteArray &input)
QByteArray toEncoded(QFlags< QUrl::FormattingOption > options) const
void clear()
Clear the project.
Definition: qgsproject.cpp:441
Interface for classes that handle missing layer files when reading project file.
Definition: qgsproject.h:434
QUrl fromLocalFile(const QString &localFile)
Layer tree node points to a map layer.
void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for the node.
QDomNode at(int index) const
bool setContent(const QByteArray &data, bool namespaceProcessing, QString *errorMsg, int *errorLine, int *errorColumn)
void dump(int tabs=0) const override
Dumps out the keys and values.
const T value(const Key &key) const
void snapSettingsChanged()
void dirty(bool b)
Definition: qgsproject.cpp:410
QByteArray toUtf8() const