QGIS API Documentation 3.34.0-Prizren (ffbdd678812)
Loading...
Searching...
No Matches
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 "qgsmaplayerfactory.h"
28#include "qgspluginlayer.h"
31#include "qgssnappingconfig.h"
32#include "qgspathresolver.h"
33#include "qgsprojectstorage.h"
35#include "qgsprojectversion.h"
36#include "qgsrasterlayer.h"
37#include "qgsreadwritecontext.h"
38#include "qgsrelationmanager.h"
42#include "qgslayerdefinition.h"
43#include "qgsunittypes.h"
44#include "qgstransaction.h"
45#include "qgstransactiongroup.h"
48#include "qgsmeshlayer.h"
49#include "qgslayoutmanager.h"
50#include "qgsbookmarkmanager.h"
51#include "qgsmaplayerstore.h"
52#include "qgsziputils.h"
53#include "qgsauxiliarystorage.h"
54#include "qgssymbollayerutils.h"
55#include "qgsapplication.h"
61#include "qgsvectortilelayer.h"
62#include "qgstiledscenelayer.h"
63#include "qgsruntimeprofiler.h"
64#include "qgsannotationlayer.h"
65#include "qgspointcloudlayer.h"
67#include "qgsgrouplayer.h"
68#include "qgsmapviewsmanager.h"
72#include "qgsthreadingutils.h"
73#include "qgssensormanager.h"
74#include "qgsproviderregistry.h"
77
78#include <algorithm>
79#include <QApplication>
80#include <QFileInfo>
81#include <QDomNode>
82#include <QObject>
83#include <QTextStream>
84#include <QTemporaryFile>
85#include <QDir>
86#include <QUrl>
87#include <QStandardPaths>
88#include <QUuid>
89#include <QRegularExpression>
90#include <QThreadPool>
91
92#ifdef _MSC_VER
93#include <sys/utime.h>
94#else
95#include <utime.h>
96#endif
97
98// canonical project instance
99QgsProject *QgsProject::sProject = nullptr;
100
109QStringList makeKeyTokens_( const QString &scope, const QString &key )
110{
111 QStringList keyTokens = QStringList( scope );
112#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
113 keyTokens += key.split( '/', QString::SkipEmptyParts );
114#else
115 keyTokens += key.split( '/', Qt::SkipEmptyParts );
116#endif
117
118 // be sure to include the canonical root node
119 keyTokens.push_front( QStringLiteral( "properties" ) );
120
121 //check validy of keys since an invalid xml name will will be dropped upon saving the xml file. If not valid, we print a message to the console.
122 for ( int i = 0; i < keyTokens.size(); ++i )
123 {
124 const QString keyToken = keyTokens.at( i );
125
126 //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
127 //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
128 const thread_local QRegularExpression sInvalidRegexp = QRegularExpression( QStringLiteral( "([^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}\\-\\.0-9\\x{B7}\\x{0300}-\\x{036F}\\x{203F}-\\x{2040}]|^[^:A-Z_a-z\\x{C0}-\\x{D6}\\x{D8}-\\x{F6}\\x{F8}-\\x{2FF}\\x{370}-\\x{37D}\\x{37F}-\\x{1FFF}\\x{200C}-\\x{200D}\\x{2070}-\\x{218F}\\x{2C00}-\\x{2FEF}\\x{3001}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFFD}])" ) );
129 if ( keyToken.contains( sInvalidRegexp ) )
130 {
131 const QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
133 }
134 }
135
136 return keyTokens;
137}
138
139
140
150QgsProjectProperty *findKey_( const QString &scope,
151 const QString &key,
152 QgsProjectPropertyKey &rootProperty )
153{
154 QgsProjectPropertyKey *currentProperty = &rootProperty;
155 QgsProjectProperty *nextProperty; // link to next property down hierarchy
156
157 QStringList keySequence = makeKeyTokens_( scope, key );
158
159 while ( !keySequence.isEmpty() )
160 {
161 // if the current head of the sequence list matches the property name,
162 // then traverse down the property hierarchy
163 if ( keySequence.first() == currentProperty->name() )
164 {
165 // remove front key since we're traversing down a level
166 keySequence.pop_front();
167
168 if ( 1 == keySequence.count() )
169 {
170 // if we have only one key name left, then return the key found
171 return currentProperty->find( keySequence.front() );
172 }
173 else if ( keySequence.isEmpty() )
174 {
175 // if we're out of keys then the current property is the one we
176 // want; i.e., we're in the rate case of being at the top-most
177 // property node
178 return currentProperty;
179 }
180 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
181 {
182 if ( nextProperty->isKey() )
183 {
184 currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
185 }
186 else if ( nextProperty->isValue() && 1 == keySequence.count() )
187 {
188 // it may be that this may be one of several property value
189 // nodes keyed by QDict string; if this is the last remaining
190 // key token and the next property is a value node, then
191 // that's the situation, so return the currentProperty
192 return currentProperty;
193 }
194 else
195 {
196 // QgsProjectPropertyValue not Key, so return null
197 return nullptr;
198 }
199 }
200 else
201 {
202 // if the next key down isn't found
203 // then the overall key sequence doesn't exist
204 return nullptr;
205 }
206 }
207 else
208 {
209 return nullptr;
210 }
211 }
212
213 return nullptr;
214}
215
216
217
227QgsProjectProperty *addKey_( const QString &scope,
228 const QString &key,
229 QgsProjectPropertyKey *rootProperty,
230 const QVariant &value,
231 bool &propertiesModified )
232{
233 QStringList keySequence = makeKeyTokens_( scope, key );
234
235 // cursor through property key/value hierarchy
236 QgsProjectPropertyKey *currentProperty = rootProperty;
237 QgsProjectProperty *nextProperty; // link to next property down hierarchy
238 QgsProjectPropertyKey *newPropertyKey = nullptr;
239
240 propertiesModified = false;
241 while ( ! keySequence.isEmpty() )
242 {
243 // if the current head of the sequence list matches the property name,
244 // then traverse down the property hierarchy
245 if ( keySequence.first() == currentProperty->name() )
246 {
247 // remove front key since we're traversing down a level
248 keySequence.pop_front();
249
250 // if key sequence has one last element, then we use that as the
251 // name to store the value
252 if ( 1 == keySequence.count() )
253 {
254 QgsProjectProperty *property = currentProperty->find( keySequence.front() );
255 if ( !property || property->value() != value )
256 {
257 currentProperty->setValue( keySequence.front(), value );
258 propertiesModified = true;
259 }
260
261 return currentProperty;
262 }
263 // we're at the top element if popping the keySequence element
264 // will leave it empty; in that case, just add the key
265 else if ( keySequence.isEmpty() )
266 {
267 if ( currentProperty->value() != value )
268 {
269 currentProperty->setValue( value );
270 propertiesModified = true;
271 }
272
273 return currentProperty;
274 }
275 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
276 {
277 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
278
279 if ( currentProperty )
280 {
281 continue;
282 }
283 else // QgsProjectPropertyValue not Key, so return null
284 {
285 return nullptr;
286 }
287 }
288 else // the next subkey doesn't exist, so add it
289 {
290 if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
291 {
292 currentProperty = newPropertyKey;
293 }
294 continue;
295 }
296 }
297 else
298 {
299 return nullptr;
300 }
301 }
302
303 return nullptr;
304}
305
313void removeKey_( const QString &scope,
314 const QString &key,
315 QgsProjectPropertyKey &rootProperty )
316{
317 QgsProjectPropertyKey *currentProperty = &rootProperty;
318
319 QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
320 QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
321
322 QStringList keySequence = makeKeyTokens_( scope, key );
323
324 while ( ! keySequence.isEmpty() )
325 {
326 // if the current head of the sequence list matches the property name,
327 // then traverse down the property hierarchy
328 if ( keySequence.first() == currentProperty->name() )
329 {
330 // remove front key since we're traversing down a level
331 keySequence.pop_front();
332
333 // if we have only one key name left, then try to remove the key
334 // with that name
335 if ( 1 == keySequence.count() )
336 {
337 currentProperty->removeKey( keySequence.front() );
338 }
339 // if we're out of keys then the current property is the one we
340 // want to remove, but we can't delete it directly; we need to
341 // delete it from the parent property key container
342 else if ( keySequence.isEmpty() )
343 {
344 previousQgsPropertyKey->removeKey( currentProperty->name() );
345 }
346 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
347 {
348 previousQgsPropertyKey = currentProperty;
349 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
350
351 if ( currentProperty )
352 {
353 continue;
354 }
355 else // QgsProjectPropertyValue not Key, so return null
356 {
357 return;
358 }
359 }
360 else // if the next key down isn't found
361 {
362 // then the overall key sequence doesn't exist
363 return;
364 }
365 }
366 else
367 {
368 return;
369 }
370 }
371}
372
373QgsProject::QgsProject( QObject *parent, Qgis::ProjectCapabilities capabilities )
374 : QObject( parent )
375 , mCapabilities( capabilities )
376 , mLayerStore( new QgsMapLayerStore( this ) )
377 , mBadLayerHandler( new QgsProjectBadLayerHandler() )
378 , mSnappingConfig( this )
379 , mRelationManager( new QgsRelationManager( this ) )
380 , mAnnotationManager( new QgsAnnotationManager( this ) )
381 , mLayoutManager( new QgsLayoutManager( this ) )
382 , m3DViewsManager( new QgsMapViewsManager( this ) )
383 , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
384 , mSensorManager( new QgsSensorManager( this ) )
385 , mViewSettings( new QgsProjectViewSettings( this ) )
386 , mStyleSettings( new QgsProjectStyleSettings( this ) )
387 , mTimeSettings( new QgsProjectTimeSettings( this ) )
388 , mElevationProperties( new QgsProjectElevationProperties( this ) )
389 , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
390 , mGpsSettings( new QgsProjectGpsSettings( this ) )
391 , mRootGroup( new QgsLayerTree )
392 , mLabelingEngineSettings( new QgsLabelingEngineSettings )
393 , mArchive( new QgsArchive() )
394 , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
395{
396 mProperties.setName( QStringLiteral( "properties" ) );
397
398 mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
399 mMainAnnotationLayer->setParent( this );
400
401 clear();
402
403 // bind the layer tree to the map layer registry.
404 // whenever layers are added to or removed from the registry,
405 // layer tree will be updated
406 mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
407 connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
408 connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
409 connect( this, qOverload< const QList<QgsMapLayer *> & >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
410
411 // proxy map layer store signals to this
412 connect( mLayerStore.get(), qOverload<const QStringList &>( &QgsMapLayerStore::layersWillBeRemoved ),
413 this, [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
414 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ),
415 this, [ = ]( const QList<QgsMapLayer *> &layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
416 connect( mLayerStore.get(), qOverload< const QString & >( &QgsMapLayerStore::layerWillBeRemoved ),
417 this, [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
418 connect( mLayerStore.get(), qOverload< QgsMapLayer * >( &QgsMapLayerStore::layerWillBeRemoved ),
419 this, [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
420 connect( mLayerStore.get(), qOverload<const QStringList & >( &QgsMapLayerStore::layersRemoved ), this,
421 [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
422 connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this,
423 [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
424 connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this,
425 [ = ]() { mProjectScope.reset(); emit removeAll(); } );
426 connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this,
427 [ = ]( const QList< QgsMapLayer * > &layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
428 connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this,
429 [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
430
432 {
434 }
435
436 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ), this,
437 [ = ]( const QList<QgsMapLayer *> &layers )
438 {
439 for ( const auto &layer : layers )
440 {
441 disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
442 }
443 }
444 );
445 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersAdded ), this,
446 [ = ]( const QList<QgsMapLayer *> &layers )
447 {
448 for ( const auto &layer : layers )
449 {
450 connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
451 }
452 }
453 );
454
458
459 mStyleSettings->combinedStyleModel()->addDefaultStyle();
460}
461
462
464{
465 mIsBeingDeleted = true;
466
467 clear();
468 releaseHandlesToProjectArchive();
469 delete mBadLayerHandler;
470 delete mRelationManager;
471 delete mLayerTreeRegistryBridge;
472 delete mRootGroup;
473 if ( this == sProject )
474 {
475 sProject = nullptr;
476 }
477}
478
480{
481 sProject = project;
482}
483
484
486{
487 if ( !sProject )
488 {
489 sProject = new QgsProject;
490
492 }
493 return sProject;
494}
495
496void QgsProject::setTitle( const QString &title )
497{
499
500 if ( title == mMetadata.title() )
501 return;
502
503 mMetadata.setTitle( title );
504 mProjectScope.reset();
505 emit metadataChanged();
506
507 setDirty( true );
508}
509
510QString QgsProject::title() const
511{
512 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
514
515 return mMetadata.title();
516}
517
518void QgsProject::setFlags( Qgis::ProjectFlags flags )
519{
521
522 const bool oldEvaluateDefaultValues = mFlags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
523 const bool newEvaluateDefaultValues = flags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
524 if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
525 {
526 const QMap<QString, QgsMapLayer *> layers = mapLayers();
527 for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
528 {
529 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
530 if ( vl->dataProvider() )
531 vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, newEvaluateDefaultValues );
532 }
533 }
534
535 const bool oldTrustLayerMetadata = mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
536 const bool newTrustLayerMetadata = flags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
537 if ( oldTrustLayerMetadata != newTrustLayerMetadata )
538 {
539 const QMap<QString, QgsMapLayer *> layers = mapLayers();
540 for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
541 {
542 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
543 {
544 vl->setReadExtentFromXml( newTrustLayerMetadata );
545 }
546 }
547 }
548
549 if ( mFlags != flags )
550 {
551 mFlags = flags;
552 setDirty( true );
553 }
554}
555
556void QgsProject::setFlag( Qgis::ProjectFlag flag, bool enabled )
557{
559
560 Qgis::ProjectFlags newFlags = mFlags;
561 if ( enabled )
562 newFlags |= flag;
563 else
564 newFlags &= ~( static_cast< int >( flag ) );
565 setFlags( newFlags );
566}
567
568QString QgsProject::saveUser() const
569{
571
572 return mSaveUser;
573}
574
576{
578
579 return mSaveUserFull;
580}
581
583{
585
586 return mSaveDateTime;
587}
588
595
597{
599
600 return mDirty;
601}
602
603void QgsProject::setDirty( const bool dirty )
604{
606
607 if ( dirty && mDirtyBlockCount > 0 )
608 return;
609
610 if ( dirty )
611 emit dirtySet();
612
613 if ( mDirty == dirty )
614 return;
615
616 mDirty = dirty;
617 emit isDirtyChanged( mDirty );
618}
619
620void QgsProject::setPresetHomePath( const QString &path )
621{
623
624 if ( path == mHomePath )
625 return;
626
627 mHomePath = path;
628 mCachedHomePath.clear();
629 mProjectScope.reset();
630
631 emit homePathChanged();
632
633 setDirty( true );
634}
635
636void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
637{
639
640 const QList<QgsAttributeEditorElement *> elements = parent->children();
641
642 for ( QgsAttributeEditorElement *element : elements )
643 {
644 if ( element->type() == Qgis::AttributeEditorType::Container )
645 {
646 QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
647
648 translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
649
650 if ( !container->children().empty() )
651 registerTranslatableContainers( translationContext, container, layerId );
652 }
653 }
654}
655
657{
659
660 //register layers
661 const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
662
663 for ( const QgsLayerTreeLayer *layer : layers )
664 {
665 translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
666
667 QgsMapLayer *mapLayer = layer->layer();
669 {
670 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
671
672 //register aliases and fields
673 const QgsFields fields = vlayer->fields();
674 for ( const QgsField &field : fields )
675 {
676 QString fieldName;
677 if ( field.alias().isEmpty() )
678 fieldName = field.name();
679 else
680 fieldName = field.alias();
681
682 translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
683
684 if ( field.editorWidgetSetup().type() == QLatin1String( "ValueRelation" ) )
685 {
686 translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
687 }
688 }
689
690 //register formcontainers
691 registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
692
693 }
694 }
695
696 //register layergroups
697 const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
698 for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
699 {
700 translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
701 }
702
703 //register relations
704 const QList<QgsRelation> &relations = mRelationManager->relations().values();
705 for ( const QgsRelation &relation : relations )
706 {
707 translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
708 }
709}
710
712{
714
715 mDataDefinedServerProperties = properties;
716}
717
719{
721
722 return mDataDefinedServerProperties;
723}
724
726{
728
729 switch ( mTransactionMode )
730 {
733 {
734 if ( ! vectorLayer )
735 return false;
736 return vectorLayer->startEditing();
737 }
738
740 return mEditBufferGroup.startEditing();
741 }
742
743 return false;
744}
745
746bool QgsProject::commitChanges( QStringList &commitErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
747{
749
750 switch ( mTransactionMode )
751 {
754 {
755 if ( ! vectorLayer )
756 {
757 commitErrors.append( tr( "Trying to commit changes without a layer specified. This only works if the transaction mode is buffered" ) );
758 return false;
759 }
760 bool success = vectorLayer->commitChanges( stopEditing );
761 commitErrors = vectorLayer->commitErrors();
762 return success;
763 }
764
766 return mEditBufferGroup.commitChanges( commitErrors, stopEditing );
767 }
768
769 return false;
770}
771
772bool QgsProject::rollBack( QStringList &rollbackErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
773{
775
776 switch ( mTransactionMode )
777 {
780 {
781 if ( ! vectorLayer )
782 {
783 rollbackErrors.append( tr( "Trying to roll back changes without a layer specified. This only works if the transaction mode is buffered" ) );
784 return false;
785 }
786 bool success = vectorLayer->rollBack( stopEditing );
787 rollbackErrors = vectorLayer->commitErrors();
788 return success;
789 }
790
792 return mEditBufferGroup.rollBack( rollbackErrors, stopEditing );
793 }
794
795 return false;
796}
797
798void QgsProject::setFileName( const QString &name )
799{
801
802 if ( name == mFile.fileName() )
803 return;
804
805 const QString oldHomePath = homePath();
806
807 mFile.setFileName( name );
808 mCachedHomePath.clear();
809 mProjectScope.reset();
810
811 emit fileNameChanged();
812
813 const QString newHomePath = homePath();
814 if ( newHomePath != oldHomePath )
815 emit homePathChanged();
816
817 setDirty( true );
818}
819
820QString QgsProject::fileName() const
821{
822 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
824
825 return mFile.fileName();
826}
827
828void QgsProject::setOriginalPath( const QString &path )
829{
831
832 mOriginalPath = path;
833}
834
836{
838
839 return mOriginalPath;
840}
841
842QFileInfo QgsProject::fileInfo() const
843{
845
846 return QFileInfo( mFile );
847}
848
850{
851 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
853
855}
856
858{
860
861 if ( QgsProjectStorage *storage = projectStorage() )
862 {
864 storage->readProjectStorageMetadata( mFile.fileName(), metadata );
865 return metadata.lastModified;
866 }
867 else
868 {
869 return QFileInfo( mFile.fileName() ).lastModified();
870 }
871}
872
874{
876
877 if ( projectStorage() )
878 return QString();
879
880 if ( mFile.fileName().isEmpty() )
881 return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
882
883 return QFileInfo( mFile.fileName() ).absolutePath();
884}
885
887{
888 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
890
891 if ( projectStorage() )
892 return QString();
893
894 if ( mFile.fileName().isEmpty() )
895 return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
896
897 return QFileInfo( mFile.fileName() ).absoluteFilePath();
898}
899
900QString QgsProject::baseName() const
901{
902 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
904
905 if ( QgsProjectStorage *storage = projectStorage() )
906 {
908 storage->readProjectStorageMetadata( mFile.fileName(), metadata );
909 return metadata.name;
910 }
911 else
912 {
913 return QFileInfo( mFile.fileName() ).completeBaseName();
914 }
915}
916
918{
920
921 const bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
923}
924
926{
928
929 switch ( type )
930 {
932 writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
933 break;
935 writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
936 break;
937 }
938}
939
941{
942 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
944
945 return mCrs;
946}
947
948void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
949{
951
952 if ( crs != mCrs )
953 {
954 mCrs = crs;
955 writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
956 mProjectScope.reset();
957
958 // if annotation layer doesn't have a crs (i.e. in a newly created project), it should
959 // initially inherit the project CRS
960 if ( !mMainAnnotationLayer->crs().isValid() || mMainAnnotationLayer->isEmpty() )
961 mMainAnnotationLayer->setCrs( crs );
962
963 setDirty( true );
964 emit crsChanged();
965 }
966
967 if ( adjustEllipsoid )
969}
970
972{
973 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
975
976 if ( !crs().isValid() )
977 return geoNone();
978
979 return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), geoNone() );
980}
981
982void QgsProject::setEllipsoid( const QString &ellipsoid )
983{
985
986 if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
987 return;
988
989 mProjectScope.reset();
990 writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
992}
993
995{
996 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
998
999 return mTransformContext;
1000}
1001
1003{
1005
1006 if ( context == mTransformContext )
1007 return;
1008
1009 mTransformContext = context;
1010 mProjectScope.reset();
1011
1012 mMainAnnotationLayer->setTransformContext( context );
1013 for ( auto &layer : mLayerStore.get()->mapLayers() )
1014 {
1015 layer->setTransformContext( context );
1016 }
1018}
1019
1021{
1023
1024 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
1025
1026 emit aboutToBeCleared();
1027
1028 mProjectScope.reset();
1029 mFile.setFileName( QString() );
1030 mProperties.clearKeys();
1031 mSaveUser.clear();
1032 mSaveUserFull.clear();
1033 mSaveDateTime = QDateTime();
1034 mSaveVersion = QgsProjectVersion();
1035 mHomePath.clear();
1036 mCachedHomePath.clear();
1037 mTransactionMode = Qgis::TransactionMode::Disabled;
1038 mFlags = Qgis::ProjectFlags();
1039 mDirty = false;
1040 mCustomVariables.clear();
1042 mMetadata = QgsProjectMetadata();
1043 mElevationShadingRenderer = QgsElevationShadingRenderer();
1044 if ( !mSettings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
1045 {
1046 mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
1048 }
1049 emit metadataChanged();
1050
1052 context.readSettings();
1053 setTransformContext( context );
1054
1055 //fallback to QGIS default measurement unit
1056 bool ok = false;
1057 const Qgis::DistanceUnit distanceUnit = QgsUnitTypes::decodeDistanceUnit( mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
1058 setDistanceUnits( ok ? distanceUnit : Qgis::DistanceUnit::Meters );
1059 ok = false;
1060 const Qgis::AreaUnit areaUnits = QgsUnitTypes::decodeAreaUnit( mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
1062
1063 mEmbeddedLayers.clear();
1064 mRelationManager->clear();
1065 mAnnotationManager->clear();
1066 mLayoutManager->clear();
1067 m3DViewsManager->clear();
1068 mBookmarkManager->clear();
1069 mSensorManager->clear();
1070 mViewSettings->reset();
1071 mTimeSettings->reset();
1072 mElevationProperties->reset();
1073 mDisplaySettings->reset();
1074 mGpsSettings->reset();
1075 mSnappingConfig.reset();
1076 mAvoidIntersectionsMode = Qgis::AvoidIntersectionsMode::AllowIntersections;
1079
1080 mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1082
1083 mLabelingEngineSettings->clear();
1084
1085 // must happen BEFORE archive reset, because we need to release the hold on any files which
1086 // exists within the archive. Otherwise the archive can't be removed.
1087 releaseHandlesToProjectArchive();
1088
1089 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
1090 mArchive.reset( new QgsArchive() );
1091
1092 // must happen AFTER archive reset, as it will populate a new style database within the new archive
1093 mStyleSettings->reset();
1094
1096
1097 if ( !mIsBeingDeleted )
1098 {
1099 // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
1100 emit projectColorsChanged();
1101 }
1102
1103 // reset some default project properties
1104 // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
1105 writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
1106 writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
1107
1108 const bool defaultRelativePaths = mSettings.value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true ).toBool();
1110
1111 int red = mSettings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
1112 int green = mSettings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
1113 int blue = mSettings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
1114 setBackgroundColor( QColor( red, green, blue ) );
1115
1116 red = mSettings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
1117 green = mSettings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
1118 blue = mSettings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
1119 const int alpha = mSettings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
1120 setSelectionColor( QColor( red, green, blue, alpha ) );
1121
1122 mSnappingConfig.clearIndividualLayerSettings();
1123
1125 mRootGroup->clear();
1126 if ( mMainAnnotationLayer )
1127 mMainAnnotationLayer->reset();
1128
1129 snapSingleBlocker.release();
1130
1131 if ( !mBlockSnappingUpdates )
1132 emit snappingConfigChanged( mSnappingConfig );
1133
1134 setDirty( false );
1135 emit homePathChanged();
1136 emit cleared();
1137}
1138
1139// basically a debugging tool to dump property list values
1140void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
1141{
1142 QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
1143 topQgsPropertyKey.dump();
1144}
1145
1174void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
1175{
1176 const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1177
1178 if ( propertiesElem.isNull() ) // no properties found, so we're done
1179 {
1180 return;
1181 }
1182
1183 const QDomNodeList scopes = propertiesElem.childNodes();
1184
1185 if ( propertiesElem.firstChild().isNull() )
1186 {
1187 QgsDebugError( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
1188 return;
1189 }
1190
1191 if ( ! project_properties.readXml( propertiesElem ) )
1192 {
1193 QgsDebugError( QStringLiteral( "Project_properties.readXml() failed" ) );
1194 }
1195}
1196
1203QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
1204{
1205 QgsPropertyCollection ddServerProperties;
1206 // Read data defined server properties
1207 const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral( "dataDefinedServerProperties" ) );
1208 if ( !ddElem.isNull() )
1209 {
1210 if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1211 {
1212 QgsDebugError( QStringLiteral( "dataDefinedServerProperties.readXml() failed" ) );
1213 }
1214 }
1215 return ddServerProperties;
1216}
1217
1222static void _getTitle( const QDomDocument &doc, QString &title )
1223{
1224 const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral( "title" ) );
1225
1226 title.clear(); // by default the title will be empty
1227
1228 if ( titleNode.isNull() )
1229 {
1230 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1231 return;
1232 }
1233
1234 if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1235 {
1236 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1237 return;
1238 }
1239
1240 const QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1241
1242 if ( !titleTextNode.isText() )
1243 {
1244 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1245 return;
1246 }
1247
1248 const QDomText titleText = titleTextNode.toText();
1249
1250 title = titleText.data();
1251
1252}
1253
1254static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1255{
1256 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1257
1258 if ( !nl.count() )
1259 {
1260 QgsDebugError( QStringLiteral( "unable to find qgis element" ) );
1261 return;
1262 }
1263
1264 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1265
1266 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1267 lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
1268 lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
1269 lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral( "saveDateTime" ), QString() ), Qt::ISODate );
1270}
1271
1272QgsProjectVersion getVersion( const QDomDocument &doc )
1273{
1274 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1275
1276 if ( !nl.count() )
1277 {
1278 QgsDebugError( QStringLiteral( " unable to find qgis element in project file" ) );
1279 return QgsProjectVersion( 0, 0, 0, QString() );
1280 }
1281
1282 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1283
1284 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1285 QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
1286 return projectVersion;
1287}
1288
1290{
1292
1293 return mSnappingConfig;
1294}
1295
1297{
1299
1300 if ( mSnappingConfig == snappingConfig )
1301 return;
1302
1303 mSnappingConfig = snappingConfig;
1304 setDirty( true );
1305 emit snappingConfigChanged( mSnappingConfig );
1306}
1307
1309{
1311
1312 if ( mAvoidIntersectionsMode == mode )
1313 return;
1314
1315 mAvoidIntersectionsMode = mode;
1317}
1318
1319static QgsMapLayer::ReadFlags projectFlagsToLayerReadFlags( Qgis::ProjectReadFlags projectReadFlags, Qgis::ProjectFlags projectFlags )
1320{
1321 QgsMapLayer::ReadFlags layerFlags = QgsMapLayer::ReadFlags();
1322 if ( projectReadFlags & Qgis::ProjectReadFlag::DontResolveLayers )
1324 // Propagate trust layer metadata flag
1325 if ( ( projectFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( projectReadFlags & Qgis::ProjectReadFlag::TrustLayerMetadata ) )
1327 // Propagate open layers in read-only mode
1328 if ( ( projectReadFlags & Qgis::ProjectReadFlag::ForceReadOnlyLayers ) )
1329 layerFlags |= QgsMapLayer::FlagForceReadOnly;
1330
1331 return layerFlags;
1332}
1333
1335{
1336 QString layerId;
1337 QString provider;
1338 QString dataSource;
1340 QgsDataProvider::ReadFlags flags;
1341 QDomElement layerElement;
1342};
1343
1344void QgsProject::preloadProviders( const QVector<QDomNode> &parallelLayerNodes,
1345 const QgsReadWriteContext &context,
1346 QMap<QString, QgsDataProvider *> &loadedProviders,
1347 QgsMapLayer::ReadFlags layerReadFlags,
1348 int totalProviderCount )
1349{
1350 int i = 0;
1351 QEventLoop loop;
1352
1353 QMap<QString, LayerToLoad> layersToLoad;
1354
1355 for ( const QDomNode &node : parallelLayerNodes )
1356 {
1357 LayerToLoad layerToLoad;
1358
1359 const QDomElement layerElement = node.toElement();
1360 layerToLoad.layerElement = layerElement;
1361 layerToLoad.layerId = layerElement.namedItem( QStringLiteral( "id" ) ).toElement().text();
1362 layerToLoad.provider = layerElement.namedItem( QStringLiteral( "provider" ) ).toElement().text();
1363 layerToLoad.dataSource = layerElement.namedItem( QStringLiteral( "datasource" ) ).toElement().text();
1364
1365 layerToLoad.dataSource = QgsProviderRegistry::instance()->relativeToAbsoluteUri( layerToLoad.provider, layerToLoad.dataSource, context );
1366
1367 layerToLoad.options = QgsDataProvider::ProviderOptions( {context.transformContext()} );
1368 layerToLoad.flags = QgsMapLayer::providerReadFlags( node, layerReadFlags );
1369
1370 // Requesting credential from worker thread could lead to deadlocks because the main thread is waiting for worker thread to fininsh
1371 layerToLoad.flags.setFlag( QgsDataProvider::SkipCredentialsRequest, true );
1372 layerToLoad.flags.setFlag( QgsDataProvider::ParallelThreadLoading, true );
1373
1374 layersToLoad.insert( layerToLoad.layerId, layerToLoad );
1375 }
1376
1377 while ( !layersToLoad.isEmpty() )
1378 {
1379 const QList<LayerToLoad> layersToAttemptInParallel = layersToLoad.values();
1380 QString layerToAttemptInMainThread;
1381
1382 QHash<QString, QgsRunnableProviderCreator *> runnables;
1383 QThreadPool threadPool;
1384 threadPool.setMaxThreadCount( QgsSettingsRegistryCore::settingsLayerParallelLoadingMaxCount->value() );
1385
1386 for ( const LayerToLoad &lay : layersToAttemptInParallel )
1387 {
1388 QgsRunnableProviderCreator *run = new QgsRunnableProviderCreator( lay.layerId, lay.provider, lay.dataSource, lay.options, lay.flags );
1389 runnables.insert( lay.layerId, run );
1390
1391 QObject::connect( run, &QgsRunnableProviderCreator::providerCreated, run, [&]( bool isValid, const QString & layId )
1392 {
1393 if ( isValid )
1394 {
1395 layersToLoad.remove( layId );
1396 i++;
1397 QgsRunnableProviderCreator *finishedRun = runnables.value( layId, nullptr );
1398 Q_ASSERT( finishedRun );
1399
1400 std::unique_ptr<QgsDataProvider> provider( finishedRun->dataProvider() );
1401 Q_ASSERT( provider && provider->isValid() );
1402
1403 loadedProviders.insert( layId, provider.release() );
1404 emit layerLoaded( i, totalProviderCount );
1405 }
1406 else
1407 {
1408 if ( layerToAttemptInMainThread.isEmpty() )
1409 layerToAttemptInMainThread = layId;
1410 threadPool.clear(); //we have to stop all loading provider to try this layer in main thread and maybe have credentials
1411 }
1412
1413 if ( i == parallelLayerNodes.count() || !isValid )
1414 loop.quit();
1415 } );
1416 threadPool.start( run );
1417 }
1418 loop.exec();
1419
1420 threadPool.waitForDone(); // to be sure all threads are finished
1421
1422 qDeleteAll( runnables );
1423
1424 // We try with the first layer returned invalid but this time in the main thread to maybe have credentials and continue with others not loaded in parallel
1425 auto it = layersToLoad.find( layerToAttemptInMainThread );
1426 if ( it != layersToLoad.end() )
1427 {
1428 std::unique_ptr<QgsDataProvider> provider;
1429 QString layerId;
1430 {
1431 const LayerToLoad &lay = it.value();
1432 QgsDataProvider::ReadFlags providerFlags = lay.flags;
1433 providerFlags.setFlag( QgsDataProvider::SkipCredentialsRequest, false );
1434 providerFlags.setFlag( QgsDataProvider::ParallelThreadLoading, false );
1435 QgsScopedRuntimeProfile profile( "Create data providers/" + lay.layerId, QStringLiteral( "projectload" ) );
1436 provider.reset( QgsProviderRegistry::instance()->createProvider( lay.provider, lay.dataSource, lay.options, providerFlags ) );
1437 i++;
1438 if ( provider && provider->isValid() )
1439 {
1440 emit layerLoaded( i, totalProviderCount );
1441 }
1442 layerId = lay.layerId;
1443 layersToLoad.erase( it );
1444 // can't access "lay" anymore -- it's now been freed
1445 }
1446 loadedProviders.insert( layerId, provider.release() );
1447 }
1448
1449 // if there still are some not loaded providers or some invalid in parallel thread we start again
1450 }
1451
1452}
1453
1454void QgsProject::releaseHandlesToProjectArchive()
1455{
1456 mStyleSettings->removeProjectStyle();
1457}
1458
1459bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, Qgis::ProjectReadFlags flags )
1460{
1462
1463 // Layer order is set by the restoring the legend settings from project file.
1464 // This is done on the 'readProject( ... )' signal
1465
1466 QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
1467
1468 // process the map layer nodes
1469
1470 if ( layerElement.isNull() ) // if we have no layers to process, bail
1471 {
1472 return true; // Decided to return "true" since it's
1473 // possible for there to be a project with no
1474 // layers; but also, more imporantly, this
1475 // would cause the tests/qgsproject to fail
1476 // since the test suite doesn't currently
1477 // support test layers
1478 }
1479
1480 bool returnStatus = true;
1481 int numLayers = 0;
1482
1483 while ( ! layerElement.isNull() )
1484 {
1485 numLayers++;
1486 layerElement = layerElement.nextSiblingElement( QStringLiteral( "maplayer" ) );
1487 }
1488
1489 // order layers based on their dependencies
1490 QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), QStringLiteral( "projectload" ) );
1491 const QgsLayerDefinition::DependencySorter depSorter( doc );
1492 if ( depSorter.hasCycle() )
1493 return false;
1494
1495 // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1496 if ( depSorter.hasMissingDependency() )
1497 returnStatus = false;
1498
1499 emit layerLoaded( 0, numLayers );
1500
1501 const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1502 const int totalLayerCount = sortedLayerNodes.count();
1503
1504 QVector<QDomNode> parallelLoading;
1505 QMap<QString, QgsDataProvider *> loadedProviders;
1506
1508 {
1509 profile.switchTask( tr( "Load providers in parallel" ) );
1510 for ( const QDomNode &node : sortedLayerNodes )
1511 {
1512 const QDomElement element = node.toElement();
1513 if ( element.attribute( QStringLiteral( "embedded" ) ) != QLatin1String( "1" ) )
1514 {
1515 const QString layerId = node.namedItem( QStringLiteral( "id" ) ).toElement().text();
1516 if ( !depSorter.isLayerDependent( layerId ) )
1517 {
1518 const QDomNode mnl = element.namedItem( QStringLiteral( "provider" ) );
1519 const QDomElement mne = mnl.toElement();
1520 const QString provider = mne.text();
1522 if ( meta && meta->providerCapabilities().testFlag( QgsProviderMetadata::ParallelCreateProvider ) )
1523 {
1524 parallelLoading.append( node );
1525 continue;
1526 }
1527 }
1528 }
1529 }
1530
1531 QgsReadWriteContext context;
1532 context.setPathResolver( pathResolver() );
1533 if ( !parallelLoading.isEmpty() )
1534 preloadProviders( parallelLoading, context, loadedProviders, projectFlagsToLayerReadFlags( flags, mFlags ), sortedLayerNodes.count() );
1535 }
1536
1537 int i = loadedProviders.count();
1538 for ( const QDomNode &node : std::as_const( sortedLayerNodes ) )
1539 {
1540 const QDomElement element = node.toElement();
1541 const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1542 if ( !name.isNull() )
1543 emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1544
1545 profile.switchTask( name );
1546 if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1547 {
1548 createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1549 }
1550 else
1551 {
1552 QgsReadWriteContext context;
1553 context.setPathResolver( pathResolver() );
1554 context.setProjectTranslator( this );
1556 QString layerId = element.namedItem( QStringLiteral( "id" ) ).toElement().text();
1557
1558 if ( !addLayer( element, brokenNodes, context, flags, loadedProviders.take( layerId ) ) )
1559 {
1560 returnStatus = false;
1561 }
1562 const auto messages = context.takeMessages();
1563 if ( !messages.isEmpty() )
1564 {
1565 emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1566 }
1567 }
1568 emit layerLoaded( i + 1, totalLayerCount );
1569 i++;
1570 }
1571
1572 return returnStatus;
1573}
1574
1575bool QgsProject::addLayer( const QDomElement &layerElem,
1576 QList<QDomNode> &brokenNodes,
1577 QgsReadWriteContext &context,
1578 Qgis::ProjectReadFlags flags,
1579 QgsDataProvider *provider )
1580{
1582
1583 const QString type = layerElem.attribute( QStringLiteral( "type" ) );
1584 QgsDebugMsgLevel( "Layer type is " + type, 4 );
1585 std::unique_ptr<QgsMapLayer> mapLayer;
1586
1587 QgsScopedRuntimeProfile profile( tr( "Create layer" ), QStringLiteral( "projectload" ) );
1588
1589 bool ok = false;
1590 const Qgis::LayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1591 if ( !ok )
1592 {
1593 QgsDebugError( QStringLiteral( "Unknown layer type \"%1\"" ).arg( type ) );
1594 return false;
1595 }
1596
1597 switch ( layerType )
1598 {
1600 mapLayer = std::make_unique<QgsVectorLayer>();
1601 break;
1602
1604 mapLayer = std::make_unique<QgsRasterLayer>();
1605 break;
1606
1608 mapLayer = std::make_unique<QgsMeshLayer>();
1609 break;
1610
1612 mapLayer = std::make_unique<QgsVectorTileLayer>();
1613 break;
1614
1616 mapLayer = std::make_unique<QgsPointCloudLayer>();
1617 break;
1618
1620 mapLayer = std::make_unique<QgsTiledSceneLayer>();
1621 break;
1622
1624 {
1625 const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1626 mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1627 break;
1628 }
1629
1631 {
1632 const QgsAnnotationLayer::LayerOptions options( mTransformContext );
1633 mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1634 break;
1635 }
1636
1638 {
1639 const QgsGroupLayer::LayerOptions options( mTransformContext );
1640 mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1641 break;
1642 }
1643 }
1644
1645 if ( !mapLayer )
1646 {
1647 QgsDebugError( QStringLiteral( "Unable to create layer" ) );
1648 return false;
1649 }
1650
1651 Q_CHECK_PTR( mapLayer ); // NOLINT
1652
1653 // This is tricky: to avoid a leak we need to check if the layer was already in the store
1654 // because if it was, the newly created layer will not be added to the store and it would leak.
1655 const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1656 Q_ASSERT( ! layerId.isEmpty() );
1657 const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1658
1659 // have the layer restore state that is stored in Dom node
1660 QgsMapLayer::ReadFlags layerFlags = projectFlagsToLayerReadFlags( flags, mFlags );
1661
1662 profile.switchTask( tr( "Load layer source" ) );
1663 const bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags, provider ) && mapLayer->isValid();
1664
1665 // apply specific settings to vector layer
1666 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1667 {
1669 if ( vl->dataProvider() )
1670 {
1672 vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, evaluateDefaultValues );
1673 }
1674 }
1675
1676 profile.switchTask( tr( "Add layer to project" ) );
1677 QList<QgsMapLayer *> newLayers;
1678 newLayers << mapLayer.get();
1679 if ( layerIsValid || flags & Qgis::ProjectReadFlag::DontResolveLayers )
1680 {
1681 emit readMapLayer( mapLayer.get(), layerElem );
1682 addMapLayers( newLayers );
1683 // Try to resolve references here (this is necessary to set up joined fields that will be possibly used by
1684 // virtual layers that point to this layer's joined field in their query otherwise they won't be valid ),
1685 // a second attempt to resolve references will be done after all layers are loaded
1686 // see https://github.com/qgis/QGIS/issues/46834
1687 if ( QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1688 {
1689 vLayer->joinBuffer()->resolveReferences( this );
1690 }
1691 }
1692 else
1693 {
1694 // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1695 addMapLayers( newLayers, false );
1696 newLayers.first();
1697 QgsDebugError( "Unable to load " + type + " layer" );
1698 brokenNodes.push_back( layerElem );
1699 }
1700
1701 const bool wasEditable = layerElem.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
1702 if ( wasEditable )
1703 {
1704 mapLayer->setCustomProperty( QStringLiteral( "_layer_was_editable" ), true );
1705 }
1706 else
1707 {
1708 mapLayer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
1709 }
1710
1711 // It should be safe to delete the layer now if layer was stored, because all the store
1712 // had to to was to reset the data source in case the validity changed.
1713 if ( ! layerWasStored )
1714 {
1715 mapLayer.release();
1716 }
1717
1718 return layerIsValid;
1719}
1720
1721bool QgsProject::read( const QString &filename, Qgis::ProjectReadFlags flags )
1722{
1724
1725 mFile.setFileName( filename );
1726 mCachedHomePath.clear();
1727 mProjectScope.reset();
1728
1729 return read( flags );
1730}
1731
1732bool QgsProject::read( Qgis::ProjectReadFlags flags )
1733{
1735
1736 const QString filename = mFile.fileName();
1737 bool returnValue;
1738
1739 if ( QgsProjectStorage *storage = projectStorage() )
1740 {
1741 QTemporaryFile inDevice;
1742 if ( !inDevice.open() )
1743 {
1744 setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1745 return false;
1746 }
1747
1748 QgsReadWriteContext context;
1749 context.setProjectTranslator( this );
1750 if ( !storage->readProject( filename, &inDevice, context ) )
1751 {
1752 QString err = tr( "Unable to open %1" ).arg( filename );
1753 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1754 if ( !messages.isEmpty() )
1755 err += QStringLiteral( "\n\n" ) + messages.last().message();
1756 setError( err );
1757 return false;
1758 }
1759 returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1760 }
1761 else
1762 {
1763 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1764 {
1765 returnValue = unzip( mFile.fileName(), flags );
1766 }
1767 else
1768 {
1769 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1770 const QFileInfo finfo( mFile.fileName() );
1771 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1772 if ( QFile( attachmentsZip ).exists() )
1773 {
1774 std::unique_ptr<QgsArchive> archive( new QgsArchive() );
1775 if ( archive->unzip( attachmentsZip ) )
1776 {
1777 releaseHandlesToProjectArchive();
1778 mArchive = std::move( archive );
1779 }
1780 }
1781 returnValue = readProjectFile( mFile.fileName(), flags );
1782 }
1783
1784 //on translation we should not change the filename back
1785 if ( !mTranslator )
1786 {
1787 mFile.setFileName( filename );
1788 mCachedHomePath.clear();
1789 mProjectScope.reset();
1790 }
1791 else
1792 {
1793 //but delete the translator
1794 mTranslator.reset( nullptr );
1795 }
1796 }
1797 emit homePathChanged();
1798 return returnValue;
1799}
1800
1801bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlags flags )
1802{
1804
1805 // avoid multiple emission of snapping updated signals
1806 ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
1807
1808 QFile projectFile( filename );
1809 clearError();
1810
1811 QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
1812 QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), QStringLiteral( "projectload" ) );
1813
1814 const QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), QgsApplication::settingsLocaleUserLocale->value() );
1815
1816 if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1817 {
1818 mTranslator.reset( new QTranslator() );
1819 ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1820 }
1821
1822 profile.switchTask( tr( "Reading project file" ) );
1823 std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1824
1825 if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1826 {
1827 projectFile.close();
1828
1829 setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1830
1831 return false;
1832 }
1833
1834 // location of problem associated with errorMsg
1835 int line, column;
1836 QString errorMsg;
1837
1838 if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1839 {
1840 const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1841 .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1842
1843 QgsDebugError( errorString );
1844
1845 projectFile.close();
1846
1847 setError( errorString );
1848
1849 return false;
1850 }
1851
1852 projectFile.close();
1853
1854 QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1855
1856 // get project version string, if any
1857 const QgsProjectVersion fileVersion = getVersion( *doc );
1858 const QgsProjectVersion thisVersion( Qgis::version() );
1859
1860 profile.switchTask( tr( "Updating project file" ) );
1861 if ( thisVersion > fileVersion )
1862 {
1863 const bool isOlderMajorVersion = fileVersion.majorVersion() < thisVersion.majorVersion();
1864
1865 if ( isOlderMajorVersion )
1866 {
1867 QgsLogger::warning( "Loading a file that was saved with an older "
1868 "version of qgis (saved in " + fileVersion.text() +
1869 ", loaded in " + Qgis::version() +
1870 "). Problems may occur." );
1871 }
1872
1873 QgsProjectFileTransform projectFile( *doc, fileVersion );
1874
1875 // Shows a warning when an old project file is read.
1877 emit oldProjectVersionWarning( fileVersion.text() );
1879 emit readVersionMismatchOccurred( fileVersion.text() );
1880
1881 projectFile.updateRevision( thisVersion );
1882 }
1883 else if ( fileVersion > thisVersion )
1884 {
1885 QgsLogger::warning( "Loading a file that was saved with a newer "
1886 "version of qgis (saved in " + fileVersion.text() +
1887 ", loaded in " + Qgis::version() +
1888 "). Problems may occur." );
1889
1890 emit readVersionMismatchOccurred( fileVersion.text() );
1891 }
1892
1893 // start new project, just keep the file name and auxiliary storage
1894 profile.switchTask( tr( "Creating auxiliary storage" ) );
1895 const QString fileName = mFile.fileName();
1896
1897
1898 // NOTE [ND] -- I suspect this is wrong, as the archive may contain any number of non-auxiliary
1899 // storage related files from the previously loaded project.
1900 std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1901 std::unique_ptr<QgsArchive> archive = std::move( mArchive );
1902
1903 clear();
1904 // this is ugly, but clear() will have created a new archive and started populating it. We
1905 // need to release handles to this archive now as the subsequent call to move will need
1906 // to delete it, and requires free access to do so.
1907 releaseHandlesToProjectArchive();
1908
1909 mAuxiliaryStorage = std::move( aStorage );
1910 mArchive = std::move( archive );
1911
1912
1913
1914 mFile.setFileName( fileName );
1915 mCachedHomePath.clear();
1916 mProjectScope.reset();
1917 mSaveVersion = fileVersion;
1918
1919 // now get any properties
1920 profile.switchTask( tr( "Reading properties" ) );
1921 _getProperties( *doc, mProperties );
1922
1923 // now get the data defined server properties
1924 mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
1925
1926 QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1927
1928#if 0
1929 dump_( mProperties );
1930#endif
1931
1932 // get older style project title
1933 QString oldTitle;
1934 _getTitle( *doc, oldTitle );
1935
1936 readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
1937
1938 const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1939 if ( homePathNl.count() > 0 )
1940 {
1941 const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1942 const QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1943 if ( !homePath.isEmpty() )
1945 }
1946 else
1947 {
1948 emit homePathChanged();
1949 }
1950
1951 const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1952 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1953 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1955 const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1956 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1957 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1958 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1960
1961
1962 const QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
1963 if ( !distanceUnitString.isEmpty() )
1964 setDistanceUnits( QgsUnitTypes::decodeDistanceUnit( distanceUnitString ) );
1965
1966 const QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
1967 if ( !areaUnitString.isEmpty() )
1968 setAreaUnits( QgsUnitTypes::decodeAreaUnit( areaUnitString ) );
1969
1970 QgsReadWriteContext context;
1971 context.setPathResolver( pathResolver() );
1972 context.setProjectTranslator( this );
1973
1974 //crs
1976 if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1977 {
1978 // first preference - dedicated projectCrs node
1979 const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1980 if ( !srsNode.isNull() )
1981 {
1982 projectCrs.readXml( srsNode );
1983 }
1984
1985 if ( !projectCrs.isValid() )
1986 {
1987 const QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1988 const long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1989 const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1990
1991 // authid should be prioritized over all
1992 const bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
1993 if ( !authid.isEmpty() && !isUserAuthId )
1994 projectCrs = QgsCoordinateReferenceSystem( authid );
1995
1996 // try the CRS
1997 if ( !projectCrs.isValid() && currentCRS >= 0 )
1998 {
1999 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
2000 }
2001
2002 // if that didn't produce a match, try the proj.4 string
2003 if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
2004 {
2005 projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
2006 }
2007
2008 // last just take the given id
2009 if ( !projectCrs.isValid() )
2010 {
2011 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
2012 }
2013 }
2014 }
2015 mCrs = projectCrs;
2016
2017 QStringList datumErrors;
2018 if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
2019 {
2020 emit missingDatumTransforms( datumErrors );
2021 }
2023
2024 // map shading
2025 const QDomNode elevationShadingNode = doc->documentElement().namedItem( QStringLiteral( "elevation-shading-renderer" ) );
2026 if ( !elevationShadingNode.isNull() )
2027 {
2028 mElevationShadingRenderer.readXml( elevationShadingNode.toElement(), context );
2029 }
2031
2032
2033 //add variables defined in project file - do this early in the reading cycle, as other components
2034 //(e.g. layouts) may depend on these variables
2035 const QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
2036 const QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
2037
2038 mCustomVariables.clear();
2039 if ( variableNames.length() == variableValues.length() )
2040 {
2041 for ( int i = 0; i < variableNames.length(); ++i )
2042 {
2043 mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
2044 }
2045 }
2046 else
2047 {
2048 QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
2049 }
2050
2051 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectMetadata" ) );
2052
2053 if ( !element.isNull() )
2054 {
2055 mMetadata.readMetadataXml( element );
2056 }
2057 else
2058 {
2059 // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
2060 mMetadata = QgsProjectMetadata();
2061 }
2062 if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
2063 {
2064 // upgrade older title storage to storing within project metadata.
2065 mMetadata.setTitle( oldTitle );
2066 }
2067 emit metadataChanged();
2068
2069 // Transaction mode
2070 element = doc->documentElement().firstChildElement( QStringLiteral( "transaction" ) );
2071 if ( !element.isNull() )
2072 {
2073 mTransactionMode = qgsEnumKeyToValue( element.attribute( QStringLiteral( "mode" ) ), Qgis::TransactionMode::Disabled );
2074 }
2075 else
2076 {
2077 // maybe older project => try read autotransaction
2078 element = doc->documentElement().firstChildElement( QStringLiteral( "autotransaction" ) );
2079 if ( ! element.isNull() )
2080 {
2081 mTransactionMode = static_cast<Qgis::TransactionMode>( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() );
2082 }
2083 }
2084
2085 // read the layer tree from project file
2086 profile.switchTask( tr( "Loading layer tree" ) );
2087 mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
2088
2089 QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
2090 if ( !layerTreeElem.isNull() )
2091 {
2092 // Use a temporary tree to read the nodes to prevent signals being delivered to the models
2093 QgsLayerTree tempTree;
2094 tempTree.readChildrenFromXml( layerTreeElem, context );
2095 mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
2096 }
2097 else
2098 {
2099 QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
2100 }
2101
2102 mLayerTreeRegistryBridge->setEnabled( false );
2103
2104 // get the map layers
2105 profile.switchTask( tr( "Reading map layers" ) );
2106
2107 loadProjectFlags( doc.get() );
2108
2109 QList<QDomNode> brokenNodes;
2110 const bool clean = _getMapLayers( *doc, brokenNodes, flags );
2111
2112 // review the integrity of the retrieved map layers
2113 if ( !clean && !( flags & Qgis::ProjectReadFlag::DontResolveLayers ) )
2114 {
2115 QgsDebugError( QStringLiteral( "Unable to get map layers from project file." ) );
2116
2117 if ( !brokenNodes.isEmpty() )
2118 {
2119 QgsDebugError( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
2120 }
2121
2122 // we let a custom handler decide what to do with missing layers
2123 // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
2124 mBadLayerHandler->handleBadLayers( brokenNodes );
2125 }
2126
2127 mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
2128 mMainAnnotationLayer->setTransformContext( mTransformContext );
2129
2130 // load embedded groups and layers
2131 profile.switchTask( tr( "Loading embedded layers" ) );
2132 loadEmbeddedNodes( mRootGroup, flags );
2133
2134 // Resolve references to other layers
2135 // Needs to be done here once all dependent layers are loaded
2136 profile.switchTask( tr( "Resolving layer references" ) );
2137 QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2138 for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
2139 {
2140 it.value()->resolveReferences( this );
2141 }
2142
2143 mLayerTreeRegistryBridge->setEnabled( true );
2144
2145 // now that layers are loaded, we can resolve layer tree's references to the layers
2146 profile.switchTask( tr( "Resolving references" ) );
2147 mRootGroup->resolveReferences( this );
2148
2149 // we need to migrate old fashion designed QgsSymbolLayerReference to new ones
2150 if ( QgsProjectVersion( 3, 28, 0 ) > mSaveVersion )
2151 {
2155 }
2156
2157 if ( !layerTreeElem.isNull() )
2158 {
2159 mRootGroup->readLayerOrderFromXml( layerTreeElem );
2160 }
2161
2162 // Load pre 3.0 configuration
2163 const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
2164 if ( !layerTreeCanvasElem.isNull( ) )
2165 {
2166 mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
2167 }
2168
2169 // Convert pre 3.4 to create layers flags
2170 if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
2171 {
2172 const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
2173 for ( const QString &layerId : requiredLayerIds )
2174 {
2175 if ( QgsMapLayer *layer = mapLayer( layerId ) )
2176 {
2177 layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
2178 }
2179 }
2180 const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
2181 for ( const QString &layerId : disabledLayerIds )
2182 {
2183 if ( QgsMapLayer *layer = mapLayer( layerId ) )
2184 {
2185 layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
2186 }
2187 }
2188 }
2189
2190 // Convert pre 3.26 default styles
2191 if ( QgsProjectVersion( 3, 26, 0 ) > mSaveVersion )
2192 {
2193 // Convert default symbols
2194 QString styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
2195 if ( !styleName.isEmpty() )
2196 {
2197 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2199 }
2200 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
2201 if ( !styleName.isEmpty() )
2202 {
2203 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2205 }
2206 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
2207 if ( !styleName.isEmpty() )
2208 {
2209 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2211 }
2212 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
2213 if ( !styleName.isEmpty() )
2214 {
2215 std::unique_ptr<QgsColorRamp> colorRamp( QgsStyle::defaultStyle()->colorRamp( styleName ) );
2216 styleSettings()->setDefaultColorRamp( colorRamp.get() );
2217 }
2218
2219 // Convert randomize default symbol fill color
2220 styleSettings()->setRandomizeDefaultSymbolColor( readBoolEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ), true ) );
2221
2222 // Convert default symbol opacity
2223 double opacity = 1.0;
2224 bool ok = false;
2225 // upgrade old setting
2226 double alpha = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ), 255, &ok );
2227 if ( ok )
2228 opacity = alpha / 255.0;
2229 double newOpacity = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ), 1.0, &ok );
2230 if ( ok )
2231 opacity = newOpacity;
2233
2234 // Cleanup
2235 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
2236 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
2237 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
2238 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
2239 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ) );
2240 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ) );
2241 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ) );
2242 }
2243
2244 // After bad layer handling we might still have invalid layers,
2245 // store them in case the user wanted to handle them later
2246 // or wanted to pass them through when saving
2248 {
2249 profile.switchTask( tr( "Storing original layer properties" ) );
2250 QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
2251 }
2252
2253 mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
2254
2255 profile.switchTask( tr( "Loading map themes" ) );
2256 mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
2258 mMapThemeCollection->readXml( *doc );
2259
2260 profile.switchTask( tr( "Loading label settings" ) );
2261 mLabelingEngineSettings->readSettingsFromProject( this );
2263
2264 profile.switchTask( tr( "Loading annotations" ) );
2265 mAnnotationManager->readXml( doc->documentElement(), context );
2267 {
2268 profile.switchTask( tr( "Loading layouts" ) );
2269 mLayoutManager->readXml( doc->documentElement(), *doc );
2270 }
2271
2273 {
2274 profile.switchTask( tr( "Loading 3D Views" ) );
2275 m3DViewsManager->readXml( doc->documentElement(), *doc );
2276 }
2277
2278 profile.switchTask( tr( "Loading bookmarks" ) );
2279 mBookmarkManager->readXml( doc->documentElement(), *doc );
2280
2281 profile.switchTask( tr( "Loading sensors" ) );
2282 mSensorManager->readXml( doc->documentElement(), *doc );
2283
2284 // reassign change dependencies now that all layers are loaded
2285 QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2286 for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
2287 {
2288 it.value()->setDependencies( it.value()->dependencies() );
2289 }
2290
2291 profile.switchTask( tr( "Loading snapping settings" ) );
2292 mSnappingConfig.readProject( *doc );
2293 mAvoidIntersectionsMode = static_cast<Qgis::AvoidIntersectionsMode>( readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( Qgis::AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
2294
2295 profile.switchTask( tr( "Loading view settings" ) );
2296 // restore older project scales settings
2297 mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
2298 const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
2299 QVector<double> res;
2300 for ( const QString &scale : scales )
2301 {
2302 const QStringList parts = scale.split( ':' );
2303 if ( parts.size() != 2 )
2304 continue;
2305
2306 bool ok = false;
2307 const double denominator = QLocale().toDouble( parts[1], &ok );
2308 if ( ok )
2309 {
2310 res << denominator;
2311 }
2312 }
2313 mViewSettings->setMapScales( res );
2314 const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
2315 if ( !viewSettingsElement.isNull() )
2316 mViewSettings->readXml( viewSettingsElement, context );
2317
2318 // restore style settings
2319 profile.switchTask( tr( "Loading style properties" ) );
2320 const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectStyleSettings" ) );
2321 if ( !styleSettingsElement.isNull() )
2322 {
2323 mStyleSettings->removeProjectStyle();
2324 mStyleSettings->readXml( styleSettingsElement, context, flags );
2325 }
2326
2327 // restore time settings
2328 profile.switchTask( tr( "Loading temporal settings" ) );
2329 const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectTimeSettings" ) );
2330 if ( !timeSettingsElement.isNull() )
2331 mTimeSettings->readXml( timeSettingsElement, context );
2332
2333
2334 profile.switchTask( tr( "Loading elevation properties" ) );
2335 const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( QStringLiteral( "ElevationProperties" ) );
2336 if ( !elevationPropertiesElement.isNull() )
2337 mElevationProperties->readXml( elevationPropertiesElement, context );
2338 mElevationProperties->resolveReferences( this );
2339
2340 profile.switchTask( tr( "Loading display settings" ) );
2341 {
2342 const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
2343 if ( !displaySettingsElement.isNull() )
2344 mDisplaySettings->readXml( displaySettingsElement, context );
2345 }
2346
2347 profile.switchTask( tr( "Loading GPS settings" ) );
2348 {
2349 const QDomElement gpsSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectGpsSettings" ) );
2350 if ( !gpsSettingsElement.isNull() )
2351 mGpsSettings->readXml( gpsSettingsElement, context );
2352 mGpsSettings->resolveReferences( this );
2353 }
2354
2355 profile.switchTask( tr( "Updating variables" ) );
2357 profile.switchTask( tr( "Updating CRS" ) );
2358 emit crsChanged();
2359 emit ellipsoidChanged( ellipsoid() );
2360
2361 // read the project: used by map canvas and legend
2362 profile.switchTask( tr( "Reading external settings" ) );
2363 emit readProject( *doc );
2364 emit readProjectWithContext( *doc, context );
2365
2366 profile.switchTask( tr( "Updating interface" ) );
2367
2368 snapSignalBlock.release();
2369 if ( !mBlockSnappingUpdates )
2370 emit snappingConfigChanged( mSnappingConfig );
2371
2374 emit projectColorsChanged();
2375
2376 // if all went well, we're allegedly in pristine state
2377 if ( clean )
2378 setDirty( false );
2379
2380 QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUser ), 2 );
2381 QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUserFull ), 2 );
2382
2386
2387 if ( mTranslator )
2388 {
2389 //project possibly translated -> rename it with locale postfix
2390 const QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
2391 setFileName( newFileName );
2392
2393 if ( write() )
2394 {
2395 setTitle( localeFileName );
2396 QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Success );
2397 }
2398 else
2399 {
2400 QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Critical );
2401 }
2402 }
2403
2404 // lastly, make any previously editable layers editable
2405 const QMap<QString, QgsMapLayer *> loadedLayers = mapLayers();
2406 for ( auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2407 {
2408 if ( it.value()->isValid() && it.value()->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2409 {
2410 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2411 vl->startEditing();
2412 it.value()->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2413 }
2414 }
2415
2416 return true;
2417}
2418
2419bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
2420{
2422
2423 bool valid = true;
2424 const auto constChildren = group->children();
2425 for ( QgsLayerTreeNode *child : constChildren )
2426 {
2427 if ( QgsLayerTree::isGroup( child ) )
2428 {
2429 QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
2430 if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2431 {
2432 // make sure to convert the path from relative to absolute
2433 const QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
2434 childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
2435 QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
2436 if ( newGroup )
2437 {
2438 QList<QgsLayerTreeNode *> clonedChildren;
2439 const QList<QgsLayerTreeNode *> constChildren = newGroup->children();
2440 clonedChildren.reserve( constChildren.size() );
2441 for ( QgsLayerTreeNode *newGroupChild : constChildren )
2442 clonedChildren << newGroupChild->clone();
2443 delete newGroup;
2444
2445 childGroup->insertChildNodes( 0, clonedChildren );
2446 }
2447 }
2448 else
2449 {
2450 loadEmbeddedNodes( childGroup, flags );
2451 }
2452 }
2453 else if ( QgsLayerTree::isLayer( child ) )
2454 {
2455 if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2456 {
2457 QList<QDomNode> brokenNodes;
2458 if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
2459 {
2460 valid = valid && false;
2461 }
2462 }
2463 }
2464
2465 }
2466
2467 return valid;
2468}
2469
2471{
2472 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
2474
2475 return mCustomVariables;
2476}
2477
2478void QgsProject::setCustomVariables( const QVariantMap &variables )
2479{
2481
2482 if ( variables == mCustomVariables )
2483 return;
2484
2485 //write variable to project
2486 QStringList variableNames;
2487 QStringList variableValues;
2488
2489 QVariantMap::const_iterator it = variables.constBegin();
2490 for ( ; it != variables.constEnd(); ++it )
2491 {
2492 variableNames << it.key();
2493 variableValues << it.value().toString();
2494 }
2495
2496 writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
2497 writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
2498
2499 mCustomVariables = variables;
2500 mProjectScope.reset();
2501
2503}
2504
2506{
2508
2509 *mLabelingEngineSettings = settings;
2511}
2512
2514{
2516
2517 return *mLabelingEngineSettings;
2518}
2519
2521{
2523
2524 mProjectScope.reset();
2525 return mLayerStore.get();
2526}
2527
2529{
2531
2532 return mLayerStore.get();
2533}
2534
2535QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
2536{
2538
2539 QList<QgsVectorLayer *> layers;
2540 const QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
2541 const auto constLayerIds = layerIds;
2542 for ( const QString &layerId : constLayerIds )
2543 {
2544 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
2545 layers << vlayer;
2546 }
2547 return layers;
2548}
2549
2550void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
2551{
2553
2554 QStringList list;
2555 list.reserve( layers.size() );
2556 for ( QgsVectorLayer *layer : layers )
2557 list << layer->id();
2558 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
2560}
2561
2573
2575{
2576 // this method is called quite extensively using QgsProject::instance()
2578
2579 // MUCH cheaper to clone than build
2580 if ( mProjectScope )
2581 {
2582 std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2583
2584 // we can't cache these variables
2585 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
2586 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
2587
2588 // neither this function
2589 projectScope->addFunction( QStringLiteral( "sensor_data" ), new GetSensorData( sensorManager()->sensorsData() ) );
2590
2591 return projectScope.release();
2592 }
2593
2594 mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
2595
2596 const QVariantMap vars = customVariables();
2597
2598 QVariantMap::const_iterator it = vars.constBegin();
2599
2600 for ( ; it != vars.constEnd(); ++it )
2601 {
2602 mProjectScope->setVariable( it.key(), it.value(), true );
2603 }
2604
2605 QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
2606 if ( projectPath.isEmpty() )
2607 projectPath = mOriginalPath;
2608 const QString projectFolder = QFileInfo( projectPath ).path();
2609 const QString projectFilename = QFileInfo( projectPath ).fileName();
2610 const QString projectBasename = baseName();
2611
2612 //add other known project variables
2613 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
2614 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
2615 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
2616 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
2617 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
2618 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
2619 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_last_saved" ), mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
2620 const QgsCoordinateReferenceSystem projectCrs = crs();
2621 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
2622 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
2623 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_description" ), projectCrs.description(), true, true ) );
2624 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
2625 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
2626 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_units" ), QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
2627 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_acronym" ), projectCrs.projectionAcronym(), true ) );
2628 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_ellipsoid" ), projectCrs.ellipsoidAcronym(), true ) );
2629 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_proj4" ), projectCrs.toProj(), true ) );
2630 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_wkt" ), projectCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), true ) );
2631
2632 // metadata
2633 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
2634 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
2635 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
2636 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
2637
2638 // keywords
2639 QVariantMap keywords;
2640 const QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2641 for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2642 {
2643 keywords.insert( it.key(), it.value() );
2644 }
2645 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
2646
2647 // layers
2648 QVariantList layersIds;
2649 QVariantList layers;
2650 const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2651 layersIds.reserve( layersInProject.count() );
2652 layers.reserve( layersInProject.count() );
2653 for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2654 {
2655 layersIds << it.value()->id();
2656 layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( it.value() ) );
2657 }
2658 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_ids" ), layersIds, true ) );
2659 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
2660
2661 mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
2662
2664}
2665
2666void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
2667{
2669
2670 const QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2671
2672 const auto constLayers = layers;
2673 for ( QgsMapLayer *layer : constLayers )
2674 {
2675 if ( ! layer->isValid() )
2676 return;
2677
2678 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
2679 {
2680 vlayer->setReadExtentFromXml( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics );
2681 if ( vlayer->dataProvider() )
2682 vlayer->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues,
2684 }
2685
2686 connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
2687
2688 // check if we have to update connections for layers with dependencies
2689 for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2690 {
2691 const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2692 if ( deps.contains( layer->id() ) )
2693 {
2694 // reconnect to change signals
2695 it.value()->setDependencies( deps );
2696 }
2697 }
2698 }
2699
2700 updateTransactionGroups();
2701
2702 if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
2703 emit snappingConfigChanged( mSnappingConfig );
2704}
2705
2706void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
2707{
2709
2710 if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
2711 emit snappingConfigChanged( mSnappingConfig );
2712}
2713
2714void QgsProject::cleanTransactionGroups( bool force )
2715{
2717
2718 bool changed = false;
2719 for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2720 {
2721 if ( tg.value()->isEmpty() || force )
2722 {
2723 delete tg.value();
2724 tg = mTransactionGroups.erase( tg );
2725 changed = true;
2726 }
2727 else
2728 {
2729 ++tg;
2730 }
2731 }
2732 if ( changed )
2734}
2735
2736void QgsProject::updateTransactionGroups()
2737{
2739
2740 mEditBufferGroup.clear();
2741
2742 switch ( mTransactionMode )
2743 {
2745 {
2746 cleanTransactionGroups( true );
2747 return;
2748 }
2749 break;
2751 cleanTransactionGroups( true );
2752 break;
2754 cleanTransactionGroups( false );
2755 break;
2756 }
2757
2758 bool tgChanged = false;
2759 const auto constLayers = mapLayers().values();
2760 for ( QgsMapLayer *layer : constLayers )
2761 {
2762 if ( ! layer->isValid() )
2763 continue;
2764
2765 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2766 if ( ! vlayer )
2767 continue;
2768
2769 switch ( mTransactionMode )
2770 {
2772 Q_ASSERT( false );
2773 break;
2775 {
2777 {
2778 const QString connString = QgsTransaction::connectionString( vlayer->source() );
2779 const QString key = vlayer->providerType();
2780
2781 QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
2782
2783 if ( !tg )
2784 {
2785 tg = new QgsTransactionGroup();
2786 mTransactionGroups.insert( qMakePair( key, connString ), tg );
2787 tgChanged = true;
2788 }
2789 tg->addLayer( vlayer );
2790 }
2791 }
2792 break;
2794 {
2795 if ( vlayer->supportsEditing() )
2796 mEditBufferGroup.addLayer( vlayer );
2797 }
2798 break;
2799 }
2800 }
2801
2802 if ( tgChanged )
2804}
2805
2806bool QgsProject::readLayer( const QDomNode &layerNode )
2807{
2809
2810 QgsReadWriteContext context;
2811 context.setPathResolver( pathResolver() );
2812 context.setProjectTranslator( this );
2813 context.setTransformContext( transformContext() );
2814 QList<QDomNode> brokenNodes;
2815 if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
2816 {
2817 // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
2818 // added layer for joins
2819 const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
2820 for ( QgsVectorLayer *layer : vectorLayers )
2821 {
2822 // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
2823 layer->resolveReferences( this );
2824
2825 if ( layer->isValid() && layer->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2826 {
2827 layer->startEditing();
2828 layer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2829 }
2830 }
2831 return true;
2832 }
2833 return false;
2834}
2835
2836bool QgsProject::write( const QString &filename )
2837{
2839
2840 mFile.setFileName( filename );
2841 mCachedHomePath.clear();
2842 return write();
2843}
2844
2846{
2848
2849 mProjectScope.reset();
2850 if ( QgsProjectStorage *storage = projectStorage() )
2851 {
2852 QgsReadWriteContext context;
2853 // for projects stored in a custom storage, we have to check for the support
2854 // of relative paths since the storage most likely will not be in a file system
2855 const QString storageFilePath { storage->filePath( mFile.fileName() ) };
2856 if ( storageFilePath.isEmpty() )
2857 {
2859 }
2860 context.setPathResolver( pathResolver() );
2861
2862 const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
2863 const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
2864
2865 if ( !zip( tmpZipFilename ) )
2866 return false; // zip() already calls setError() when returning false
2867
2868 QFile tmpZipFile( tmpZipFilename );
2869 if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
2870 {
2871 setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
2872 return false;
2873 }
2874
2876 if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
2877 {
2878 QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
2879 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2880 if ( !messages.isEmpty() )
2881 err += QStringLiteral( "\n\n" ) + messages.last().message();
2882 setError( err );
2883 return false;
2884 }
2885
2886 tmpZipFile.close();
2887 QFile::remove( tmpZipFilename );
2888
2889 return true;
2890 }
2891
2892 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2893 {
2894 return zip( mFile.fileName() );
2895 }
2896 else
2897 {
2898 // write project file even if the auxiliary storage is not correctly
2899 // saved
2900 const bool asOk = saveAuxiliaryStorage();
2901 const bool writeOk = writeProjectFile( mFile.fileName() );
2902 bool attachmentsOk = true;
2903 if ( !mArchive->files().isEmpty() )
2904 {
2905 const QFileInfo finfo( mFile.fileName() );
2906 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
2907 attachmentsOk = mArchive->zip( attachmentsZip );
2908 }
2909
2910 // errors raised during writing project file are more important
2911 if ( ( !asOk || !attachmentsOk ) && writeOk )
2912 {
2913 QStringList errorMessage;
2914 if ( !asOk )
2915 {
2916 const QString err = mAuxiliaryStorage->errorString();
2917 errorMessage.append( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2918 }
2919 if ( !attachmentsOk )
2920 {
2921 errorMessage.append( tr( "Unable to save attachments archive" ) );
2922 }
2923 setError( errorMessage.join( '\n' ) );
2924 }
2925
2926 return asOk && writeOk && attachmentsOk;
2927 }
2928}
2929
2930bool QgsProject::writeProjectFile( const QString &filename )
2931{
2933
2934 QFile projectFile( filename );
2935 clearError();
2936
2937 // if we have problems creating or otherwise writing to the project file,
2938 // let's find out up front before we go through all the hand-waving
2939 // necessary to create all the Dom objects
2940 const QFileInfo myFileInfo( projectFile );
2941 if ( myFileInfo.exists() && !myFileInfo.isWritable() )
2942 {
2943 setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
2944 .arg( projectFile.fileName() ) );
2945 return false;
2946 }
2947
2948 QgsReadWriteContext context;
2949 context.setPathResolver( pathResolver() );
2951
2952 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
2953
2954 const QDomDocumentType documentType =
2955 QDomImplementation().createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
2956 QStringLiteral( "SYSTEM" ) );
2957 std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
2958
2959 QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
2960 qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
2961 qgisNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
2962
2963 if ( !mSettings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
2964 {
2965 const QString newSaveUser = QgsApplication::userLoginName();
2966 const QString newSaveUserFull = QgsApplication::userFullName();
2967 qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2968 qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2969 mSaveUser = newSaveUser;
2970 mSaveUserFull = newSaveUserFull;
2971 mSaveDateTime = QDateTime::currentDateTime();
2972 qgisNode.setAttribute( QStringLiteral( "saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
2973 }
2974 else
2975 {
2976 mSaveUser.clear();
2977 mSaveUserFull.clear();
2978 mSaveDateTime = QDateTime();
2979 }
2980 doc->appendChild( qgisNode );
2981 mSaveVersion = QgsProjectVersion( Qgis::version() );
2982
2983 QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2984 homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2985 qgisNode.appendChild( homePathNode );
2986
2987 // title
2988 QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2989 qgisNode.appendChild( titleNode );
2990
2991 QDomElement transactionNode = doc->createElement( QStringLiteral( "transaction" ) );
2992 transactionNode.setAttribute( QStringLiteral( "mode" ), qgsEnumValueToKey( mTransactionMode ) );
2993 qgisNode.appendChild( transactionNode );
2994
2995 QDomElement flagsNode = doc->createElement( QStringLiteral( "projectFlags" ) );
2996 flagsNode.setAttribute( QStringLiteral( "set" ), qgsFlagValueToKeys( mFlags ) );
2997 qgisNode.appendChild( flagsNode );
2998
2999 const QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
3000 titleNode.appendChild( titleText );
3001
3002 // write project CRS
3003 QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
3004 mCrs.writeXml( srsNode, *doc );
3005 qgisNode.appendChild( srsNode );
3006
3007 QDomElement elevationShadingNode = doc->createElement( QStringLiteral( "elevation-shading-renderer" ) );
3008 mElevationShadingRenderer.writeXml( elevationShadingNode, context );
3009 qgisNode.appendChild( elevationShadingNode );
3010
3011 // write layer tree - make sure it is without embedded subgroups
3012 QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
3014 QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
3015
3016 clonedRoot->writeXml( qgisNode, context );
3017 delete clonedRoot;
3018
3019 mSnappingConfig.writeProject( *doc );
3020 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( mAvoidIntersectionsMode ) );
3021
3022 // let map canvas and legend write their information
3023 emit writeProject( *doc );
3024
3025 // within top level node save list of layers
3026 const QMap<QString, QgsMapLayer *> layers = mapLayers();
3027
3028 QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
3029 mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
3030 qgisNode.appendChild( annotationLayerNode );
3031
3032 // Iterate over layers in zOrder
3033 // Call writeXml() on each
3034 QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
3035
3036 QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
3037 while ( li != layers.end() )
3038 {
3039 QgsMapLayer *ml = li.value();
3040
3041 if ( ml )
3042 {
3043 const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
3044 if ( emIt == mEmbeddedLayers.constEnd() )
3045 {
3046 QDomElement maplayerElem;
3047 // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
3048 // not available, just write what we DO have
3049 if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
3050 {
3051 // general layer metadata
3052 maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
3053 ml->writeLayerXml( maplayerElem, *doc, context );
3054
3056 maplayerElem.setAttribute( QStringLiteral( "editable" ), QStringLiteral( "1" ) );
3057 }
3058 else if ( ! ml->originalXmlProperties().isEmpty() )
3059 {
3060 QDomDocument document;
3061 if ( document.setContent( ml->originalXmlProperties() ) )
3062 {
3063 maplayerElem = document.firstChildElement();
3064 }
3065 else
3066 {
3067 QgsDebugError( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
3068 }
3069 }
3070
3071 emit writeMapLayer( ml, maplayerElem, *doc );
3072
3073 projectLayersNode.appendChild( maplayerElem );
3074 }
3075 else
3076 {
3077 // layer defined in an external project file
3078 // only save embedded layer if not managed by a legend group
3079 if ( emIt.value().second )
3080 {
3081 QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
3082 mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
3083 mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
3084 mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
3085 projectLayersNode.appendChild( mapLayerElem );
3086 }
3087 }
3088 }
3089 li++;
3090 }
3091
3092 qgisNode.appendChild( projectLayersNode );
3093
3094 QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
3095 const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
3096 for ( QgsMapLayer *layer : constCustomLayerOrder )
3097 {
3098 QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
3099 mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
3100 layerOrderNode.appendChild( mapLayerElem );
3101 }
3102 qgisNode.appendChild( layerOrderNode );
3103
3104 mLabelingEngineSettings->writeSettingsToProject( this );
3105
3106 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
3107 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
3108 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
3109
3110 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
3111 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
3112 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
3113 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
3114
3115 writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( mDistanceUnits ) );
3116 writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( mAreaUnits ) );
3117
3118 // now add the optional extra properties
3119#if 0
3120 dump_( mProperties );
3121#endif
3122
3123 QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 2 );
3124
3125 if ( !mProperties.isEmpty() ) // only worry about properties if we
3126 // actually have any properties
3127 {
3128 mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
3129 }
3130
3131 QDomElement ddElem = doc->createElement( QStringLiteral( "dataDefinedServerProperties" ) );
3132 mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
3133 qgisNode.appendChild( ddElem );
3134
3135 mMapThemeCollection->writeXml( *doc );
3136
3137 mTransformContext.writeXml( qgisNode, context );
3138
3139 QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
3140 mMetadata.writeMetadataXml( metadataElem, *doc );
3141 qgisNode.appendChild( metadataElem );
3142
3143 {
3144 const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
3145 qgisNode.appendChild( annotationsElem );
3146 }
3147
3148 {
3149 const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
3150 qgisNode.appendChild( layoutElem );
3151 }
3152
3153 {
3154 const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
3155 qgisNode.appendChild( views3DElem );
3156 }
3157
3158 {
3159 const QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
3160 qgisNode.appendChild( bookmarkElem );
3161 }
3162
3163 {
3164 const QDomElement sensorElem = mSensorManager->writeXml( *doc );
3165 qgisNode.appendChild( sensorElem );
3166 }
3167
3168 {
3169 const QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
3170 qgisNode.appendChild( viewSettingsElem );
3171 }
3172
3173 {
3174 const QDomElement styleSettingsElem = mStyleSettings->writeXml( *doc, context );
3175 qgisNode.appendChild( styleSettingsElem );
3176 }
3177
3178 {
3179 const QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
3180 qgisNode.appendChild( timeSettingsElement );
3181 }
3182
3183 {
3184 const QDomElement elevationPropertiesElement = mElevationProperties->writeXml( *doc, context );
3185 qgisNode.appendChild( elevationPropertiesElement );
3186 }
3187
3188 {
3189 const QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
3190 qgisNode.appendChild( displaySettingsElem );
3191 }
3192
3193 {
3194 const QDomElement gpsSettingsElem = mGpsSettings->writeXml( *doc, context );
3195 qgisNode.appendChild( gpsSettingsElem );
3196 }
3197
3198 // now wrap it up and ship it to the project file
3199 doc->normalize(); // XXX I'm not entirely sure what this does
3200
3201 // Create backup file
3202 if ( QFile::exists( fileName() ) )
3203 {
3204 QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
3205 bool ok = true;
3206 ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
3207 ok &= projectFile.open( QIODevice::ReadOnly );
3208
3209 QByteArray ba;
3210 while ( ok && !projectFile.atEnd() )
3211 {
3212 ba = projectFile.read( 10240 );
3213 ok &= backupFile.write( ba ) == ba.size();
3214 }
3215
3216 projectFile.close();
3217 backupFile.close();
3218
3219 if ( !ok )
3220 {
3221 setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
3222 return false;
3223 }
3224
3225 const QFileInfo fi( fileName() );
3226 struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
3227 utime( backupFile.fileName().toUtf8().constData(), &tb );
3228 }
3229
3230 if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3231 {
3232 projectFile.close(); // even though we got an error, let's make
3233 // sure it's closed anyway
3234
3235 setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
3236 return false;
3237 }
3238
3239 QTemporaryFile tempFile;
3240 bool ok = tempFile.open();
3241 if ( ok )
3242 {
3243 QTextStream projectFileStream( &tempFile );
3244 doc->save( projectFileStream, 2 ); // save as utf-8
3245 ok &= projectFileStream.pos() > -1;
3246
3247 ok &= tempFile.seek( 0 );
3248
3249 QByteArray ba;
3250 while ( ok && !tempFile.atEnd() )
3251 {
3252 ba = tempFile.read( 10240 );
3253 ok &= projectFile.write( ba ) == ba.size();
3254 }
3255
3256 ok &= projectFile.error() == QFile::NoError;
3257
3258 projectFile.close();
3259 }
3260
3261 tempFile.close();
3262
3263 if ( !ok )
3264 {
3265 setError( tr( "Unable to save to file %1. Your project "
3266 "may be corrupted on disk. Try clearing some space on the volume and "
3267 "check file permissions before pressing save again." )
3268 .arg( projectFile.fileName() ) );
3269 return false;
3270 }
3271
3272 setDirty( false ); // reset to pristine state
3273
3274 emit projectSaved();
3275 return true;
3276}
3277
3278bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
3279{
3281
3282 bool propertiesModified;
3283 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3284
3285 if ( propertiesModified )
3286 setDirty( true );
3287
3288 return success;
3289}
3290
3291bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
3292{
3294
3295 bool propertiesModified;
3296 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3297
3298 if ( propertiesModified )
3299 setDirty( true );
3300
3301 return success;
3302}
3303
3304bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
3305{
3307
3308 bool propertiesModified;
3309 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3310
3311 if ( propertiesModified )
3312 setDirty( true );
3313
3314 return success;
3315}
3316
3317bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
3318{
3320
3321 bool propertiesModified;
3322 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3323
3324 if ( propertiesModified )
3325 setDirty( true );
3326
3327 return success;
3328}
3329
3330bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
3331{
3333
3334 bool propertiesModified;
3335 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3336
3337 if ( propertiesModified )
3338 setDirty( true );
3339
3340 return success;
3341}
3342
3343QStringList QgsProject::readListEntry( const QString &scope,
3344 const QString &key,
3345 const QStringList &def,
3346 bool *ok ) const
3347{
3348 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
3350
3351 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3352
3353 QVariant value;
3354
3355 if ( property )
3356 {
3357 value = property->value();
3358
3359 const bool valid = QVariant::StringList == value.type();
3360 if ( ok )
3361 *ok = valid;
3362
3363 if ( valid )
3364 {
3365 return value.toStringList();
3366 }
3367 }
3368 else if ( ok )
3369 *ok = false;
3370
3371
3372 return def;
3373}
3374
3375QString QgsProject::readEntry( const QString &scope,
3376 const QString &key,
3377 const QString &def,
3378 bool *ok ) const
3379{
3381
3382 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3383
3384 QVariant value;
3385
3386 if ( property )
3387 {
3388 value = property->value();
3389
3390 const bool valid = value.canConvert( QVariant::String );
3391 if ( ok )
3392 *ok = valid;
3393
3394 if ( valid )
3395 return value.toString();
3396 }
3397 else if ( ok )
3398 *ok = false;
3399
3400 return def;
3401}
3402
3403int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
3404 bool *ok ) const
3405{
3407
3408 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3409
3410 QVariant value;
3411
3412 if ( property )
3413 {
3414 value = property->value();
3415 }
3416
3417 const bool valid = value.canConvert( QVariant::Int );
3418
3419 if ( ok )
3420 {
3421 *ok = valid;
3422 }
3423
3424 if ( valid )
3425 {
3426 return value.toInt();
3427 }
3428
3429 return def;
3430}
3431
3432double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
3433 double def,
3434 bool *ok ) const
3435{
3437
3438 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3439 if ( property )
3440 {
3441 const QVariant value = property->value();
3442
3443 const bool valid = value.canConvert( QVariant::Double );
3444 if ( ok )
3445 *ok = valid;
3446
3447 if ( valid )
3448 return value.toDouble();
3449 }
3450 else if ( ok )
3451 *ok = false;
3452
3453 return def;
3454}
3455
3456bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
3457 bool *ok ) const
3458{
3460
3461 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3462
3463 if ( property )
3464 {
3465 const QVariant value = property->value();
3466
3467 const bool valid = value.canConvert( QVariant::Bool );
3468 if ( ok )
3469 *ok = valid;
3470
3471 if ( valid )
3472 return value.toBool();
3473 }
3474 else if ( ok )
3475 *ok = false;
3476
3477 return def;
3478}
3479
3480bool QgsProject::removeEntry( const QString &scope, const QString &key )
3481{
3483
3484 if ( findKey_( scope, key, mProperties ) )
3485 {
3486 removeKey_( scope, key, mProperties );
3487 setDirty( true );
3488 }
3489
3490 return !findKey_( scope, key, mProperties );
3491}
3492
3493QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
3494{
3496
3497 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3498
3499 QStringList entries;
3500
3501 if ( foundProperty )
3502 {
3503 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3504
3505 if ( propertyKey )
3506 { propertyKey->entryList( entries ); }
3507 }
3508
3509 return entries;
3510}
3511
3512QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
3513{
3515
3516 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3517
3518 QStringList entries;
3519
3520 if ( foundProperty )
3521 {
3522 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3523
3524 if ( propertyKey )
3525 { propertyKey->subkeyList( entries ); }
3526 }
3527
3528 return entries;
3529}
3530
3532{
3534
3535 dump_( mProperties );
3536}
3537
3539{
3541
3542 QString filePath;
3543 switch ( filePathStorage() )
3544 {
3546 break;
3547
3549 {
3550 // for projects stored in a custom storage, we need to ask to the
3551 // storage for the path, if the storage returns an empty path
3552 // relative paths are not supported
3553 if ( QgsProjectStorage *storage = projectStorage() )
3554 {
3555 filePath = storage->filePath( mFile.fileName() );
3556 }
3557 else
3558 {
3559 filePath = fileName();
3560 }
3561 break;
3562 }
3563 }
3564
3565 return QgsPathResolver( filePath, mArchive->dir() );
3566}
3567
3568QString QgsProject::readPath( const QString &src ) const
3569{
3571
3572 return pathResolver().readPath( src );
3573}
3574
3575QString QgsProject::writePath( const QString &src ) const
3576{
3578
3579 return pathResolver().writePath( src );
3580}
3581
3582void QgsProject::setError( const QString &errorMessage )
3583{
3585
3586 mErrorMessage = errorMessage;
3587}
3588
3589QString QgsProject::error() const
3590{
3592
3593 return mErrorMessage;
3594}
3595
3596void QgsProject::clearError()
3597{
3599
3600 setError( QString() );
3601}
3602
3604{
3606
3607 delete mBadLayerHandler;
3608 mBadLayerHandler = handler;
3609}
3610
3611QString QgsProject::layerIsEmbedded( const QString &id ) const
3612{
3614
3615 const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
3616 if ( it == mEmbeddedLayers.constEnd() )
3617 {
3618 return QString();
3619 }
3620 return it.value().first;
3621}
3622
3623bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
3624 bool saveFlag, Qgis::ProjectReadFlags flags )
3625{
3627
3629
3630 static QString sPrevProjectFilePath;
3631 static QDateTime sPrevProjectFileTimestamp;
3632 static QDomDocument sProjectDocument;
3633
3634 QString qgsProjectFile = projectFilePath;
3635 QgsProjectArchive archive;
3636 if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3637 {
3638 archive.unzip( projectFilePath );
3639 qgsProjectFile = archive.projectFile();
3640 }
3641
3642 const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
3643
3644 if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
3645 {
3646 sPrevProjectFilePath.clear();
3647
3648 QFile projectFile( qgsProjectFile );
3649 if ( !projectFile.open( QIODevice::ReadOnly ) )
3650 {
3651 return false;
3652 }
3653
3654 if ( !sProjectDocument.setContent( &projectFile ) )
3655 {
3656 return false;
3657 }
3658
3659 sPrevProjectFilePath = projectFilePath;
3660 sPrevProjectFileTimestamp = projectFileTimestamp;
3661 }
3662
3663 // does project store paths absolute or relative?
3664 bool useAbsolutePaths = true;
3665
3666 const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
3667 if ( !propertiesElem.isNull() )
3668 {
3669 const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
3670 if ( !absElem.isNull() )
3671 {
3672 useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
3673 }
3674 }
3675
3676 QgsReadWriteContext embeddedContext;
3677 if ( !useAbsolutePaths )
3678 embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
3679 embeddedContext.setProjectTranslator( this );
3680 embeddedContext.setTransformContext( transformContext() );
3681
3682 const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
3683 if ( projectLayersElem.isNull() )
3684 {
3685 return false;
3686 }
3687
3688 QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral( "maplayer" ) );
3689 while ( ! mapLayerElem.isNull() )
3690 {
3691 // get layer id
3692 const QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
3693 if ( id == layerId )
3694 {
3695 // layer can be embedded only once
3696 if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
3697 {
3698 return false;
3699 }
3700
3701 mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
3702
3703 if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
3704 {
3705 return true;
3706 }
3707 else
3708 {
3709 mEmbeddedLayers.remove( layerId );
3710 return false;
3711 }
3712 }
3713 mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
3714 }
3715
3716 return false;
3717}
3718
3719QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags )
3720{
3722
3723 QString qgsProjectFile = projectFilePath;
3724 QgsProjectArchive archive;
3725 if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3726 {
3727 archive.unzip( projectFilePath );
3728 qgsProjectFile = archive.projectFile();
3729 }
3730
3731 // open project file, get layer ids in group, add the layers
3732 QFile projectFile( qgsProjectFile );
3733 if ( !projectFile.open( QIODevice::ReadOnly ) )
3734 {
3735 return nullptr;
3736 }
3737
3738 QDomDocument projectDocument;
3739 if ( !projectDocument.setContent( &projectFile ) )
3740 {
3741 return nullptr;
3742 }
3743
3744 QgsReadWriteContext context;
3745 context.setPathResolver( pathResolver() );
3746 context.setProjectTranslator( this );
3748
3750
3751 QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
3752 if ( !layerTreeElem.isNull() )
3753 {
3754 root->readChildrenFromXml( layerTreeElem, context );
3755 }
3756 else
3757 {
3758 QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
3759 }
3760
3761 QgsLayerTreeGroup *group = root->findGroup( groupName );
3762 if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
3763 {
3764 // embedded groups cannot be embedded again
3765 delete root;
3766 return nullptr;
3767 }
3768
3769 // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
3770 QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
3771 delete root;
3772 root = nullptr;
3773
3774 newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3775 newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
3776
3777 // set "embedded" to all children + load embedded layers
3778 mLayerTreeRegistryBridge->setEnabled( false );
3779 initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
3780 mLayerTreeRegistryBridge->setEnabled( true );
3781
3782 // consider the layers might be identify disabled in its project
3783 const auto constFindLayerIds = newGroup->findLayerIds();
3784 for ( const QString &layerId : constFindLayerIds )
3785 {
3786 QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
3787 if ( layer )
3788 {
3789 layer->resolveReferences( this );
3790 layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
3791 }
3792 }
3793
3794 return newGroup;
3795}
3796
3797void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
3798{
3800
3801 const auto constChildren = group->children();
3802 for ( QgsLayerTreeNode *child : constChildren )
3803 {
3804 // all nodes in the subtree will have "embedded" custom property set
3805 child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3806
3807 if ( QgsLayerTree::isGroup( child ) )
3808 {
3809 initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
3810 }
3811 else if ( QgsLayerTree::isLayer( child ) )
3812 {
3813 // load the layer into our project
3814 QList<QDomNode> brokenNodes;
3815 createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
3816 }
3817 }
3818}
3819
3826
3833
3835{
3837
3838 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
3840}
3841
3843{
3845
3846 return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
3847}
3848
3850{
3852
3853 if ( mDistanceUnits == unit )
3854 return;
3855
3856 mDistanceUnits = unit;
3857
3858 emit distanceUnitsChanged();
3859}
3860
3862{
3864
3865 if ( mAreaUnits == unit )
3866 return;
3867
3868 mAreaUnits = unit;
3869
3870 emit areaUnitsChanged();
3871}
3872
3874{
3875 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
3877
3878 if ( !mCachedHomePath.isEmpty() )
3879 return mCachedHomePath;
3880
3881 const QFileInfo pfi( fileName() );
3882
3883 if ( !mHomePath.isEmpty() )
3884 {
3885 const QFileInfo homeInfo( mHomePath );
3886 if ( !homeInfo.isRelative() )
3887 {
3888 mCachedHomePath = mHomePath;
3889 return mHomePath;
3890 }
3891 }
3892 else if ( !fileName().isEmpty() )
3893 {
3894
3895 // If it's not stored in the file system, try to get the path from the storage
3896 if ( QgsProjectStorage *storage = projectStorage() )
3897 {
3898 const QString storagePath { storage->filePath( fileName() ) };
3899 if ( ! storagePath.isEmpty() && QFileInfo::exists( storagePath ) )
3900 {
3901 mCachedHomePath = QFileInfo( storagePath ).path();
3902 return mCachedHomePath;
3903 }
3904 }
3905
3906 mCachedHomePath = pfi.path();
3907 return mCachedHomePath;
3908 }
3909
3910 if ( !pfi.exists() )
3911 {
3912 mCachedHomePath = mHomePath;
3913 return mHomePath;
3914 }
3915
3916 if ( !mHomePath.isEmpty() )
3917 {
3918 // path is relative to project file
3919 mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
3920 }
3921 else
3922 {
3923 mCachedHomePath = pfi.canonicalPath();
3924 }
3925 return mCachedHomePath;
3926}
3927
3929{
3931
3932 return mHomePath;
3933}
3934
3936{
3937 // because relation aggregate functions are not thread safe
3939
3940 return mRelationManager;
3941}
3942
3944{
3946
3947 return mLayoutManager.get();
3948}
3949
3951{
3953
3954 return mLayoutManager.get();
3955}
3956
3958{
3960
3961 return m3DViewsManager.get();
3962}
3963
3965{
3967
3968 return m3DViewsManager.get();
3969}
3970
3972{
3974
3975 return mBookmarkManager;
3976}
3977
3979{
3981
3982 return mBookmarkManager;
3983}
3984
3986{
3988
3989 return mSensorManager;
3990}
3991
3993{
3995
3996 return mSensorManager;
3997}
3998
4000{
4002
4003 return mViewSettings;
4004}
4005
4012
4014{
4016
4017 return mStyleSettings;
4018}
4019
4021{
4022 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
4024
4025 return mStyleSettings;
4026}
4027
4029{
4031
4032 return mTimeSettings;
4033}
4034
4041
4043{
4045
4046 return mElevationProperties;
4047}
4048
4055
4057{
4059
4060 return mDisplaySettings;
4061}
4062
4064{
4066
4067 return mDisplaySettings;
4068}
4069
4071{
4073
4074 return mGpsSettings;
4075}
4076
4083
4085{
4087
4088 return mRootGroup;
4089}
4090
4092{
4094
4095 return mMapThemeCollection.get();
4096}
4097
4099{
4101
4102 return mAnnotationManager.get();
4103}
4104
4106{
4108
4109 return mAnnotationManager.get();
4110}
4111
4112void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
4113{
4115
4116 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4117 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4118 {
4119 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
4120 continue;
4121
4122 if ( layers.contains( it.value() ) )
4123 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
4124 else
4125 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
4126 }
4127
4131}
4132
4133void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
4134{
4136
4137 QList<QgsMapLayer *> nonIdentifiableLayers;
4138 nonIdentifiableLayers.reserve( layerIds.count() );
4139 for ( const QString &layerId : layerIds )
4140 {
4141 QgsMapLayer *layer = mapLayer( layerId );
4142 if ( layer )
4143 nonIdentifiableLayers << layer;
4144 }
4148}
4149
4151{
4153
4154 QStringList nonIdentifiableLayers;
4155
4156 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4157 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4158 {
4159 if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
4160 {
4161 nonIdentifiableLayers.append( it.value()->id() );
4162 }
4163 }
4164 return nonIdentifiableLayers;
4165}
4166
4168{
4170
4171 return mTransactionMode == Qgis::TransactionMode::AutomaticGroups;
4172}
4173
4174void QgsProject::setAutoTransaction( bool autoTransaction )
4175{
4177
4178 if ( autoTransaction
4179 && mTransactionMode == Qgis::TransactionMode::AutomaticGroups )
4180 return;
4181
4182 if ( ! autoTransaction
4183 && mTransactionMode == Qgis::TransactionMode::Disabled )
4184 return;
4185
4186 if ( autoTransaction )
4187 mTransactionMode = Qgis::TransactionMode::AutomaticGroups;
4188 else
4189 mTransactionMode = Qgis::TransactionMode::Disabled;
4190
4191 updateTransactionGroups();
4192}
4193
4195{
4197
4198 return mTransactionMode;
4199}
4200
4202{
4204
4205 if ( transactionMode == mTransactionMode )
4206 return true;
4207
4208 // Check that all layer are not in edit mode
4209 const auto constLayers = mapLayers().values();
4210 for ( QgsMapLayer *layer : constLayers )
4211 {
4212 if ( layer->isEditable() )
4213 {
4214 QgsLogger::warning( tr( "Transaction mode can be changed only if all layers are not editable." ) );
4215 return false;
4216 }
4217 }
4218
4219 mTransactionMode = transactionMode;
4220 updateTransactionGroups();
4221 return true;
4222}
4223
4224QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
4225{
4227
4228 return mTransactionGroups;
4229}
4230
4231
4232//
4233// QgsMapLayerStore methods
4234//
4235
4236
4238{
4240
4241 return mLayerStore->count();
4242}
4243
4245{
4247
4248 return mLayerStore->validCount();
4249}
4250
4251QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
4252{
4253 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
4255
4256 return mLayerStore->mapLayer( layerId );
4257}
4258
4259QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
4260{
4262
4263 return mLayerStore->mapLayersByName( layerName );
4264}
4265
4266QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
4267{
4269
4270 QList<QgsMapLayer *> layers;
4271 const auto constMapLayers { mLayerStore->mapLayers() };
4272 for ( const auto &l : constMapLayers )
4273 {
4274 if ( ! l->shortName().isEmpty() )
4275 {
4276 if ( l->shortName() == shortName )
4277 layers << l;
4278 }
4279 else if ( l->name() == shortName )
4280 {
4281 layers << l;
4282 }
4283 }
4284 return layers;
4285}
4286
4287bool QgsProject::unzip( const QString &filename, Qgis::ProjectReadFlags flags )
4288{
4290
4291 clearError();
4292 std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
4293
4294 // unzip the archive
4295 if ( !archive->unzip( filename ) )
4296 {
4297 setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
4298 return false;
4299 }
4300
4301 // test if zip provides a .qgs file
4302 if ( archive->projectFile().isEmpty() )
4303 {
4304 setError( tr( "Zip archive does not provide a project file" ) );
4305 return false;
4306 }
4307
4308 // Keep the archive
4309 releaseHandlesToProjectArchive();
4310 mArchive = std::move( archive );
4311
4312 // load auxiliary storage
4313 if ( !static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile().isEmpty() )
4314 {
4315 // database file is already a copy as it's been unzipped. So we don't open
4316 // auxiliary storage in copy mode in this case
4317 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile(), false ) );
4318 }
4319 else
4320 {
4321 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
4322 }
4323
4324 // read the project file
4325 if ( ! readProjectFile( static_cast<QgsProjectArchive *>( mArchive.get() )->projectFile(), flags ) )
4326 {
4327 setError( tr( "Cannot read unzipped qgs project file" ) + QStringLiteral( ": " ) + error() );
4328 return false;
4329 }
4330
4331 // Remove the temporary .qgs file
4332 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
4333
4334 return true;
4335}
4336
4337bool QgsProject::zip( const QString &filename )
4338{
4340
4341 clearError();
4342
4343 // save the current project in a temporary .qgs file
4344 std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
4345 const QString baseName = QFileInfo( filename ).baseName();
4346 const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
4347 QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
4348
4349 bool writeOk = false;
4350 if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
4351 {
4352 writeOk = writeProjectFile( qgsFile.fileName() );
4353 qgsFile.close();
4354 }
4355
4356 // stop here with an error message
4357 if ( ! writeOk )
4358 {
4359 setError( tr( "Unable to write temporary qgs file" ) );
4360 return false;
4361 }
4362
4363 // save auxiliary storage
4364 const QFileInfo info( qgsFile );
4365 const QString asExt = QStringLiteral( ".%1" ).arg( QgsAuxiliaryStorage::extension() );
4366 const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
4367
4368 bool auxiliaryStorageSavedOk = true;
4369 if ( ! saveAuxiliaryStorage( asFileName ) )
4370 {
4371 const QString err = mAuxiliaryStorage->errorString();
4372 setError( tr( "Unable to save auxiliary storage file ('%1'). The project has been saved but the latest changes to auxiliary data cannot be recovered. It is recommended to reload the project." ).arg( err ) );
4373 auxiliaryStorageSavedOk = false;
4374
4375 // fixes the current archive and keep the previous version of qgd
4376 if ( !mArchive->exists() )
4377 {
4378 releaseHandlesToProjectArchive();
4379 mArchive.reset( new QgsProjectArchive() );
4380 mArchive->unzip( mFile.fileName() );
4381 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
4382
4383 const QString auxiliaryStorageFile = static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile();
4384 if ( ! auxiliaryStorageFile.isEmpty() )
4385 {
4386 archive->addFile( auxiliaryStorageFile );
4387 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( auxiliaryStorageFile, false ) );
4388 }
4389 }
4390 }
4391 else
4392 {
4393 // in this case, an empty filename means that the auxiliary database is
4394 // empty, so we don't want to save it
4395 if ( QFile::exists( asFileName ) )
4396 {
4397 archive->addFile( asFileName );
4398 }
4399 }
4400
4401 // create the archive
4402 archive->addFile( qgsFile.fileName() );
4403
4404 // Add all other files
4405 const QStringList &files = mArchive->files();
4406 for ( const QString &file : files )
4407 {
4408 if ( !file.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
4409 {
4410 archive->addFile( file );
4411 }
4412 }
4413
4414 // zip
4415 bool zipOk = true;
4416 if ( !archive->zip( filename ) )
4417 {
4418 setError( tr( "Unable to perform zip" ) );
4419 zipOk = false;
4420 }
4421
4422 return auxiliaryStorageSavedOk && zipOk;
4423}
4424
4426{
4428
4429 return QgsZipUtils::isZipFile( mFile.fileName() );
4430}
4431
4432QList<QgsMapLayer *> QgsProject::addMapLayers(
4433 const QList<QgsMapLayer *> &layers,
4434 bool addToLegend,
4435 bool takeOwnership )
4436{
4438
4439 const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
4440 if ( !myResultList.isEmpty() )
4441 {
4442 // Update transform context
4443 for ( auto &l : myResultList )
4444 {
4445 l->setTransformContext( transformContext() );
4446 }
4447 if ( addToLegend )
4448 {
4449 emit legendLayersAdded( myResultList );
4450 }
4451 }
4452
4453 if ( mAuxiliaryStorage )
4454 {
4455 for ( QgsMapLayer *mlayer : myResultList )
4456 {
4457 if ( mlayer->type() != Qgis::LayerType::Vector )
4458 continue;
4459
4460 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
4461 if ( vl )
4462 {
4463 vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
4464 }
4465 }
4466 }
4467
4468 mProjectScope.reset();
4469
4470 return myResultList;
4471}
4472
4475 bool addToLegend,
4476 bool takeOwnership )
4477{
4479
4480 QList<QgsMapLayer *> addedLayers;
4481 addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
4482 return addedLayers.isEmpty() ? nullptr : addedLayers[0];
4483}
4484
4485void QgsProject::removeAuxiliaryLayer( const QgsMapLayer *ml )
4486{
4488
4489 if ( ! ml || ml->type() != Qgis::LayerType::Vector )
4490 return;
4491
4492 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( ml );
4493 if ( vl && vl->auxiliaryLayer() )
4494 {
4495 const QgsDataSourceUri uri( vl->auxiliaryLayer()->source() );
4497 }
4498}
4499
4500void QgsProject::removeMapLayers( const QStringList &layerIds )
4501{
4503
4504 for ( const auto &layerId : layerIds )
4505 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4506
4507 mProjectScope.reset();
4508 mLayerStore->removeMapLayers( layerIds );
4509}
4510
4511void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
4512{
4514
4515 for ( const auto &layer : layers )
4516 removeAuxiliaryLayer( layer );
4517
4518 mProjectScope.reset();
4519 mLayerStore->removeMapLayers( layers );
4520}
4521
4522void QgsProject::removeMapLayer( const QString &layerId )
4523{
4525
4526 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4527 mProjectScope.reset();
4528 mLayerStore->removeMapLayer( layerId );
4529}
4530
4532{
4534
4535 removeAuxiliaryLayer( layer );
4536 mProjectScope.reset();
4537 mLayerStore->removeMapLayer( layer );
4538}
4539
4541{
4543
4544 mProjectScope.reset();
4545 return mLayerStore->takeMapLayer( layer );
4546}
4547
4549{
4551
4552 return mMainAnnotationLayer;
4553}
4554
4556{
4558
4559 if ( mLayerStore->count() == 0 )
4560 return;
4561
4562 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
4563 mProjectScope.reset();
4564 mLayerStore->removeAllMapLayers();
4565
4566 snapSingleBlocker.release();
4567 mSnappingConfig.clearIndividualLayerSettings();
4568 if ( !mBlockSnappingUpdates )
4569 emit snappingConfigChanged( mSnappingConfig );
4570}
4571
4573{
4575
4576 const QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
4577 QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
4578 for ( ; it != layers.constEnd(); ++it )
4579 {
4580 it.value()->reload();
4581 }
4582}
4583
4584QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
4585{
4586 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
4588
4589 return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
4590}
4591
4592QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
4593{
4595
4596 return mTransactionGroups.value( qMakePair( providerKey, connString ) );
4597}
4598
4605
4607{
4609
4611
4612 // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
4613 // in the meantime, we have a slightly hacky way to read the settings key using an enum which isn't available (since it lives in app)
4614 if ( mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
4615 || mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == QLatin1String( "2" ) )
4616 {
4617 // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
4618 defaultCrs = crs();
4619 }
4620 else
4621 {
4622 // global crs
4623 const QString layerDefaultCrs = mSettings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
4624 defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
4625 }
4626
4627 return defaultCrs;
4628}
4629
4636
4643
4644bool QgsProject::saveAuxiliaryStorage( const QString &filename )
4645{
4647
4648 const QMap<QString, QgsMapLayer *> layers = mapLayers();
4649 bool empty = true;
4650 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4651 {
4652 if ( it.value()->type() != Qgis::LayerType::Vector )
4653 continue;
4654
4655 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
4656 if ( vl && vl->auxiliaryLayer() )
4657 {
4658 vl->auxiliaryLayer()->save();
4659 empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
4660 }
4661 }
4662
4663 if ( !mAuxiliaryStorage->exists( *this ) && empty )
4664 {
4665 return true; // it's not an error
4666 }
4667 else if ( !filename.isEmpty() )
4668 {
4669 return mAuxiliaryStorage->saveAs( filename );
4670 }
4671 else
4672 {
4673 return mAuxiliaryStorage->saveAs( *this );
4674 }
4675}
4676
4677QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
4678{
4679 static QgsPropertiesDefinition sPropertyDefinitions
4680 {
4681 {
4683 QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
4684 },
4685 };
4686 return sPropertyDefinitions;
4687}
4688
4690{
4691 mElevationShadingRenderer = elevationShadingRenderer;
4693}
4694
4696{
4698
4699 return mAuxiliaryStorage.get();
4700}
4701
4703{
4705
4706 return mAuxiliaryStorage.get();
4707}
4708
4709QString QgsProject::createAttachedFile( const QString &nameTemplate )
4710{
4712
4713 const QDir archiveDir( mArchive->dir() );
4714 QTemporaryFile tmpFile( archiveDir.filePath( "XXXXXX_" + nameTemplate ), this );
4715 tmpFile.setAutoRemove( false );
4716 tmpFile.open();
4717 mArchive->addFile( tmpFile.fileName() );
4718 return tmpFile.fileName();
4719}
4720
4721QStringList QgsProject::attachedFiles() const
4722{
4724
4725 QStringList attachments;
4726 const QString baseName = QFileInfo( fileName() ).baseName();
4727 const QStringList files = mArchive->files();
4728 attachments.reserve( files.size() );
4729 for ( const QString &file : files )
4730 {
4731 if ( QFileInfo( file ).baseName() != baseName )
4732 {
4733 attachments.append( file );
4734 }
4735 }
4736 return attachments;
4737}
4738
4739bool QgsProject::removeAttachedFile( const QString &path )
4740{
4742
4743 return mArchive->removeFile( path );
4744}
4745
4746QString QgsProject::attachmentIdentifier( const QString &attachedFile ) const
4747{
4749
4750 return QStringLiteral( "attachment:///%1" ).arg( QFileInfo( attachedFile ).fileName() );
4751}
4752
4753QString QgsProject::resolveAttachmentIdentifier( const QString &identifier ) const
4754{
4756
4757 if ( identifier.startsWith( QLatin1String( "attachment:///" ) ) )
4758 {
4759 return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
4760 }
4761 return QString();
4762}
4763
4765{
4766 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
4768
4769 return mMetadata;
4770}
4771
4773{
4775
4776 if ( metadata == mMetadata )
4777 return;
4778
4779 mMetadata = metadata;
4780 mProjectScope.reset();
4781
4782 emit metadataChanged();
4783
4784 setDirty( true );
4785}
4786
4787QSet<QgsMapLayer *> QgsProject::requiredLayers() const
4788{
4790
4791 QSet<QgsMapLayer *> requiredLayers;
4792
4793 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4794 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4795 {
4796 if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4797 {
4798 requiredLayers.insert( it.value() );
4799 }
4800 }
4801 return requiredLayers;
4802}
4803
4804void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
4805{
4807
4808 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4809 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4810 {
4811 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4812 continue;
4813
4814 if ( layers.contains( it.value() ) )
4815 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
4816 else
4817 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
4818 }
4819}
4820
4822{
4824
4825 // save colors to project
4826 QStringList customColors;
4827 QStringList customColorLabels;
4828
4829 QgsNamedColorList::const_iterator colorIt = colors.constBegin();
4830 for ( ; colorIt != colors.constEnd(); ++colorIt )
4831 {
4832 const QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
4833 const QString label = ( *colorIt ).second;
4834 customColors.append( color );
4835 customColorLabels.append( label );
4836 }
4837 writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
4838 writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
4839 mProjectScope.reset();
4840 emit projectColorsChanged();
4841}
4842
4843void QgsProject::setBackgroundColor( const QColor &color )
4844{
4846
4847 if ( mBackgroundColor == color )
4848 return;
4849
4850 mBackgroundColor = color;
4852}
4853
4855{
4857
4858 return mBackgroundColor;
4859}
4860
4861void QgsProject::setSelectionColor( const QColor &color )
4862{
4864
4865 if ( mSelectionColor == color )
4866 return;
4867
4868 mSelectionColor = color;
4869 emit selectionColorChanged();
4870}
4871
4873{
4875
4876 return mSelectionColor;
4877}
4878
4879void QgsProject::setMapScales( const QVector<double> &scales )
4880{
4882
4883 mViewSettings->setMapScales( scales );
4884}
4885
4886QVector<double> QgsProject::mapScales() const
4887{
4889
4890 return mViewSettings->mapScales();
4891}
4892
4894{
4896
4897 mViewSettings->setUseProjectScales( enabled );
4898}
4899
4901{
4903
4904 return mViewSettings->useProjectScales();
4905}
4906
4907void QgsProject::generateTsFile( const QString &locale )
4908{
4910
4911 QgsTranslationContext translationContext;
4912 translationContext.setProject( this );
4913 translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
4914
4915 QgsApplication::instance()->collectTranslatableObjects( &translationContext );
4916
4917 translationContext.writeTsFile( locale );
4918}
4919
4920QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
4921{
4923
4924 if ( !mTranslator )
4925 {
4926 return sourceText;
4927 }
4928
4929 QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
4930
4931 if ( result.isEmpty() )
4932 {
4933 return sourceText;
4934 }
4935 return result;
4936}
4937
4939{
4941
4942 const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
4943 if ( !layers.empty() )
4944 {
4945 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4946 {
4947 // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
4948 if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4949 {
4950 if ( !( ( *it )->accept( visitor ) ) )
4951 return false;
4952
4953 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4954 return false;
4955 }
4956 }
4957 }
4958
4959 if ( !mLayoutManager->accept( visitor ) )
4960 return false;
4961
4962 if ( !mAnnotationManager->accept( visitor ) )
4963 return false;
4964
4965 return true;
4966}
4967
4969{
4970 return mElevationShadingRenderer;
4971}
4972
4973void QgsProject::loadProjectFlags( const QDomDocument *doc )
4974{
4976
4977 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectFlags" ) );
4978 Qgis::ProjectFlags flags;
4979 if ( !element.isNull() )
4980 {
4981 flags = qgsFlagKeysToValue( element.attribute( QStringLiteral( "set" ) ), Qgis::ProjectFlags() );
4982 }
4983 else
4984 {
4985 // older project compatibility
4986 element = doc->documentElement().firstChildElement( QStringLiteral( "evaluateDefaultValues" ) );
4987 if ( !element.isNull() )
4988 {
4989 if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
4991 }
4992
4993 // Read trust layer metadata config in the project
4994 element = doc->documentElement().firstChildElement( QStringLiteral( "trust" ) );
4995 if ( !element.isNull() )
4996 {
4997 if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
4999 }
5000 }
5001
5002 setFlags( flags );
5003}
5004
5006GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
5007 : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
5008{
5009 if ( !project )
5010 return;
5011
5012 //build up color list from project. Do this in advance for speed
5013 QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
5014 const QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
5015
5016 //generate list from custom colors
5017 int colorIndex = 0;
5018 for ( QStringList::iterator it = colorStrings.begin();
5019 it != colorStrings.end(); ++it )
5020 {
5021 const QColor color = QgsSymbolLayerUtils::decodeColor( *it );
5022 QString label;
5023 if ( colorLabels.length() > colorIndex )
5024 {
5025 label = colorLabels.at( colorIndex );
5026 }
5027
5028 mColors.insert( label.toLower(), color );
5029 colorIndex++;
5030 }
5031}
5032
5033GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
5034 : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
5035 , mColors( colors )
5036{
5037}
5038
5039QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5040{
5041 const QString colorName = values.at( 0 ).toString().toLower();
5042 if ( mColors.contains( colorName ) )
5043 {
5044 return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
5045 }
5046 else
5047 return QVariant();
5048}
5049
5050QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
5051{
5052 return new GetNamedProjectColor( mColors );
5053}
5054
5055// ----------------
5056
5057GetSensorData::GetSensorData( const QMap<QString, QgsAbstractSensor::SensorData> &sensorData )
5058 : QgsScopedExpressionFunction( QStringLiteral( "sensor_data" ),
5059 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( QStringLiteral( "name" ) ) << QgsExpressionFunction::Parameter( QStringLiteral( "expiration" ), true, 0 ),
5060 QStringLiteral( "Sensors" ) )
5061 , mSensorData( sensorData )
5062{
5063}
5064
5065QVariant GetSensorData::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5066{
5067 const QString sensorName = values.at( 0 ).toString();
5068 const int expiration = values.at( 1 ).toInt();
5069 const qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
5070 if ( mSensorData.contains( sensorName ) )
5071 {
5072 if ( expiration <= 0 || ( timestamp - mSensorData[sensorName].lastTimestamp.toMSecsSinceEpoch() ) < expiration )
5073 {
5074 return mSensorData[sensorName].lastValue;
5075 }
5076 }
5077
5078 return QVariant();
5079}
5080
5081QgsScopedExpressionFunction *GetSensorData::clone() const
5082{
5083 return new GetSensorData( mSensorData );
5084}
@ DontLoad3DViews
Skip loading 3D views (since QGIS 3.26)
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
@ ForceReadOnlyLayers
Open layers in a read-only mode. (since QGIS 3.28)
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
@ DontResolveLayers
Don't resolve layer paths (i.e. don't load any layer content). Dramatically improves project read tim...
static QString version()
Version string.
Definition qgis.cpp:258
DistanceUnit
Units of distance.
Definition qgis.h:3496
FilePathType
File path types.
Definition qgis.h:1185
@ Relative
Relative path.
@ Absolute
Absolute path.
TransactionMode
Transaction mode.
Definition qgis.h:2675
@ AutomaticGroups
Automatic transactional editing means that on supported datasources (postgres and geopackage database...
@ BufferedGroups
Buffered transactional editing means that all editable layers in the buffered transaction group are t...
@ Disabled
Edits are buffered locally and sent to the provider when toggling layer editing mode.
AreaUnit
Units of area.
Definition qgis.h:3534
@ SquareMeters
Square meters.
@ Critical
Critical/error message.
Definition qgis.h:102
@ Success
Used for reporting a successful operation.
Definition qgis.h:103
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
Definition qgis.h:2903
@ AvoidIntersectionsLayers
Overlap with features from a specified list of layers when digitizing new features not allowed.
@ AllowIntersections
Overlap with any feature allowed when digitizing new features.
ProjectFlag
Flags which control the behavior of QgsProjects.
Definition qgis.h:2757
@ RememberLayerEditStatusBetweenSessions
If set, then any layers set to be editable will be stored in the project and immediately made editabl...
@ EvaluateDefaultValuesOnProviderSide
If set, default values for fields will be evaluated on the provider side when features from the proje...
@ TrustStoredLayerStatistics
If set, then layer statistics (such as the layer extent) will be read from values stored in the proje...
LayerType
Types of layers that can be added to a map.
Definition qgis.h:114
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ Vector
Vector layer.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ Raster
Raster layer.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
@ Marker
Marker symbol.
@ Line
Line symbol.
@ Fill
Fill symbol.
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
void setTitle(const QString &title)
Sets the human readable title (name) of the resource, typically displayed in search results.
QString title() const
Returns the human readable name of the resource, typically displayed in search results.
QgsAbstractMetadataBase::KeywordMap keywords() const
Returns the keywords map, which is a set of descriptive keywords associated with the resource.
virtual bool readXml(const QDomElement &collectionElem, const QgsPropertiesDefinition &definitions)
Reads property collection state from an XML element.
virtual bool writeXml(QDomElement &collectionElem, const QgsPropertiesDefinition &definitions) const
Writes the current state of the property collection into an XML element.
Represents a map layer containing a set of georeferenced annotations, e.g.
void setTransformContext(const QgsCoordinateTransformContext &context) override
Sets the coordinate transform context to transformContext.
void reset()
Resets the annotation layer to a default state, and clears all items from it.
bool isEmpty() const
Returns true if the annotation layer is empty and contains no annotations.
Manages storage of a set of QgsAnnotation annotation objects.
static QgsApplication * instance()
Returns the singleton instance of the QgsApplication.
static QgsProjectStorageRegistry * projectStorageRegistry()
Returns registry of available project storage implementations.
static const QgsSettingsEntryString * settingsLocaleUserLocale
Settings entry locale user locale.
static QgsRuntimeProfiler * profiler()
Returns the application runtime profiler.
void collectTranslatableObjects(QgsTranslationContext *translationContext)
Emits the signal to collect all the strings of .qgs to be included in ts file.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
void requestForTranslatableObjects(QgsTranslationContext *translationContext)
Emitted when project strings which require translation are being collected for inclusion in a ....
static QString userFullName()
Returns the user's operating system login account full display name.
static QString userLoginName()
Returns the user's operating system login account name.
Class allowing to manage the zip/unzip actions.
Definition qgsarchive.h:36
void addFile(const QString &filename)
Add a new file to this archive.
This is a container for attribute editors, used to group them visually in the attribute form if it is...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
This is an abstract base class for any elements of a drag and drop form.
QString name() const
Returns the name of this element.
QgsFields auxiliaryFields() const
Returns a list of all auxiliary fields currently managed by the layer.
bool save()
Commits changes and starts editing then.
Class providing some utility methods to manage auxiliary storage.
static QString extension()
Returns the extension used for auxiliary databases.
static bool deleteTable(const QgsDataSourceUri &uri)
Removes a table from the auxiliary storage.
Manages storage of a set of bookmarks.
bool readXml(const QDomElement &element, const QDomDocument &doc)
Reads the manager's state from a DOM element, restoring all bookmarks present in the XML document.
void clear()
Removes and deletes all bookmarks from the manager.
QDomElement writeXml(QDomDocument &doc) const
Returns a DOM element representing the state of the manager.
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
QString toProj() const
Returns a Proj string representation of this CRS.
bool readXml(const QDomNode &node)
Restores state from the given DOM node.
QString ellipsoidAcronym() const
Returns the ellipsoid acronym for the ellipsoid used by the CRS.
QString projectionAcronym() const
Returns the projection acronym for the projection used by the CRS.
static QgsCoordinateReferenceSystem fromProj(const QString &proj)
Creates a CRS from a proj style formatted string.
@ WKT_PREFERRED
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
bool writeXml(QDomNode &node, QDomDocument &doc) const
Stores state to the given Dom node in the given document.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
QString toWkt(WktVariant variant=WKT1_GDAL, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
Contains information about the context in which a coordinate transform is executed.
void readSettings()
Reads the context's state from application settings.
void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes the context's state to a DOM element.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, QStringList &missingTransforms)
Reads the context's state from a DOM element.
Abstract base class for spatial data provider implementations.
@ ParallelThreadLoading
Skip credentials if the provided one are not valid, let the provider be invalid, avoiding to block th...
@ EvaluateDefaultValues
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
Class for storing the component parts of a RDBMS data source URI (e.g.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
This class can render elevation shading on an image with different methods (eye dome lighting,...
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const
Writes configuration on a DOM element.
void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads configuration from a DOM element.
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
A abstract base class for defining QgsExpression functions.
An expression node for expression functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
Container of fields for a vector layer.
Definition qgsfields.h:45
bool isEmpty() const
Checks whether the container is empty.
Stores global configuration for labeling engine.
Class used to work with layer dependencies stored in a XML project or layer definition file.
Layer tree group node serves as a container for layers and further groups.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
QList< QgsLayerTreeGroup * > findGroups(bool recursive=false) const
Find group layer nodes.
QString name() const override
Returns the group's name.
QStringList findLayerIds() const
Find layer IDs used in all layer nodes.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
QgsLayerTreeLayer * findLayer(QgsMapLayer *layer) const
Find layer node representing the map layer.
Layer tree node points to a map layer.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Resolves reference to layer from stored layer ID (if it has not been resolved already)
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the childrens, disconnect all the forwarded and external signals and sets their parent to nul...
void setCustomProperty(const QString &key, const QVariant &value)
Sets a custom property for the node. Properties are stored in a map and saved in project file.
virtual void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context)=0
Write layer tree to XML.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
void removeCustomProperty(const QString &key)
Remove a custom property from layer. Properties are stored in a map and saved in project file.
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.
void setItemVisibilityChecked(bool checked)
Check or uncheck a node (independently of its ancestors or children)
Listens to the updates in map layer registry and does changes in layer tree.
static void replaceChildrenOfEmbeddedGroups(QgsLayerTreeGroup *group)
Remove subtree of embedded groups and replaces it with a custom property embedded-visible-layers.
static void storeOriginalLayersProperties(QgsLayerTreeGroup *group, const QDomDocument *doc)
Stores in a layer's originalXmlProperties the layer properties information.
static void updateEmbeddedGroupsProjectPath(QgsLayerTreeGroup *group, const QgsProject *project)
Updates an embedded group from a project.
static bool readOldLegend(QgsLayerTreeGroup *root, const QDomElement &legendElem)
Try to load layer tree from.
Namespace with helper functions for layer tree operations.
void readLayerOrderFromXml(const QDomElement &doc)
Load the layer order from an XML element.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
void clear()
Clear any information from this layer tree.
static bool isLayer(const QgsLayerTreeNode *node)
Check whether the node is a valid layer node.
static bool isGroup(QgsLayerTreeNode *node)
Check whether the node is a valid group node.
static QgsLayerTreeGroup * toGroup(QgsLayerTreeNode *node)
Cast node to a group.
QList< QgsMapLayer * > customLayerOrder() const
The order in which layers will be rendered on the canvas.
QgsLayerTree * clone() const override
Create a copy of the node. Returns new instance.
Manages storage of a set of layouts.
static void warning(const QString &msg)
Goes to qWarning.
static Qgis::LayerType typeFromString(const QString &string, bool &ok)
Returns the map layer type corresponding a string value.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the store.
void layerWillBeRemoved(const QString &layerId)
Emitted when a layer is about to be removed from the store.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the store.
void allLayersRemoved()
Emitted when all layers are removed, before layersWillBeRemoved() and layerWillBeRemoved() signals ar...
void layerRemoved(const QString &layerId)
Emitted after a layer was removed from the store.
void layerWasAdded(QgsMapLayer *layer)
Emitted when a layer was added to the store.
QgsMapLayer * mapLayer(const QString &id) const
Retrieve a pointer to a layer by layer id.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the store.
Base class for all map layer types.
Definition qgsmaplayer.h:74
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
void configChanged()
Emitted whenever the configuration is changed.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:80
QString id() const
Returns the layer's unique ID, which is used to access this layer from QgsProject.
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
Qgis::LayerType type
Definition qgsmaplayer.h:81
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
virtual bool isEditable() const
Returns true if the layer can be edited.
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
@ Identifiable
If the layer is identifiable using the identify map tool and as a WMS layer.
@ Removable
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
@ FlagForceReadOnly
Force open as read only.
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags(), QgsDataProvider *preloadedProvider=nullptr)
Sets state from DOM document.
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
static QgsDataProvider::ReadFlags providerReadFlags(const QDomNode &layerNode, QgsMapLayer::ReadFlags layerReadFlags)
Returns provider read flag deduced from layer read flags layerReadFlags and a dom node layerNode that...
Container class that allows storage of map themes consisting of visible map layers and layer styles.
Manages storage of a set of views.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Resolves relative paths into absolute paths and vice versa.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
QString readPath(const QString &filename) const
Turn filename read from the project file to an absolute path.
Class allowing to manage the zip/unzip actions on project file.
Definition qgsarchive.h:122
QString projectFile() const
Returns the current .qgs project file or an empty string if there's none.
QString auxiliaryStorageFile() const
Returns the current .qgd auxiliary storage file or an empty string if there's none.
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Interface for classes that handle missing layer files when reading project file.
virtual void handleBadLayers(const QList< QDomNode > &layers)
This method will be called whenever the project tries to load layers which cannot be accessed.
Contains settings and properties relating to how a QgsProject should display values such as map coord...
void reset()
Resets the settings to a default state.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
Contains elevation properties for a QgsProject.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the property state from a DOM element.
void reset()
Resets the properties to a default state.
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Returns a DOM element representing the properties.
void resolveReferences(const QgsProject *project)
Resolves reference to layers from stored layer ID.
Class to convert from older project file versions to newer.
static Q_DECL_DEPRECATED void fixOldSymbolLayerReferences(const QMap< QString, QgsMapLayer * > &mapLayers)
QgsSymbolLayerReference uses QgsSymbolLayer unique uuid identifier since QGIS 3.30,...
Contains settings and properties relating to how a QgsProject should interact with a GPS device.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void reset()
Resets the settings to a default state.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
void resolveReferences(const QgsProject *project)
Resolves reference to layers from stored layer ID (if it has not been resolved already)
A structured metadata store for a map layer.
bool writeMetadataXml(QDomElement &metadataElement, QDomDocument &document) const override
Stores state in a DOM node.
void setCreationDateTime(const QDateTime &creationDateTime)
Sets the project's creation date/timestamp.
bool readMetadataXml(const QDomElement &metadataElement) override
Sets state from DOM document.
void setAuthor(const QString &author)
Sets the project author string.
Project property key node.
QString name() const
The name of the property is used as identifier.
QgsProjectProperty * find(const QString &propertyName) const
Attempts to find a property with a matching sub-key name.
void removeKey(const QString &keyName)
Removes the specified key.
void dump(int tabs=0) const override
Dumps out the keys and values.
bool isEmpty() const
Returns true if this property contains no sub-keys.
virtual void clearKeys()
Deletes any sub-nodes from the property.
bool writeXml(const QString &nodeName, QDomElement &element, QDomDocument &document) override
Writes the property hierarchy to a specified DOM element.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
void setName(const QString &name)
The name of the property is used as identifier.
QgsProjectPropertyKey * addKey(const QString &keyName)
Adds the specified property key as a sub-key.
QVariant value() const override
If this key has a value, it will be stored by its name in its properties.
QgsProjectPropertyValue * setValue(const QString &name, const QVariant &value)
Sets the value associated with this key.
void entryList(QStringList &entries) const
Returns any sub-keys contained by this property that do not contain other keys.
int count() const
Returns the number of sub-keys contained by this property.
bool readXml(const QDomNode &keyNode) override
Restores the property hierarchy from a specified DOM node.
An Abstract Base Class for QGIS project property hierarchys.
virtual bool isKey() const =0
Returns true if the property is a QgsProjectPropertyKey.
virtual bool isValue() const =0
Returns true if the property is a QgsProjectPropertyValue.
QgsProjectStorage * projectStorageFromUri(const QString &uri)
Returns storage implementation if the URI matches one. Returns nullptr otherwise (it is a normal file...
Metadata associated with a project.
Abstract interface for project storage - to be implemented by various backends and registered in QgsP...
Contains settings and properties relating to how a QgsProject should handle styling.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
void setDefaultSymbol(Qgis::SymbolType symbolType, QgsSymbol *symbol)
Sets the project default symbol for a given type.
void reset()
Resets the settings to a default state.
void removeProjectStyle()
Removes and deletes the project style database.
void setRandomizeDefaultSymbolColor(bool randomized)
Sets whether the default symbol fill color is randomized.
void setDefaultColorRamp(QgsColorRamp *colorRamp)
Sets the project default color ramp.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Reads the settings's state from a DOM element.
void setDefaultSymbolOpacity(double opacity)
Sets the default symbol opacity.
Contains temporal settings and properties for the project, this may be used when animating maps or sh...
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void reset()
Resets the settings to a default state.
QDomElement writeXml(QDomDocument &document, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
A class to describe the version of a project.
QString text() const
Returns a string representation of the version.
int majorVersion() const
Returns the major version number.
Contains settings and properties relating to how a QgsProject should be displayed inside map canvas,...
bool useProjectScales() const
Returns true if project mapScales() are enabled.
void reset()
Resets the settings to a default state.
bool readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads the settings's state from a DOM element.
void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
QVector< double > mapScales() const
Returns the list of custom project map scales.
void mapScalesChanged()
Emitted when the list of custom project map scales changes.
QDomElement writeXml(QDomDocument &doc, const QgsReadWriteContext &context) const
Returns a DOM element representing the settings.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:107
bool isZipped() const
Returns true if the project comes from a zip archive, false otherwise.
bool removeAttachedFile(const QString &path)
Removes the attached file.
QgsRelationManager * relationManager
Definition qgsproject.h:117
bool write()
Writes the project to its current associated file (see fileName() ).
QgsProject(QObject *parent=nullptr, Qgis::ProjectCapabilities capabilities=Qgis::ProjectCapability::ProjectStyles)
Create a new QgsProject.
void removeMapLayer(const QString &layerId)
Remove a layer from the registry by layer ID.
Q_DECL_DEPRECATED bool evaluateDefaultValues() const
Should default values be evaluated on provider side when requested and not when committed.
Qgis::DistanceUnit distanceUnits
Definition qgsproject.h:124
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
void clear()
Clears the project, removing all settings and resetting it back to an empty, default state.
~QgsProject() override
QString error() const
Returns error message from previous read/write.
Q_DECL_DEPRECATED void setUseProjectScales(bool enabled)
Sets whether project mapScales() are enabled.
int readNumEntry(const QString &scope, const QString &key, int def=0, bool *ok=nullptr) const
Reads an integer from the specified scope and key.
Q_DECL_DEPRECATED void setNonIdentifiableLayers(const QList< QgsMapLayer * > &layers)
Set a list of layers which should not be taken into account on map identification.
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
Qgis::ProjectFlags flags() const
Returns the project's flags, which dictate the behavior of the project.
Definition qgsproject.h:205
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project's associated file.
QString presetHomePath() const
Returns any manual project home path setting, or an empty string if not set.
void setBackgroundColor(const QColor &color)
Sets the default background color used by default map canvases.
void setCrs(const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid=false)
Sets the project's native coordinate reference system.
QColor selectionColor
Definition qgsproject.h:122
QString title() const
Returns the project's title.
bool commitChanges(QStringList &commitErrors, bool stopEditing=true, QgsVectorLayer *vectorLayer=nullptr)
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Qgis::FilePathType filePathStorage() const
Returns the type of paths used when storing file paths in a QGS/QGZ project file.
QString createAttachedFile(const QString &nameTemplate)
Attaches a file to the project.
Q_DECL_DEPRECATED void mapScalesChanged()
Emitted when the list of custom project map scales changes.
void readVersionMismatchOccurred(const QString &fileVersion)
Emitted when a project is read and the version of QGIS used to save the project differs from the curr...
QString ellipsoid
Definition qgsproject.h:114
void fileNameChanged()
Emitted when the file name of the project changes.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted when a layer is being saved.
const QgsSensorManager * sensorManager() const
Returns the project's sensor manager, which manages sensors within the project.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
void areaUnitsChanged()
Emitted when the default area units changes.
QgsPropertyCollection dataDefinedServerProperties() const
Returns the data defined properties used for overrides in user defined server parameters.
Q_DECL_DEPRECATED void nonIdentifiableLayersChanged(QStringList nonIdentifiableLayers)
Emitted when the list of layer which are excluded from map identification changes.
void layersWillBeRemoved(const QStringList &layerIds)
Emitted when one or more layers are about to be removed from the registry.
QString attachmentIdentifier(const QString &attachedFile) const
Returns an identifier for an attachment file path An attachment identifier is a string which does not...
QgsVectorLayerEditBufferGroup * editBufferGroup()
Returns the edit buffer group.
void setSelectionColor(const QColor &color)
Sets the color used to highlight selected features.
bool rollBack(QStringList &rollbackErrors, bool stopEditing=true, QgsVectorLayer *vectorLayer=nullptr)
Stops a current editing operation on vectorLayer and discards any uncommitted edits.
void snappingConfigChanged(const QgsSnappingConfig &config)
Emitted whenever the configuration for snapping has changed.
QgsPathResolver pathResolver() const
Returns path resolver object with considering whether the project uses absolute or relative paths and...
void setBadLayerHandler(QgsProjectBadLayerHandler *handler)
Change handler for missing layers.
Q_DECL_DEPRECATED void setEvaluateDefaultValues(bool evaluateDefaultValues)
Defines if default values should be evaluated on provider side when requested and not when committed.
Qgis::AreaUnit areaUnits
Definition qgsproject.h:125
void crsChanged()
Emitted when the CRS of the project has changed.
QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const override
Translates the project with QTranslator and qm file.
const QgsProjectStyleSettings * styleSettings() const
Returns the project's style settings, which contains settings and properties relating to how a QgsPro...
QgsSnappingConfig snappingConfig
Definition qgsproject.h:116
const QgsProjectGpsSettings * gpsSettings() const
Returns the project's GPS settings, which contains settings and properties relating to how a QgsProje...
void setFileName(const QString &name)
Sets the file name associated with the project.
void avoidIntersectionsLayersChanged()
Emitted whenever avoidIntersectionsLayers has changed.
void setDataDefinedServerProperties(const QgsPropertyCollection &properties)
Sets the data defined properties used for overrides in user defined server parameters to properties.
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
void distanceUnitsChanged()
Emitted when the default distance units changes.
QgsAnnotationLayer * mainAnnotationLayer()
Returns the main annotation layer associated with the project.
const QgsBookmarkManager * bookmarkManager() const
Returns the project's bookmark manager, which manages bookmarks within the project.
void readMapLayer(QgsMapLayer *mapLayer, const QDomElement &layerNode)
Emitted after the basic initialization of a layer from the project file is done.
Q_DECL_DEPRECATED void setAutoTransaction(bool autoTransaction)
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
bool startEditing(QgsVectorLayer *vectorLayer=nullptr)
Makes the layer editable.
void aboutToBeCleared()
Emitted when the project is about to be cleared.
Q_DECL_DEPRECATED void setTrustLayerMetadata(bool trust)
Sets the trust option allowing to indicate if the extent has to be read from the XML document when da...
void cleared()
Emitted when the project is cleared (and additionally when an open project is cleared just before a n...
@ WMSOnlineResource
Alias.
Definition qgsproject.h:142
Q_DECL_DEPRECATED void oldProjectVersionWarning(const QString &)
Emitted when an old project file is read.
void setLabelingEngineSettings(const QgsLabelingEngineSettings &settings)
Sets project's global labeling engine settings.
QgsExpressionContext createExpressionContext() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
void metadataChanged()
Emitted when the project's metadata is changed.
QString resolveAttachmentIdentifier(const QString &identifier) const
Resolves an attachment identifier to a attachment file path.
const QgsProjectElevationProperties * elevationProperties() const
Returns the project's elevation properties, which contains the project's elevation related settings.
QString absolutePath() const
Returns full absolute path to the project folder if the project is stored in a file system - derived ...
void removeMapLayers(const QStringList &layerIds)
Remove a set of layers from the registry by layer ID.
Q_DECL_DEPRECATED void setRequiredLayers(const QSet< QgsMapLayer * > &layers)
Configures a set of map layers that are required in the project and therefore they should not get rem...
bool createEmbeddedLayer(const QString &layerId, const QString &projectFilePath, QList< QDomNode > &brokenNodes, bool saveFlag=true, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Creates a maplayer instance defined in an arbitrary project file.
QList< QgsVectorLayer * > avoidIntersectionsLayers
Definition qgsproject.h:119
QString readEntry(const QString &scope, const QString &key, const QString &def=QString(), bool *ok=nullptr) const
Reads a string from the specified scope and key.
QgsExpressionContextScope * createExpressionContextScope() const override
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QString baseName() const
Returns the base name of the project file without the path and without extension - derived from fileN...
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsMapThemeCollection * mapThemeCollection
Definition qgsproject.h:115
void generateTsFile(const QString &locale)
Triggers the collection strings of .qgs to be included in ts file and calls writeTsFile()
QStringList entryList(const QString &scope, const QString &key) const
Returns a list of child keys with values which exist within the the specified scope and key.
QgsAnnotationManager * annotationManager()
Returns pointer to the project's annotation manager.
QgsProjectDisplaySettings * displaySettings
Definition qgsproject.h:126
Qgis::TransactionMode transactionMode() const
Returns the transaction mode.
QgsProjectMetadata metadata
Definition qgsproject.h:120
void projectColorsChanged()
Emitted whenever the project's color scheme has been changed.
QString saveUser() const
Returns the user name that did the last save.
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
void setProjectColors(const QgsNamedColorList &colors)
Sets the colors for the project's color scheme (see QgsProjectColorScheme).
bool setTransactionMode(Qgis::TransactionMode transactionMode)
Set transaction mode.
QgsCoordinateTransformContext transformContext
Definition qgsproject.h:113
void labelingEngineSettingsChanged()
Emitted when global configuration of the labeling engine changes.
void customVariablesChanged()
Emitted whenever the expression variables stored in the project have been changed.
QgsLayerTree * layerTreeRoot() const
Returns pointer to the root (invisible) node of the project's layer tree.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Reads a boolean from the specified scope and key.
QgsMapLayerStore * layerStore()
Returns a pointer to the project's internal layer store.
void readProject(const QDomDocument &)
Emitted when a project is being read.
QString originalPath() const
Returns the original path associated with the project.
void setOriginalPath(const QString &path)
Sets the original path associated with the project.
void dumpProperties() const
Dump out current project properties to stderr.
QgsElevationShadingRenderer elevationShadingRenderer() const
Returns the elevation shading renderer used for map shading.
const QgsMapViewsManager * viewsManager() const
Returns the project's views manager, which manages map views (including 3d maps) in the project.
static void setInstance(QgsProject *project)
Set the current project singleton instance to project.
int validCount() const
Returns the number of registered valid layers.
const QgsLayoutManager * layoutManager() const
Returns the project's layout manager, which manages print layouts, atlases and reports within the pro...
void elevationShadingRendererChanged()
Emitted when the map shading renderer changes.
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QString fileName
Definition qgsproject.h:110
Q_DECL_DEPRECATED bool autoTransaction() const
Transactional editing means that on supported datasources (postgres databases) the edit state of all ...
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified style entity visitor, causing it to visit all style entities associated with th...
QStringList attachedFiles() const
Returns a map of all attached files with identifier and real paths.
void setMetadata(const QgsProjectMetadata &metadata)
Sets the project's metadata store.
void missingDatumTransforms(const QStringList &missingTransforms)
Emitted when datum transforms stored in the project are not available locally.
QgsTransactionGroup * transactionGroup(const QString &providerKey, const QString &connString)
Returns the matching transaction group from a provider key and connection string.
QgsCoordinateReferenceSystem crs
Definition qgsproject.h:112
void readProjectWithContext(const QDomDocument &, QgsReadWriteContext &context)
Emitted when a project is being read.
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
QStringList nonIdentifiableLayers
Definition qgsproject.h:109
void setAvoidIntersectionsMode(const Qgis::AvoidIntersectionsMode mode)
Sets the avoid intersections mode.
void transactionGroupsChanged()
Emitted whenever a new transaction group has been created or a transaction group has been removed.
const QgsAuxiliaryStorage * auxiliaryStorage() const
Returns the current const auxiliary storage.
void reloadAllLayers()
Reload all registered layer's provider data caches, synchronising the layer with any changes in the d...
int count() const
Returns the number of registered layers.
void loadingLayerMessageReceived(const QString &layerName, const QList< QgsReadWriteContext::ReadWriteMessage > &messages)
Emitted when loading layers has produced some messages.
void setAreaUnits(Qgis::AreaUnit unit)
Sets the default area measurement units for the project.
void setTitle(const QString &title)
Sets the project's title.
QMap< QPair< QString, QString >, QgsTransactionGroup * > transactionGroups()
Map of transaction groups.
void writeProject(QDomDocument &)
Emitted when the project is being written.
void setFlag(Qgis::ProjectFlag flag, bool enabled=true)
Sets whether a project flag is enabled.
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
bool readLayer(const QDomNode &layerNode)
Reads the layer described in the associated DOM node.
double readDoubleEntry(const QString &scope, const QString &key, double def=0, bool *ok=nullptr) const
Reads a double from the specified scope and key.
bool writeEntry(const QString &scope, const QString &key, bool value)
Write a boolean value to the project file.
QString absoluteFilePath() const
Returns full absolute path to the project file if the project is stored in a file system - derived fr...
QDateTime lastSaveDateTime() const
Returns the date and time when the project was last saved.
void projectSaved()
Emitted when the project file has been written and closed.
Q_DECL_DEPRECATED bool trustLayerMetadata() const
Returns true if the trust option is activated, false otherwise.
QString writePath(const QString &filename) const
Prepare a filename to save it to the project file.
void setEllipsoid(const QString &ellipsoid)
Sets the project's ellipsoid from a proj string representation, e.g., "WGS84".
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
QColor backgroundColor
Definition qgsproject.h:121
void layerLoaded(int i, int n)
Emitted when a layer from a projects was read.
QStringList subkeyList(const QString &scope, const QString &key) const
Returns a list of child keys which contain other keys that exist within the the specified scope and k...
bool read(const QString &filename, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Reads given project file from the given file.
QStringList readListEntry(const QString &scope, const QString &key, const QStringList &def=QStringList(), bool *ok=nullptr) const
Reads a string list from the specified scope and key.
void selectionColorChanged()
Emitted whenever the project's selection color has been changed.
bool topologicalEditing
Definition qgsproject.h:123
const QgsLabelingEngineSettings & labelingEngineSettings() const
Returns project's global labeling engine settings.
void removeAllMapLayers()
Removes all registered layers.
Q_DECL_DEPRECATED QVector< double > mapScales() const
Returns the list of custom project map scales.
void setDirty(bool b=true)
Flag the project as dirty (modified).
void backgroundColorChanged()
Emitted whenever the project's canvas background color has been changed.
QgsLayerTreeGroup * createEmbeddedGroup(const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags=Qgis::ProjectReadFlags())
Create layer group instance defined in an arbitrary project file.
const QgsProjectViewSettings * viewSettings() const
Returns the project's view settings, which contains settings and properties relating to how a QgsProj...
QString readPath(const QString &filename) const
Transforms a filename read from the project file to an absolute path.
void registerTranslatableContainers(QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId)
Registers the containers that require translation into the translationContext.
void setElevationShadingRenderer(const QgsElevationShadingRenderer &elevationShadingRenderer)
Sets the elevation shading renderer used for global map shading.
void setFilePathStorage(Qgis::FilePathType type)
Sets the type of paths used when storing file paths in a QGS/QGZ project file.
Q_DECL_DEPRECATED QSet< QgsMapLayer * > requiredLayers() const
Returns a set of map layers that are required in the project and therefore they should not get remove...
void transformContextChanged()
Emitted when the project transformContext() is changed.
void setTopologicalEditing(bool enabled)
Convenience function to set topological editing.
void legendLayersAdded(const QList< QgsMapLayer * > &layers)
Emitted, when a layer was added to the registry and the legend.
QVariantMap customVariables() const
A map of custom project variables.
void setAvoidIntersectionsLayers(const QList< QgsVectorLayer * > &layers)
Sets the list of layers with which intersections should be avoided.
void homePathChanged()
Emitted when the home path of the project changes.
void dirtySet()
Emitted when setDirty(true) is called.
void setCustomVariables(const QVariantMap &customVariables)
A map of custom project variables.
QgsCoordinateReferenceSystem defaultCrsForNewLayers() const
Returns the default CRS for new layers based on the settings and the current project CRS.
QString saveUserFullName() const
Returns the full user name that did the last save.
void layersAdded(const QList< QgsMapLayer * > &layers)
Emitted when one or more layers were added to the registry.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
QString homePath
Definition qgsproject.h:111
bool isDirty() const
Returns true if the project has been modified since the last write()
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
void isDirtyChanged(bool dirty)
Emitted when the project dirty status changes.
void setDistanceUnits(Qgis::DistanceUnit unit)
Sets the default distance measurement units for the project.
Q_DECL_DEPRECATED bool useProjectScales() const
Returns true if project mapScales() are enabled.
Q_DECL_DEPRECATED void setMapScales(const QVector< double > &scales)
Sets the list of custom project map scales.
void setPresetHomePath(const QString &path)
Sets the project's home path.
void setFlags(Qgis::ProjectFlags flags)
Sets the project's flags, which dictate the behavior of the project.
QList< QgsMapLayer * > mapLayersByShortName(const QString &shortName) const
Retrieves a list of matching registered layers by layer shortName.
QgsProjectStorage * projectStorage() const
Returns pointer to project storage implementation that handles read/write of the project file.
QString layerIsEmbedded(const QString &id) const
Returns the source project file path if the layer with matching id is embedded from other project fil...
const QgsProjectTimeSettings * timeSettings() const
Returns the project's time settings, which contains the project's temporal range and other time based...
void topologicalEditingChanged()
Emitted when the topological editing flag has changed.
bool removeEntry(const QString &scope, const QString &key)
Remove the given key from the specified scope.
QgsProjectVersion lastSaveVersion() const
Returns the QGIS version which the project was last saved using.
void avoidIntersectionsModeChanged()
Emitted whenever the avoid intersections mode has changed.
void loadingLayer(const QString &layerName)
Emitted when a layer is loaded.
A grouped map of multiple QgsProperty objects, each referenced by a integer key value.
Definition for a property.
Definition qgsproperty.h:46
@ String
Any string value.
Definition qgsproperty.h:60
Holds data provider key, description, and associated shared library file or function pointer informat...
virtual QgsProviderMetadata::ProviderCapabilities providerCapabilities() const
Returns the provider's capabilities.
@ ParallelCreateProvider
Indicates that the provider supports parallel creation, that is, can be created on another thread tha...
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
QString relativeToAbsoluteUri(const QString &providerKey, const QString &uri, const QgsReadWriteContext &context) const
Converts relative path(s) to absolute path(s) in the given provider-specific URI.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
The class is used as a container of context for various read/write operations on other objects.
void setTransformContext(const QgsCoordinateTransformContext &transformContext)
Sets data coordinate transform context to transformContext.
QgsCoordinateTransformContext transformContext() const
Returns data provider coordinate transform context.
QList< QgsReadWriteContext::ReadWriteMessage > takeMessages()
Returns the stored messages and remove them.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
This class manages a set of relations between layers.
void clear()
Remove any relation managed by this class.
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
The QgsRunnableProviderCreator class is used when reading a project to create asynchronously provider...
void providerCreated(bool isValid, const QString &layerId)
Emitted when a provider is created with isValid set to True when the provider is valid.
QgsDataProvider * dataProvider()
Returns the created data provider.
void clear(const QString &group="startup")
clear Clear all profile data.
Expression function for use within a QgsExpressionContextScope.
Scoped object for logging of the runtime for a single operation or group of operations.
Manages sensors.
QDomElement writeXml(QDomDocument &document) const
Returns a DOM element representing the state of the manager.
void clear()
Deregisters and removes all sensors from the manager.
bool readXml(const QDomElement &element, const QDomDocument &document)
Reads the manager's state from a DOM element, restoring all sensors present in the XML document.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
static const QgsSettingsEntryInteger * settingsLayerParallelLoadingMaxCount
Settings entry maximum thread count used to load layer in parallel.
static const QgsSettingsEntryBool * settingsLayerParallelLoading
Settings entry whether layer are loading in parallel.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
This is a container for configuration of the snapping of the project.
bool addLayers(const QList< QgsMapLayer * > &layers)
Adds the specified layers as individual layers to the configuration with standard configuration.
void readProject(const QDomDocument &doc)
Reads the configuration from the specified QGIS project document.
void reset()
reset to default values
void writeProject(QDomDocument &doc)
Writes the configuration to the specified QGIS project document.
void clearIndividualLayerSettings()
Removes all individual layer snapping settings.
bool removeLayers(const QList< QgsMapLayer * > &layers)
Removes the specified layers from the individual layer configuration.
An interface for classes which can visit style entity (e.g.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
static QgsStyle * defaultStyle()
Returns default application-wide style.
Definition qgsstyle.cpp:145
void triggerIconRebuild()
Triggers emission of the rebuildIconPreviews() signal.
static QColor decodeColor(const QString &str)
static QString encodeColor(const QColor &color)
bool addLayer(QgsVectorLayer *layer)
Add a layer to this transaction group.
static bool supportsTransaction(const QgsVectorLayer *layer)
Checks if the provider of a given layer supports transactions.
QString connectionString() const
Returns the connection string of the transaction.
Used for the collecting of strings from projects for translation and creation of ts files.
void registerTranslation(const QString &context, const QString &source)
Registers the source to be translated.
void setProject(QgsProject *project)
Sets the project being translated.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE Qgis::AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE Qgis::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
The edit buffer group manages a group of edit buffers.
bool commitChanges(QStringList &commitErrors, bool stopEditing=true)
Attempts to commit any changes to disk.
void clear()
Remove all layers from this edit buffer group.
bool rollBack(QStringList &rollbackErrors, bool stopEditing=true)
Stop editing and discard the edits.
void addLayer(QgsVectorLayer *layer)
Add a layer to this edit buffer group.
Represents a vector layer which manages a vector based data sets.
Q_INVOKABLE bool startEditing()
Makes the layer editable.
bool loadAuxiliaryLayer(const QgsAuxiliaryStorage &storage, const QString &key=QString())
Loads the auxiliary layer for this vector layer.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
QStringList commitErrors() const
Returns a list containing any error messages generated when attempting to commit changes to the layer...
Q_INVOKABLE bool rollBack(bool deleteBuffer=true)
Stops a current editing operation and discards any uncommitted edits.
Q_INVOKABLE bool commitChanges(bool stopEditing=true)
Attempts to commit to the underlying data provider any buffered changes made since the last to call t...
QgsEditFormConfig editFormConfig
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
CORE_EXPORT bool isZipFile(const QString &filename)
Returns true if the file name is a zipped file ( i.e with a '.qgz' extension, false otherwise.
CORE_EXPORT const QStringList files(const QString &zip)
Returns the list of files within a zip file.
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition qgis.h:4834
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:4555
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:4916
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:4536
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition qgis.h:4594
T qgsFlagKeysToValue(const QString &keys, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given keys of a flag.
Definition qgis.h:4616
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:4915
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition qgis.h:4828
#define QgsDebugCall
Definition qgslogger.h:37
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
void _getProperties(const QDomDocument &doc, QgsProjectPropertyKey &project_properties)
Restores any optional properties found in "doc" to "properties".
QgsPropertyCollection getDataDefinedServerProperties(const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions)
Returns the data defined server properties collection found in "doc" to "dataDefinedServerProperties"...
void removeKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Removes a given key.
QgsProjectVersion getVersion(const QDomDocument &doc)
Returns the version string found in the given DOM document.
QStringList makeKeyTokens_(const QString &scope, const QString &key)
Takes the given scope and key and convert them to a string list of key tokens that will be used to na...
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Returns the property that matches the given key sequence, if any.
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value, bool &propertiesModified)
Adds the given key and value.
CORE_EXPORT QgsProjectVersion getVersion(QDomDocument const &doc)
Returns the version string found in the given DOM document.
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
const QgsCoordinateReferenceSystem & crs
const QString & typeName
QgsDataProvider::ReadFlags flags
QDomElement layerElement
QString layerId
QgsDataProvider::ProviderOptions options
QString provider
QString dataSource
Setting options for loading annotation layers.
Setting options for creating vector data providers.
Single variable definition for use within a QgsExpressionContextScope.
Setting options for loading group layers.
Contains information relating to a node (i.e.