QGIS API Documentation 3.99.0-Master (d270888f95f)
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 <algorithm>
21
22#include "qgsaction.h"
23#include "qgsactionmanager.h"
24#include "qgsannotationlayer.h"
26#include "qgsapplication.h"
28#include "qgsauxiliarystorage.h"
29#include "qgsbookmarkmanager.h"
30#include "qgscolorutils.h"
32#include "qgsdatasourceuri.h"
35#include "qgsgrouplayer.h"
37#include "qgslayerdefinition.h"
38#include "qgslayertree.h"
40#include "qgslayertreeutils.h"
41#include "qgslayoutmanager.h"
42#include "qgslogger.h"
43#include "qgsmaplayerfactory.h"
44#include "qgsmaplayerstore.h"
46#include "qgsmapviewsmanager.h"
47#include "qgsmeshlayer.h"
48#include "qgsmessagelog.h"
49#include "qgsobjectvisitor.h"
50#include "qgspathresolver.h"
51#include "qgspluginlayer.h"
53#include "qgspointcloudlayer.h"
58#include "qgsprojectstorage.h"
62#include "qgsprojectutils.h"
63#include "qgsprojectversion.h"
65#include "qgsproviderregistry.h"
66#include "qgspythonrunner.h"
67#include "qgsrasterlayer.h"
68#include "qgsreadwritecontext.h"
69#include "qgsrelationmanager.h"
71#include "qgsruntimeprofiler.h"
72#include "qgssensormanager.h"
74#include "qgssnappingconfig.h"
76#include "qgsthreadingutils.h"
77#include "qgstiledscenelayer.h"
78#include "qgstransaction.h"
79#include "qgstransactiongroup.h"
80#include "qgsunittypes.h"
83#include "qgsvectortilelayer.h"
84#include "qgsziputils.h"
85
86#include <QApplication>
87#include <QDir>
88#include <QDomNode>
89#include <QFileInfo>
90#include <QObject>
91#include <QRegularExpression>
92#include <QStandardPaths>
93#include <QString>
94#include <QTemporaryFile>
95#include <QTextStream>
96#include <QThreadPool>
97#include <QUrl>
98#include <QUuid>
99
100#include "moc_qgsproject.cpp"
101
102using namespace Qt::StringLiterals;
103
104#ifdef _MSC_VER
105#include <sys/utime.h>
106#else
107#include <utime.h>
108#endif
109
110// canonical project instance
111QgsProject *QgsProject::sProject = nullptr;
112
121QStringList makeKeyTokens_( const QString &scope, const QString &key )
122{
123 QStringList keyTokens = QStringList( scope );
124 keyTokens += key.split( '/', Qt::SkipEmptyParts );
125
126 // be sure to include the canonical root node
127 keyTokens.push_front( u"properties"_s );
128
129 return keyTokens;
130}
131
132
133
143QgsProjectProperty *findKey_( const QString &scope,
144 const QString &key,
145 QgsProjectPropertyKey &rootProperty )
146{
147 QgsProjectPropertyKey *currentProperty = &rootProperty;
148 QgsProjectProperty *nextProperty; // link to next property down hierarchy
149
150 QStringList keySequence = makeKeyTokens_( scope, key );
151
152 while ( !keySequence.isEmpty() )
153 {
154 // if the current head of the sequence list matches the property name,
155 // then traverse down the property hierarchy
156 if ( keySequence.first() == currentProperty->name() )
157 {
158 // remove front key since we're traversing down a level
159 keySequence.pop_front();
160
161 if ( 1 == keySequence.count() )
162 {
163 // if we have only one key name left, then return the key found
164 return currentProperty->find( keySequence.front() );
165 }
166 else if ( keySequence.isEmpty() )
167 {
168 // if we're out of keys then the current property is the one we
169 // want; i.e., we're in the rate case of being at the top-most
170 // property node
171 return currentProperty;
172 }
173 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
174 {
175 if ( nextProperty->isKey() )
176 {
177 currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
178 }
179 else if ( nextProperty->isValue() && 1 == keySequence.count() )
180 {
181 // it may be that this may be one of several property value
182 // nodes keyed by QDict string; if this is the last remaining
183 // key token and the next property is a value node, then
184 // that's the situation, so return the currentProperty
185 return currentProperty;
186 }
187 else
188 {
189 // QgsProjectPropertyValue not Key, so return null
190 return nullptr;
191 }
192 }
193 else
194 {
195 // if the next key down isn't found
196 // then the overall key sequence doesn't exist
197 return nullptr;
198 }
199 }
200 else
201 {
202 return nullptr;
203 }
204 }
205
206 return nullptr;
207}
208
209
210
220QgsProjectProperty *addKey_( const QString &scope,
221 const QString &key,
222 QgsProjectPropertyKey *rootProperty,
223 const QVariant &value,
224 bool &propertiesModified )
225{
226 QStringList keySequence = makeKeyTokens_( scope, key );
227
228 // cursor through property key/value hierarchy
229 QgsProjectPropertyKey *currentProperty = rootProperty;
230 QgsProjectProperty *nextProperty; // link to next property down hierarchy
231 QgsProjectPropertyKey *newPropertyKey = nullptr;
232
233 propertiesModified = false;
234 while ( ! keySequence.isEmpty() )
235 {
236 // if the current head of the sequence list matches the property name,
237 // then traverse down the property hierarchy
238 if ( keySequence.first() == currentProperty->name() )
239 {
240 // remove front key since we're traversing down a level
241 keySequence.pop_front();
242
243 // if key sequence has one last element, then we use that as the
244 // name to store the value
245 if ( 1 == keySequence.count() )
246 {
247 QgsProjectProperty *property = currentProperty->find( keySequence.front() );
248 if ( !property || property->value() != value )
249 {
250 currentProperty->setValue( keySequence.front(), value );
251 propertiesModified = true;
252 }
253
254 return currentProperty;
255 }
256 // we're at the top element if popping the keySequence element
257 // will leave it empty; in that case, just add the key
258 else if ( keySequence.isEmpty() )
259 {
260 if ( currentProperty->value() != value )
261 {
262 currentProperty->setValue( value );
263 propertiesModified = true;
264 }
265
266 return currentProperty;
267 }
268 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
269 {
270 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
271
272 if ( currentProperty )
273 {
274 continue;
275 }
276 else // QgsProjectPropertyValue not Key, so return null
277 {
278 return nullptr;
279 }
280 }
281 else // the next subkey doesn't exist, so add it
282 {
283 if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
284 {
285 currentProperty = newPropertyKey;
286 }
287 continue;
288 }
289 }
290 else
291 {
292 return nullptr;
293 }
294 }
295
296 return nullptr;
297}
298
306void removeKey_( const QString &scope,
307 const QString &key,
308 QgsProjectPropertyKey &rootProperty )
309{
310 QgsProjectPropertyKey *currentProperty = &rootProperty;
311
312 QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
313 QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
314
315 QStringList keySequence = makeKeyTokens_( scope, key );
316
317 while ( ! keySequence.isEmpty() )
318 {
319 // if the current head of the sequence list matches the property name,
320 // then traverse down the property hierarchy
321 if ( keySequence.first() == currentProperty->name() )
322 {
323 // remove front key since we're traversing down a level
324 keySequence.pop_front();
325
326 // if we have only one key name left, then try to remove the key
327 // with that name
328 if ( 1 == keySequence.count() )
329 {
330 currentProperty->removeKey( keySequence.front() );
331 }
332 // if we're out of keys then the current property is the one we
333 // want to remove, but we can't delete it directly; we need to
334 // delete it from the parent property key container
335 else if ( keySequence.isEmpty() )
336 {
337 previousQgsPropertyKey->removeKey( currentProperty->name() );
338 }
339 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
340 {
341 previousQgsPropertyKey = currentProperty;
342 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
343
344 if ( currentProperty )
345 {
346 continue;
347 }
348 else // QgsProjectPropertyValue not Key, so return null
349 {
350 return;
351 }
352 }
353 else // if the next key down isn't found
354 {
355 // then the overall key sequence doesn't exist
356 return;
357 }
358 }
359 else
360 {
361 return;
362 }
363 }
364}
365
367 : QObject( parent )
368 , mCapabilities( capabilities )
369 , mLayerStore( new QgsMapLayerStore( this ) )
370 , mBadLayerHandler( std::make_unique<QgsProjectBadLayerHandler>() )
371 , mSnappingConfig( this )
372 , mRelationManager( std::make_unique<QgsRelationManager>( this ) )
373 , mAnnotationManager( new QgsAnnotationManager( this ) )
374 , mLayoutManager( new QgsLayoutManager( this ) )
375 , mElevationProfileManager( new QgsElevationProfileManager( this ) )
376 , m3DViewsManager( new QgsMapViewsManager( this ) )
377 , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
378 , mSensorManager( new QgsSensorManager( this ) )
379 , mViewSettings( new QgsProjectViewSettings( this ) )
380 , mStyleSettings( new QgsProjectStyleSettings( this ) )
381 , mTimeSettings( new QgsProjectTimeSettings( this ) )
382 , mElevationProperties( new QgsProjectElevationProperties( this ) )
383 , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
384 , mGpsSettings( new QgsProjectGpsSettings( this ) )
385 , mRootGroup( std::make_unique<QgsLayerTree>() )
386 , mLabelingEngineSettings( new QgsLabelingEngineSettings )
387 , mArchive( new QgsArchive() )
388 , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
389{
390 mProperties.setName( u"properties"_s );
391
392 mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
393 mMainAnnotationLayer->setParent( this );
394
395 clear();
396
397 // bind the layer tree to the map layer registry.
398 // whenever layers are added to or removed from the registry,
399 // layer tree will be updated
400 mLayerTreeRegistryBridge = std::make_unique<QgsLayerTreeRegistryBridge>( mRootGroup.get(), this, this );
401 connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
402 connect( this, &QgsProject::layersRemoved, this, [this] { cleanTransactionGroups(); } );
403 connect( this, qOverload< const QList<QgsMapLayer *> & >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
404
405 // proxy map layer store signals to this
406 connect( mLayerStore.get(), qOverload<const QStringList &>( &QgsMapLayerStore::layersWillBeRemoved ),
407 this, [this]( const QStringList & layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
408 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ),
409 this, [this]( const QList<QgsMapLayer *> &layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
410 connect( mLayerStore.get(), qOverload< const QString & >( &QgsMapLayerStore::layerWillBeRemoved ),
411 this, [this]( const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
412 connect( mLayerStore.get(), qOverload< QgsMapLayer * >( &QgsMapLayerStore::layerWillBeRemoved ),
413 this, [this]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
414 connect( mLayerStore.get(), qOverload<const QStringList & >( &QgsMapLayerStore::layersRemoved ), this,
415 [this]( const QStringList & layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
416 connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this,
417 [this]( const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
418 connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this,
419 [this]() { mProjectScope.reset(); emit removeAll(); } );
420 connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this,
421 [this]( const QList< QgsMapLayer * > &layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
422 connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this,
423 [this]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
424
426 {
428 }
429
430 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ), this,
431 [this]( const QList<QgsMapLayer *> &layers )
432 {
433 for ( const auto &layer : layers )
434 {
435 disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager.get(), &QgsRelationManager::updateRelationsStatus );
436 }
437 }
438 );
439 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersAdded ), this,
440 [this]( const QList<QgsMapLayer *> &layers )
441 {
442 for ( const auto &layer : layers )
443 {
444 connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager.get(), &QgsRelationManager::updateRelationsStatus );
445 }
446 }
447 );
448
452
453 mStyleSettings->combinedStyleModel()->addDefaultStyle();
454}
455
456
458{
459 mIsBeingDeleted = true;
460
461 clear();
462 releaseHandlesToProjectArchive();
463
464 if ( this == sProject )
465 {
466 sProject = nullptr;
467 }
468}
469
471{
472 sProject = project;
473}
474
475
476QgsProject *QgsProject::instance() // skip-keyword-check
477{
478 if ( !sProject )
479 {
480 sProject = new QgsProject;
481
483 }
484 return sProject;
485}
486
487void QgsProject::setTitle( const QString &title )
488{
490
491 if ( title == mMetadata.title() )
492 return;
493
494 mMetadata.setTitle( title );
495 mProjectScope.reset();
496 emit metadataChanged();
497 emit titleChanged();
498
499 setDirty( true );
500}
501
502QString QgsProject::title() const
503{
504 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
506
507 return mMetadata.title();
508}
509
511{
513
514 const bool oldEvaluateDefaultValues = mFlags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
515 const bool newEvaluateDefaultValues = flags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
516 if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
517 {
518 const QMap<QString, QgsMapLayer *> layers = mapLayers();
519 for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
520 {
521 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
522 if ( vl->dataProvider() )
523 vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, newEvaluateDefaultValues );
524 }
525 }
526
527 const bool oldTrustLayerMetadata = mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
528 const bool newTrustLayerMetadata = flags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
529 if ( oldTrustLayerMetadata != newTrustLayerMetadata )
530 {
531 const QMap<QString, QgsMapLayer *> layers = mapLayers();
532 for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
533 {
534 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
535 {
536 vl->setReadExtentFromXml( newTrustLayerMetadata );
537 }
538 }
539 }
540
541 if ( mFlags != flags )
542 {
543 mFlags = flags;
544 setDirty( true );
545 }
546}
547
548void QgsProject::setFlag( Qgis::ProjectFlag flag, bool enabled )
549{
551
552 Qgis::ProjectFlags newFlags = mFlags;
553 if ( enabled )
554 newFlags |= flag;
555 else
556 newFlags &= ~( static_cast< int >( flag ) );
557 setFlags( newFlags );
558}
559
560QString QgsProject::saveUser() const
561{
563
564 return mSaveUser;
565}
566
568{
570
571 return mSaveUserFull;
572}
573
575{
577
578 return mSaveDateTime;
579}
580
587
589{
591
592 return mDirty;
593}
594
595void QgsProject::setDirty( const bool dirty )
596{
598
599 if ( dirty && mDirtyBlockCount > 0 )
600 return;
601
602 if ( dirty )
603 emit dirtySet();
604
605 if ( mDirty == dirty )
606 return;
607
608 mDirty = dirty;
609 emit isDirtyChanged( mDirty );
610}
611
612void QgsProject::setPresetHomePath( const QString &path )
613{
615
616 if ( path == mHomePath )
617 return;
618
619 mHomePath = path;
620 mCachedHomePath.clear();
621 mProjectScope.reset();
622
623 emit homePathChanged();
624
625 setDirty( true );
626}
627
628void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
629{
631
632 const QList<QgsAttributeEditorElement *> elements = parent->children();
633
634 for ( QgsAttributeEditorElement *element : elements )
635 {
636 if ( element->type() == Qgis::AttributeEditorType::Container )
637 {
638 QgsAttributeEditorContainer *container = qgis::down_cast<QgsAttributeEditorContainer *>( element );
639
640 translationContext->registerTranslation( u"project:layers:%1:formcontainers"_s.arg( layerId ), container->name() );
641
642 if ( !container->children().empty() )
643 registerTranslatableContainers( translationContext, container, layerId );
644 }
645 }
646}
647
649{
651
652 //register layers
653 const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
654
655 for ( const QgsLayerTreeLayer *layer : layers )
656 {
657 translationContext->registerTranslation( u"project:layers:%1"_s.arg( layer->layerId() ), layer->name() );
658
659 if ( QgsMapLayer *mapLayer = layer->layer() )
660 {
661 switch ( mapLayer->type() )
662 {
664 {
665 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
666
667 //register general (like alias) and widget specific field settings (like value map descriptions)
668 const QgsFields fields = vlayer->fields();
669 for ( const QgsField &field : fields )
670 {
671 //general
672 //alias
673 QString fieldName;
674 if ( field.alias().isEmpty() )
675 fieldName = field.name();
676 else
677 fieldName = field.alias();
678
679 translationContext->registerTranslation( u"project:layers:%1:fieldaliases"_s.arg( vlayer->id() ), fieldName );
680
681 //constraint description
682 if ( !field.constraints().constraintDescription().isEmpty() )
683 translationContext->registerTranslation( u"project:layers:%1:constraintdescriptions"_s.arg( vlayer->id() ), field.constraints().constraintDescription() );
684
685 //widget specific
686 //value relation
687 if ( field.editorWidgetSetup().type() == "ValueRelation"_L1 )
688 {
689 translationContext->registerTranslation( u"project:layers:%1:fields:%2:valuerelationvalue"_s.arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( u"Value"_s ).toString() );
690 translationContext->registerTranslation( u"project:layers:%1:fields:%2:valuerelationdescription"_s.arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( u"Description"_s ).toString() );
691 }
692
693 //value map
694 if ( field.editorWidgetSetup().type() == "ValueMap"_L1 )
695 {
696 if ( field.editorWidgetSetup().config().value( u"map"_s ).canConvert<QList<QVariant>>() )
697 {
698 const QList<QVariant> valueList = field.editorWidgetSetup().config().value( u"map"_s ).toList();
699
700 for ( int i = 0, row = 0; i < valueList.count(); i++, row++ )
701 {
702 translationContext->registerTranslation( u"project:layers:%1:fields:%2:valuemapdescriptions"_s.arg( vlayer->id(), field.name() ), valueList[i].toMap().constBegin().key() );
703 }
704 }
705 }
706 }
707
708 //register formcontainers
709 registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
710
711 //actions
712 for ( const QgsAction &action : vlayer->actions()->actions() )
713 {
714 translationContext->registerTranslation( u"project:layers:%1:actiondescriptions"_s.arg( vlayer->id() ), action.name() );
715 translationContext->registerTranslation( u"project:layers:%1:actionshorttitles"_s.arg( vlayer->id() ), action.shortTitle() );
716 }
717
718 //legend
719 if ( vlayer->renderer() )
720 {
721 for ( const QgsLegendSymbolItem &item : vlayer->renderer()->legendSymbolItems() )
722 {
723 translationContext->registerTranslation( u"project:layers:%1:legendsymbollabels"_s.arg( vlayer->id() ), item.label() );
724 }
725 }
726 break;
727 }
728
737 break;
738 }
739
740 //register metadata
741 mapLayer->metadata().registerTranslations( translationContext );
742 }
743 }
744
745 //register layergroups and subgroups
746 const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups( true );
747 for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
748 {
749 translationContext->registerTranslation( u"project:layergroups"_s, groupLayer->name() );
750 }
751
752 //register relations
753 const QList<QgsRelation> &relations = mRelationManager->relations().values();
754 for ( const QgsRelation &relation : relations )
755 {
756 translationContext->registerTranslation( u"project:relations"_s, relation.name() );
757 }
758
759 //register metadata
760 mMetadata.registerTranslations( translationContext );
761}
762
764{
766
767 mDataDefinedServerProperties = properties;
768}
769
771{
773
774 return mDataDefinedServerProperties;
775}
776
778{
780
781 switch ( mTransactionMode )
782 {
785 {
786 if ( ! vectorLayer )
787 return false;
788 return vectorLayer->startEditing();
789 }
790
792 return mEditBufferGroup.startEditing();
793 }
794
795 return false;
796}
797
798bool QgsProject::commitChanges( QStringList &commitErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
799{
801
802 switch ( mTransactionMode )
803 {
806 {
807 if ( ! vectorLayer )
808 {
809 commitErrors.append( tr( "Trying to commit changes without a layer specified. This only works if the transaction mode is buffered" ) );
810 return false;
811 }
812 bool success = vectorLayer->commitChanges( stopEditing );
813 commitErrors = vectorLayer->commitErrors();
814 return success;
815 }
816
818 return mEditBufferGroup.commitChanges( commitErrors, stopEditing );
819 }
820
821 return false;
822}
823
824bool QgsProject::rollBack( QStringList &rollbackErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
825{
827
828 switch ( mTransactionMode )
829 {
832 {
833 if ( ! vectorLayer )
834 {
835 rollbackErrors.append( tr( "Trying to roll back changes without a layer specified. This only works if the transaction mode is buffered" ) );
836 return false;
837 }
838 bool success = vectorLayer->rollBack( stopEditing );
839 rollbackErrors = vectorLayer->commitErrors();
840 return success;
841 }
842
844 return mEditBufferGroup.rollBack( rollbackErrors, stopEditing );
845 }
846
847 return false;
848}
849
850void QgsProject::setFileName( const QString &name )
851{
853
854 if ( name == mFile.fileName() )
855 return;
856
857 const QString oldHomePath = homePath();
858
859 mFile.setFileName( name );
860 mCachedHomePath.clear();
861 mProjectScope.reset();
862
863 emit fileNameChanged();
864
865 const QString newHomePath = homePath();
866 if ( newHomePath != oldHomePath )
867 emit homePathChanged();
868
869 setDirty( true );
870}
871
872QString QgsProject::fileName() const
873{
874 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
876
877 return mFile.fileName();
878}
879
880void QgsProject::setOriginalPath( const QString &path )
881{
883
884 mOriginalPath = path;
885}
886
888{
890
891 return mOriginalPath;
892}
893
894QFileInfo QgsProject::fileInfo() const
895{
897
898 return QFileInfo( mFile );
899}
900
902{
903 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
905
907}
908
910{
912
913 if ( QgsProjectStorage *storage = projectStorage() )
914 {
916 storage->readProjectStorageMetadata( mFile.fileName(), metadata );
917 return metadata.lastModified;
918 }
919 else
920 {
921 return QFileInfo( mFile.fileName() ).lastModified();
922 }
923}
924
926{
928
929 if ( projectStorage() )
930 return QString();
931
932 if ( mFile.fileName().isEmpty() )
933 return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
934
935 return QFileInfo( mFile.fileName() ).absolutePath();
936}
937
939{
940 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
942
943 if ( projectStorage() )
944 return QString();
945
946 if ( mFile.fileName().isEmpty() )
947 return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
948
949 return QFileInfo( mFile.fileName() ).absoluteFilePath();
950}
951
952QString QgsProject::baseName() const
953{
954 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
956
957 if ( QgsProjectStorage *storage = projectStorage() )
958 {
960 storage->readProjectStorageMetadata( mFile.fileName(), metadata );
961 return metadata.name;
962 }
963 else
964 {
965 return QFileInfo( mFile.fileName() ).completeBaseName();
966 }
967}
968
970{
972
973 const bool absolutePaths = readBoolEntry( u"Paths"_s, u"/Absolute"_s, false );
975}
976
978{
980
981 switch ( type )
982 {
984 writeEntry( u"Paths"_s, u"/Absolute"_s, true );
985 break;
987 writeEntry( u"Paths"_s, u"/Absolute"_s, false );
988 break;
989 }
990}
991
993{
994 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
996
997 return mCrs;
998}
999
1001{
1003
1004 return mCrs3D.isValid() ? mCrs3D : mCrs;
1005}
1006
1007void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
1008{
1010
1011 if ( crs != mCrs )
1012 {
1013 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
1014 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
1015 mCrs = crs;
1016 writeEntry( u"SpatialRefSys"_s, u"/ProjectionsEnabled"_s, crs.isValid() ? 1 : 0 );
1017 mProjectScope.reset();
1018
1019 // if annotation layer doesn't have a crs (i.e. in a newly created project), it should
1020 // initially inherit the project CRS
1021 if ( !mMainAnnotationLayer->crs().isValid() || mMainAnnotationLayer->isEmpty() )
1022 mMainAnnotationLayer->setCrs( crs );
1023
1024 rebuildCrs3D();
1025
1026 setDirty( true );
1027 emit crsChanged();
1028 // Did vertical crs also change as a result of this? If so, emit signal
1029 if ( oldVerticalCrs != verticalCrs() )
1030 emit verticalCrsChanged();
1031 if ( oldCrs3D != mCrs3D )
1032 emit crs3DChanged();
1033 }
1034
1035 if ( adjustEllipsoid )
1036 setEllipsoid( crs.ellipsoidAcronym() );
1037}
1038
1040{
1041 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
1043
1044 if ( !crs().isValid() )
1045 return Qgis::geoNone();
1046
1047 return readEntry( u"Measure"_s, u"/Ellipsoid"_s, Qgis::geoNone() );
1048}
1049
1051{
1053
1054 if ( ellipsoid == readEntry( u"Measure"_s, u"/Ellipsoid"_s ) )
1055 return;
1056
1057 mProjectScope.reset();
1058 writeEntry( u"Measure"_s, u"/Ellipsoid"_s, ellipsoid );
1060}
1061
1063{
1064 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
1066
1067 switch ( mCrs.type() )
1068 {
1069 case Qgis::CrsType::Vertical: // would hope this never happens!
1070 QgsDebugError( u"Project has a vertical CRS set as the horizontal CRS!"_s );
1071 return mCrs;
1072
1074 return mCrs.verticalCrs();
1075
1087 break;
1088 }
1089 return mVerticalCrs;
1090}
1091
1093{
1095 bool res = true;
1096 if ( crs.isValid() )
1097 {
1098 // validate that passed crs is a vertical crs
1099 switch ( crs.type() )
1100 {
1102 break;
1103
1116 if ( errorMessage )
1117 *errorMessage = QObject::tr( "Specified CRS is a %1 CRS, not a Vertical CRS" ).arg( qgsEnumValueToKey( crs.type() ) );
1118 return false;
1119 }
1120 }
1121
1122 if ( crs != mVerticalCrs )
1123 {
1124 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
1125 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
1126
1127 switch ( mCrs.type() )
1128 {
1130 if ( crs != oldVerticalCrs )
1131 {
1132 if ( errorMessage )
1133 *errorMessage = QObject::tr( "Project CRS is a Compound CRS, specified Vertical CRS will be ignored" );
1134 return false;
1135 }
1136 break;
1137
1139 if ( crs != oldVerticalCrs )
1140 {
1141 if ( errorMessage )
1142 *errorMessage = QObject::tr( "Project CRS is a Geographic 3D CRS, specified Vertical CRS will be ignored" );
1143 return false;
1144 }
1145 break;
1146
1148 if ( crs != oldVerticalCrs )
1149 {
1150 if ( errorMessage )
1151 *errorMessage = QObject::tr( "Project CRS is a Geocentric CRS, specified Vertical CRS will be ignored" );
1152 return false;
1153 }
1154 break;
1155
1157 if ( mCrs.hasVerticalAxis() && crs != oldVerticalCrs )
1158 {
1159 if ( errorMessage )
1160 *errorMessage = QObject::tr( "Project CRS is a Projected 3D CRS, specified Vertical CRS will be ignored" );
1161 return false;
1162 }
1163 break;
1164
1174 break;
1175 }
1176
1177 mVerticalCrs = crs;
1178 res = rebuildCrs3D( errorMessage );
1179 mProjectScope.reset();
1180
1181 setDirty( true );
1182 // only emit signal if vertical crs was actually changed, so eg if mCrs is compound
1183 // then we haven't actually changed the vertical crs by this call!
1184 if ( verticalCrs() != oldVerticalCrs )
1185 emit verticalCrsChanged();
1186 if ( mCrs3D != oldCrs3D )
1187 emit crs3DChanged();
1188 }
1189 return res;
1190}
1191
1193{
1194 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
1196
1197 return mTransformContext;
1198}
1199
1201{
1203
1204 if ( context == mTransformContext )
1205 return;
1206
1207 mTransformContext = context;
1208 mProjectScope.reset();
1209
1210 mMainAnnotationLayer->setTransformContext( context );
1211 for ( auto &layer : mLayerStore.get()->mapLayers() )
1212 {
1213 layer->setTransformContext( context );
1214 }
1216}
1217
1219{
1221
1222 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
1223
1224 emit aboutToBeCleared();
1225
1226 if ( !mIsBeingDeleted )
1227 {
1228 // Unregister expression functions stored in the project.
1229 // If we clean on destruction we may end-up with a non-valid
1230 // mPythonUtils, so be safe and only clean when not destroying.
1231 // This should be called before calling mProperties.clearKeys().
1233 }
1234
1235 mProjectScope.reset();
1236 mFile.setFileName( QString() );
1237 mProperties.clearKeys();
1238 mSaveUser.clear();
1239 mSaveUserFull.clear();
1240 mSaveDateTime = QDateTime();
1241 mSaveVersion = QgsProjectVersion();
1242 mHomePath.clear();
1243 mCachedHomePath.clear();
1244 mTransactionMode = Qgis::TransactionMode::Disabled;
1245 mFlags = Qgis::ProjectFlags();
1246 mDirty = false;
1247 mCustomVariables.clear();
1249 mVerticalCrs = QgsCoordinateReferenceSystem();
1251 mMetadata = QgsProjectMetadata();
1252 mElevationShadingRenderer = QgsElevationShadingRenderer();
1253 if ( !mSettings.value( u"projects/anonymize_new_projects"_s, false, QgsSettings::Core ).toBool() )
1254 {
1255 mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
1256 mMetadata.setAuthor( QgsApplication::userFullName() );
1257 }
1258 emit metadataChanged();
1259
1261 context.readSettings();
1262 setTransformContext( context );
1263
1264 //fallback to QGIS default measurement unit
1265 bool ok = false;
1266 const Qgis::DistanceUnit distanceUnit = QgsUnitTypes::decodeDistanceUnit( mSettings.value( u"/qgis/measure/displayunits"_s ).toString(), &ok );
1267 setDistanceUnits( ok ? distanceUnit : Qgis::DistanceUnit::Meters );
1268 ok = false;
1269 const Qgis::AreaUnit areaUnits = QgsUnitTypes::decodeAreaUnit( mSettings.value( u"/qgis/measure/areaunits"_s ).toString(), &ok );
1271
1273
1274 mEmbeddedLayers.clear();
1275 mRelationManager->clear();
1276 mAnnotationManager->clear();
1277 mLayoutManager->clear();
1278 mElevationProfileManager->clear();
1279 m3DViewsManager->clear();
1280 mBookmarkManager->clear();
1281 mSensorManager->clear();
1282 mViewSettings->reset();
1283 mTimeSettings->reset();
1284 mElevationProperties->reset();
1285 mDisplaySettings->reset();
1286 mGpsSettings->reset();
1287 mSnappingConfig.reset();
1288 mAvoidIntersectionsMode = Qgis::AvoidIntersectionsMode::AllowIntersections;
1291
1292 mMapThemeCollection = std::make_unique< QgsMapThemeCollection >( this );
1294
1295 mLabelingEngineSettings->clear();
1296
1297 // must happen BEFORE archive reset, because we need to release the hold on any files which
1298 // exists within the archive. Otherwise the archive can't be removed.
1299 releaseHandlesToProjectArchive();
1300
1301 mAuxiliaryStorage = std::make_unique< QgsAuxiliaryStorage >();
1302 mArchive = std::make_unique< QgsArchive >();
1303
1304 // must happen AFTER archive reset, as it will populate a new style database within the new archive
1305 mStyleSettings->reset();
1306
1308
1309 if ( !mIsBeingDeleted )
1310 {
1311 // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
1312 emit projectColorsChanged();
1313 }
1314
1315 // reset some default project properties
1316 // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
1317 writeEntry( u"PositionPrecision"_s, u"/Automatic"_s, true );
1318 writeEntry( u"PositionPrecision"_s, u"/DecimalPlaces"_s, 2 );
1319
1320 const bool defaultRelativePaths = mSettings.value( u"/qgis/defaultProjectPathsRelative"_s, true ).toBool();
1322
1323 int red = mSettings.value( u"qgis/default_canvas_color_red"_s, 255 ).toInt();
1324 int green = mSettings.value( u"qgis/default_canvas_color_green"_s, 255 ).toInt();
1325 int blue = mSettings.value( u"qgis/default_canvas_color_blue"_s, 255 ).toInt();
1326 setBackgroundColor( QColor( red, green, blue ) );
1327
1328 red = mSettings.value( u"qgis/default_selection_color_red"_s, 255 ).toInt();
1329 green = mSettings.value( u"qgis/default_selection_color_green"_s, 255 ).toInt();
1330 blue = mSettings.value( u"qgis/default_selection_color_blue"_s, 0 ).toInt();
1331 const int alpha = mSettings.value( u"qgis/default_selection_color_alpha"_s, 255 ).toInt();
1332 setSelectionColor( QColor( red, green, blue, alpha ) );
1333
1334 mSnappingConfig.clearIndividualLayerSettings();
1335
1337 mRootGroup->clear();
1338 if ( mMainAnnotationLayer )
1339 mMainAnnotationLayer->reset();
1340
1341 snapSingleBlocker.release();
1342
1343 if ( !mBlockSnappingUpdates )
1344 emit snappingConfigChanged( mSnappingConfig );
1345
1346 setDirty( false );
1347 emit homePathChanged();
1348 emit fileNameChanged();
1349 if ( !mBlockChangeSignalsDuringClear )
1350 {
1351 emit verticalCrsChanged();
1352 emit crs3DChanged();
1353 }
1354 emit cleared();
1355}
1356
1357// basically a debugging tool to dump property list values
1358void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
1359{
1360 QgsDebugMsgLevel( u"current properties:"_s, 3 );
1361 topQgsPropertyKey.dump();
1362}
1363
1392void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
1393{
1394 const QDomElement propertiesElem = doc.documentElement().firstChildElement( u"properties"_s );
1395
1396 if ( propertiesElem.isNull() ) // no properties found, so we're done
1397 {
1398 return;
1399 }
1400
1401 const QDomNodeList scopes = propertiesElem.childNodes();
1402
1403 if ( propertiesElem.firstChild().isNull() )
1404 {
1405 QgsDebugError( u"empty ``properties'' XML tag ... bailing"_s );
1406 return;
1407 }
1408
1409 if ( ! project_properties.readXml( propertiesElem ) )
1410 {
1411 QgsDebugError( u"Project_properties.readXml() failed"_s );
1412 }
1413}
1414
1421QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
1422{
1423 QgsPropertyCollection ddServerProperties;
1424 // Read data defined server properties
1425 const QDomElement ddElem = doc.documentElement().firstChildElement( u"dataDefinedServerProperties"_s );
1426 if ( !ddElem.isNull() )
1427 {
1428 if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1429 {
1430 QgsDebugError( u"dataDefinedServerProperties.readXml() failed"_s );
1431 }
1432 }
1433 return ddServerProperties;
1434}
1435
1440static void _getTitle( const QDomDocument &doc, QString &title )
1441{
1442 const QDomElement titleNode = doc.documentElement().firstChildElement( u"title"_s );
1443
1444 title.clear(); // by default the title will be empty
1445
1446 if ( titleNode.isNull() )
1447 {
1448 QgsDebugMsgLevel( u"unable to find title element"_s, 2 );
1449 return;
1450 }
1451
1452 if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1453 {
1454 QgsDebugMsgLevel( u"unable to find title element"_s, 2 );
1455 return;
1456 }
1457
1458 const QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1459
1460 if ( !titleTextNode.isText() )
1461 {
1462 QgsDebugMsgLevel( u"unable to find title element"_s, 2 );
1463 return;
1464 }
1465
1466 const QDomText titleText = titleTextNode.toText();
1467
1468 title = titleText.data();
1469
1470}
1471
1472static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1473{
1474 const QDomNodeList nl = doc.elementsByTagName( u"qgis"_s );
1475
1476 if ( !nl.count() )
1477 {
1478 QgsDebugError( u"unable to find qgis element"_s );
1479 return;
1480 }
1481
1482 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1483
1484 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1485 lastUser = qgisElement.attribute( u"saveUser"_s, QString() );
1486 lastUserFull = qgisElement.attribute( u"saveUserFull"_s, QString() );
1487 lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( u"saveDateTime"_s, QString() ), Qt::ISODate );
1488}
1489
1490QgsProjectVersion getVersion( const QDomDocument &doc )
1491{
1492 const QDomNodeList nl = doc.elementsByTagName( u"qgis"_s );
1493
1494 if ( !nl.count() )
1495 {
1496 QgsDebugError( u" unable to find qgis element in project file"_s );
1497 return QgsProjectVersion( 0, 0, 0, QString() );
1498 }
1499
1500 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1501
1502 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1503 QgsProjectVersion projectVersion( qgisElement.attribute( u"version"_s ) );
1504 return projectVersion;
1505}
1506
1508{
1510
1511 return mSnappingConfig;
1512}
1513
1515{
1517
1518 if ( mSnappingConfig == snappingConfig )
1519 return;
1520
1521 mSnappingConfig = snappingConfig;
1522 setDirty( true );
1523 emit snappingConfigChanged( mSnappingConfig );
1524}
1525
1527{
1529
1530 if ( mAvoidIntersectionsMode == mode )
1531 return;
1532
1533 mAvoidIntersectionsMode = mode;
1535}
1536
1537static QgsMapLayer::ReadFlags projectFlagsToLayerReadFlags( Qgis::ProjectReadFlags projectReadFlags, Qgis::ProjectFlags projectFlags )
1538{
1540 // Propagate don't resolve layers
1541 if ( projectReadFlags & Qgis::ProjectReadFlag::DontResolveLayers )
1543 // Propagate trust layer metadata flag
1544 // Propagate read extent from XML based trust layer metadata flag
1545 if ( ( projectFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( projectReadFlags & Qgis::ProjectReadFlag::TrustLayerMetadata ) )
1546 {
1549 }
1550 // Propagate open layers in read-only mode
1551 if ( ( projectReadFlags & Qgis::ProjectReadFlag::ForceReadOnlyLayers ) )
1552 layerFlags |= QgsMapLayer::FlagForceReadOnly;
1553
1554 return layerFlags;
1555}
1556
1566
1567void QgsProject::preloadProviders( const QVector<QDomNode> &parallelLayerNodes,
1568 const QgsReadWriteContext &context,
1569 QMap<QString, QgsDataProvider *> &loadedProviders,
1570 QgsMapLayer::ReadFlags layerReadFlags,
1571 int totalProviderCount )
1572{
1573 int i = 0;
1574 QEventLoop loop;
1575
1576 QMap<QString, LayerToLoad> layersToLoad;
1577
1578 for ( const QDomNode &node : parallelLayerNodes )
1579 {
1580 LayerToLoad layerToLoad;
1581
1582 const QDomElement layerElement = node.toElement();
1583 layerToLoad.layerElement = layerElement;
1584 layerToLoad.layerId = layerElement.namedItem( u"id"_s ).toElement().text();
1585 layerToLoad.provider = layerElement.namedItem( u"provider"_s ).toElement().text();
1586 layerToLoad.dataSource = layerElement.namedItem( u"datasource"_s ).toElement().text();
1587
1588 layerToLoad.dataSource = QgsProviderRegistry::instance()->relativeToAbsoluteUri( layerToLoad.provider, layerToLoad.dataSource, context );
1589
1590 layerToLoad.options = QgsDataProvider::ProviderOptions( {context.transformContext()} );
1591 layerToLoad.flags = QgsMapLayer::providerReadFlags( node, layerReadFlags );
1592
1593 // Requesting credential from worker thread could lead to deadlocks because the main thread is waiting for worker thread to fininsh
1594 layerToLoad.flags.setFlag( Qgis::DataProviderReadFlag::SkipCredentialsRequest, true );
1595 layerToLoad.flags.setFlag( Qgis::DataProviderReadFlag::ParallelThreadLoading, true );
1596
1597 layersToLoad.insert( layerToLoad.layerId, layerToLoad );
1598 }
1599
1600 while ( !layersToLoad.isEmpty() )
1601 {
1602 const QList<LayerToLoad> layersToAttemptInParallel = layersToLoad.values();
1603 QString layerToAttemptInMainThread;
1604
1605 QHash<QString, QgsRunnableProviderCreator *> runnables;
1606 QThreadPool threadPool;
1607 threadPool.setMaxThreadCount( QgsSettingsRegistryCore::settingsLayerParallelLoadingMaxCount->value() );
1608
1609 for ( const LayerToLoad &lay : layersToAttemptInParallel )
1610 {
1611 QgsRunnableProviderCreator *run = new QgsRunnableProviderCreator( lay.layerId, lay.provider, lay.dataSource, lay.options, lay.flags );
1612 runnables.insert( lay.layerId, run );
1613
1614 QObject::connect( run, &QgsRunnableProviderCreator::providerCreated, run, [&]( bool isValid, const QString & layId )
1615 {
1616 if ( isValid )
1617 {
1618 layersToLoad.remove( layId );
1619 i++;
1620 QgsRunnableProviderCreator *finishedRun = runnables.value( layId, nullptr );
1621 Q_ASSERT( finishedRun );
1622
1623 std::unique_ptr<QgsDataProvider> provider( finishedRun->dataProvider() );
1624 Q_ASSERT( provider && provider->isValid() );
1625
1626 loadedProviders.insert( layId, provider.release() );
1627 emit layerLoaded( i, totalProviderCount );
1628 }
1629 else
1630 {
1631 if ( layerToAttemptInMainThread.isEmpty() )
1632 layerToAttemptInMainThread = layId;
1633 threadPool.clear(); //we have to stop all loading provider to try this layer in main thread and maybe have credentials
1634 }
1635
1636 if ( i == parallelLayerNodes.count() || !isValid )
1637 loop.quit();
1638 } );
1639 threadPool.start( run );
1640 }
1641 loop.exec();
1642
1643 threadPool.waitForDone(); // to be sure all threads are finished
1644
1645 qDeleteAll( runnables );
1646
1647 // 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
1648 auto it = layersToLoad.find( layerToAttemptInMainThread );
1649 if ( it != layersToLoad.end() )
1650 {
1651 std::unique_ptr<QgsDataProvider> provider;
1652 QString layerId;
1653 {
1654 const LayerToLoad &lay = it.value();
1655 Qgis::DataProviderReadFlags providerFlags = lay.flags;
1656 providerFlags.setFlag( Qgis::DataProviderReadFlag::SkipCredentialsRequest, false );
1657 providerFlags.setFlag( Qgis::DataProviderReadFlag::ParallelThreadLoading, false );
1658 QgsScopedRuntimeProfile profile( "Create data providers/" + lay.layerId, u"projectload"_s );
1659 provider.reset( QgsProviderRegistry::instance()->createProvider( lay.provider, lay.dataSource, lay.options, providerFlags ) );
1660 i++;
1661 if ( provider && provider->isValid() )
1662 {
1663 emit layerLoaded( i, totalProviderCount );
1664 }
1665 layerId = lay.layerId;
1666 layersToLoad.erase( it );
1667 // can't access "lay" anymore -- it's now been freed
1668 }
1669 loadedProviders.insert( layerId, provider.release() );
1670 }
1671
1672 // if there still are some not loaded providers or some invalid in parallel thread we start again
1673 }
1674
1675}
1676
1677void QgsProject::releaseHandlesToProjectArchive()
1678{
1679 mStyleSettings->removeProjectStyle();
1680}
1681
1682bool QgsProject::rebuildCrs3D( QString *error )
1683{
1684 bool res = true;
1685 if ( !mCrs.isValid() )
1686 {
1687 mCrs3D = QgsCoordinateReferenceSystem();
1688 }
1689 else if ( !mVerticalCrs.isValid() )
1690 {
1691 mCrs3D = mCrs;
1692 }
1693 else
1694 {
1695 switch ( mCrs.type() )
1696 {
1700 mCrs3D = mCrs;
1701 break;
1702
1704 {
1705 QString tempError;
1706 mCrs3D = mCrs.hasVerticalAxis() ? mCrs : QgsCoordinateReferenceSystem::createCompoundCrs( mCrs, mVerticalCrs, error ? *error : tempError );
1707 res = mCrs3D.isValid();
1708 break;
1709 }
1710
1712 // nonsense situation
1713 mCrs3D = QgsCoordinateReferenceSystem();
1714 res = false;
1715 break;
1716
1725 {
1726 QString tempError;
1727 mCrs3D = QgsCoordinateReferenceSystem::createCompoundCrs( mCrs, mVerticalCrs, error ? *error : tempError );
1728 res = mCrs3D.isValid();
1729 break;
1730 }
1731 }
1732 }
1733 return res;
1734}
1735
1736bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, Qgis::ProjectReadFlags flags )
1737{
1739
1740 // Layer order is set by the restoring the legend settings from project file.
1741 // This is done on the 'readProject( ... )' signal
1742
1743 QDomElement layerElement = doc.documentElement().firstChildElement( u"projectlayers"_s ).firstChildElement( u"maplayer"_s );
1744
1745 // process the map layer nodes
1746
1747 if ( layerElement.isNull() ) // if we have no layers to process, bail
1748 {
1749 return true; // Decided to return "true" since it's
1750 // possible for there to be a project with no
1751 // layers; but also, more imporantly, this
1752 // would cause the tests/qgsproject to fail
1753 // since the test suite doesn't currently
1754 // support test layers
1755 }
1756
1757 bool returnStatus = true;
1758 int numLayers = 0;
1759
1760 while ( ! layerElement.isNull() )
1761 {
1762 numLayers++;
1763 layerElement = layerElement.nextSiblingElement( u"maplayer"_s );
1764 }
1765
1766 // order layers based on their dependencies
1767 QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), u"projectload"_s );
1768 const QgsLayerDefinition::DependencySorter depSorter( doc );
1769 if ( depSorter.hasCycle() )
1770 return false;
1771
1772 // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1773 if ( depSorter.hasMissingDependency() )
1774 returnStatus = false;
1775
1776 emit layerLoaded( 0, numLayers );
1777
1778 const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1779 const int totalLayerCount = sortedLayerNodes.count();
1780
1781 QVector<QDomNode> parallelLoading;
1782 QMap<QString, QgsDataProvider *> loadedProviders;
1783
1786 {
1787 profile.switchTask( tr( "Load providers in parallel" ) );
1788 for ( const QDomNode &node : sortedLayerNodes )
1789 {
1790 const QDomElement element = node.toElement();
1791 if ( element.attribute( u"embedded"_s ) != "1"_L1 )
1792 {
1793 const QString layerId = node.namedItem( u"id"_s ).toElement().text();
1794 if ( !depSorter.isLayerDependent( layerId ) )
1795 {
1796 const QDomNode mnl = element.namedItem( u"provider"_s );
1797 const QDomElement mne = mnl.toElement();
1798 const QString provider = mne.text();
1799 QgsProviderMetadata *meta = QgsProviderRegistry::instance()->providerMetadata( provider );
1800 if ( meta && meta->providerCapabilities().testFlag( QgsProviderMetadata::ParallelCreateProvider ) )
1801 {
1802 parallelLoading.append( node );
1803 continue;
1804 }
1805 }
1806 }
1807 }
1808
1809 QgsReadWriteContext context;
1810 context.setPathResolver( pathResolver() );
1811 if ( !parallelLoading.isEmpty() )
1812 preloadProviders( parallelLoading, context, loadedProviders, projectFlagsToLayerReadFlags( flags, mFlags ), sortedLayerNodes.count() );
1813 }
1814
1815 int i = loadedProviders.count();
1816 for ( const QDomNode &node : std::as_const( sortedLayerNodes ) )
1817 {
1818 const QDomElement element = node.toElement();
1819 const QString name = translate( u"project:layers:%1"_s.arg( node.namedItem( u"id"_s ).toElement().text() ), node.namedItem( u"layername"_s ).toElement().text() );
1820 if ( !name.isNull() )
1821 emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1822
1823 profile.switchTask( name );
1824 if ( element.attribute( u"embedded"_s ) == "1"_L1 )
1825 {
1826 createEmbeddedLayer( element.attribute( u"id"_s ), readPath( element.attribute( u"project"_s ) ), brokenNodes, true, flags );
1827 }
1828 else
1829 {
1830 QgsReadWriteContext context;
1831 context.setPathResolver( pathResolver() );
1832 context.setProjectTranslator( this );
1834 QString layerId = element.namedItem( u"id"_s ).toElement().text();
1835 context.setCurrentLayerId( layerId );
1836 if ( !addLayer( element, brokenNodes, context, flags, loadedProviders.take( layerId ) ) )
1837 {
1838 returnStatus = false;
1839 }
1840 const auto messages = context.takeMessages();
1841 if ( !messages.isEmpty() )
1842 {
1843 emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1844 }
1845 }
1846 emit layerLoaded( i + 1, totalLayerCount );
1847 i++;
1848 }
1849
1850 return returnStatus;
1851}
1852
1853bool QgsProject::addLayer( const QDomElement &layerElem,
1854 QList<QDomNode> &brokenNodes,
1855 QgsReadWriteContext &context,
1857 QgsDataProvider *provider )
1858{
1860
1861 const QString type = layerElem.attribute( u"type"_s );
1862 QgsDebugMsgLevel( "Layer type is " + type, 4 );
1863 std::unique_ptr<QgsMapLayer> mapLayer;
1864
1865 QgsScopedRuntimeProfile profile( tr( "Create layer" ), u"projectload"_s );
1866
1867 bool ok = false;
1868 const Qgis::LayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1869 if ( !ok )
1870 {
1871 QgsDebugError( u"Unknown layer type \"%1\""_s.arg( type ) );
1872 return false;
1873 }
1874
1875 switch ( layerType )
1876 {
1878 mapLayer = std::make_unique<QgsVectorLayer>();
1879 break;
1880
1882 mapLayer = std::make_unique<QgsRasterLayer>();
1883 break;
1884
1886 mapLayer = std::make_unique<QgsMeshLayer>();
1887 break;
1888
1890 mapLayer = std::make_unique<QgsVectorTileLayer>();
1891 break;
1892
1894 mapLayer = std::make_unique<QgsPointCloudLayer>();
1895 break;
1896
1898 mapLayer = std::make_unique<QgsTiledSceneLayer>();
1899 break;
1900
1902 {
1903 const QString typeName = layerElem.attribute( u"name"_s );
1904 mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1905 break;
1906 }
1907
1909 {
1910 const QgsAnnotationLayer::LayerOptions options( mTransformContext );
1911 mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1912 break;
1913 }
1914
1916 {
1917 const QgsGroupLayer::LayerOptions options( mTransformContext );
1918 mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1919 break;
1920 }
1921 }
1922
1923 if ( !mapLayer )
1924 {
1925 QgsDebugError( u"Unable to create layer"_s );
1926 return false;
1927 }
1928
1929 Q_CHECK_PTR( mapLayer ); // NOLINT
1930
1931 // This is tricky: to avoid a leak we need to check if the layer was already in the store
1932 // because if it was, the newly created layer will not be added to the store and it would leak.
1933 const QString layerId { layerElem.namedItem( u"id"_s ).toElement().text() };
1934 Q_ASSERT( ! layerId.isEmpty() );
1935 const bool layerWasStored = layerStore()->mapLayer( layerId );
1936
1937 // have the layer restore state that is stored in Dom node
1938 QgsMapLayer::ReadFlags layerFlags = projectFlagsToLayerReadFlags( flags, mFlags );
1939
1940 profile.switchTask( tr( "Load layer source" ) );
1941 const bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags, provider ) && mapLayer->isValid();
1942
1943 // apply specific settings to vector layer
1944 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1945 {
1946 vl->setReadExtentFromXml( layerFlags & QgsMapLayer::FlagReadExtentFromXml );
1947 if ( vl->dataProvider() )
1948 {
1950 vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, evaluateDefaultValues );
1951 }
1952 }
1953
1954 profile.switchTask( tr( "Add layer to project" ) );
1955 QList<QgsMapLayer *> newLayers;
1956 newLayers << mapLayer.get();
1957 if ( layerIsValid || flags & Qgis::ProjectReadFlag::DontResolveLayers )
1958 {
1959 emit readMapLayer( mapLayer.get(), layerElem );
1960 addMapLayers( newLayers );
1961 // Try to resolve references here (this is necessary to set up joined fields that will be possibly used by
1962 // virtual layers that point to this layer's joined field in their query otherwise they won't be valid ),
1963 // a second attempt to resolve references will be done after all layers are loaded
1964 // see https://github.com/qgis/QGIS/issues/46834
1965 if ( QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1966 {
1967 vLayer->joinBuffer()->resolveReferences( this );
1968 }
1969 }
1970 else
1971 {
1972 // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1973 addMapLayers( newLayers, false );
1974 newLayers.first();
1975 QgsDebugError( "Unable to load " + type + " layer" );
1976 brokenNodes.push_back( layerElem );
1977 }
1978
1979 const bool wasEditable = layerElem.attribute( u"editable"_s, u"0"_s ).toInt();
1980 if ( wasEditable )
1981 {
1982 mapLayer->setCustomProperty( u"_layer_was_editable"_s, true );
1983 }
1984 else
1985 {
1986 mapLayer->removeCustomProperty( u"_layer_was_editable"_s );
1987 }
1988
1989 // It should be safe to delete the layer now if layer was stored, because all the store
1990 // had to to was to reset the data source in case the validity changed.
1991 if ( ! layerWasStored )
1992 {
1993 mapLayer.release();
1994 }
1995
1996 return layerIsValid;
1997}
1998
1999bool QgsProject::read( const QString &filename, Qgis::ProjectReadFlags flags )
2000{
2002
2003 mFile.setFileName( filename );
2004 mCachedHomePath.clear();
2005 mProjectScope.reset();
2006
2007 return read( flags );
2008}
2009
2011{
2013
2014 const QString filename = mFile.fileName();
2015 bool returnValue;
2016
2017 if ( QgsProjectStorage *storage = projectStorage() )
2018 {
2019 QTemporaryFile inDevice;
2020 if ( !inDevice.open() )
2021 {
2022 setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
2023 return false;
2024 }
2025
2026 QgsReadWriteContext context;
2027 context.setProjectTranslator( this );
2028 if ( !storage->readProject( filename, &inDevice, context ) )
2029 {
2030 QString err = tr( "Unable to open %1" ).arg( filename );
2031 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2032 if ( !messages.isEmpty() )
2033 err += u"\n\n"_s + messages.last().message();
2034 setError( err );
2035 return false;
2036 }
2037 returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
2038 }
2039 else
2040 {
2041 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2042 {
2043 returnValue = unzip( mFile.fileName(), flags );
2044 }
2045 else
2046 {
2047 mAuxiliaryStorage = std::make_unique< QgsAuxiliaryStorage >( *this );
2048 const QFileInfo finfo( mFile.fileName() );
2049 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( u"%1_attachments.zip"_s.arg( finfo.completeBaseName() ) );
2050 if ( QFile( attachmentsZip ).exists() )
2051 {
2052 auto archive = std::make_unique<QgsArchive>();
2053 if ( archive->unzip( attachmentsZip ) )
2054 {
2055 releaseHandlesToProjectArchive();
2056 mArchive = std::move( archive );
2057 }
2058 }
2059 returnValue = readProjectFile( mFile.fileName(), flags );
2060 }
2061
2062 //on translation we should not change the filename back
2063 if ( !mTranslator )
2064 {
2065 mFile.setFileName( filename );
2066 mCachedHomePath.clear();
2067 mProjectScope.reset();
2068 }
2069 else
2070 {
2071 //but delete the translator
2072 mTranslator.reset( nullptr );
2073 }
2074 }
2075 emit fileNameChanged();
2076 emit homePathChanged();
2077 return returnValue;
2078}
2079
2080bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlags flags )
2081{
2083
2084 // avoid multiple emission of snapping updated signals
2085 ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
2086
2087 QFile projectFile( filename );
2088 clearError();
2089
2090 QgsApplication::profiler()->clear( u"projectload"_s );
2091 QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), u"projectload"_s );
2092
2093 const QString localeFileName = u"%1_%2"_s.arg( QFileInfo( mFile ).baseName(), QgsApplication::settingsLocaleUserLocale->value() );
2094
2095 if ( QFile( u"%1/%2.qm"_s.arg( QFileInfo( mFile ).absolutePath(), localeFileName ) ).exists() )
2096 {
2097 mTranslator = std::make_unique< QTranslator >();
2098 ( void )mTranslator->load( localeFileName, QFileInfo( mFile ).absolutePath() );
2099 }
2100
2101 profile.switchTask( tr( "Reading project file" ) );
2102 auto doc = std::make_unique<QDomDocument>( u"qgis"_s );
2103
2104 if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
2105 {
2106 projectFile.close();
2107
2108 setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
2109
2110 return false;
2111 }
2112
2113 QTextStream textStream( &projectFile );
2114 QString projectString = textStream.readAll();
2115 projectFile.close();
2116
2117 for ( int i = 0; i < 32; i++ )
2118 {
2119 if ( i == 9 || i == 10 || i == 13 )
2120 {
2121 continue;
2122 }
2123 projectString.replace( QChar( i ), u"%1%2%1"_s.arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
2124 }
2125
2126 // location of problem associated with errorMsg
2127 int line, column;
2128 QString errorMsg;
2129 if ( !doc->setContent( projectString, &errorMsg, &line, &column ) )
2130 {
2131 const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
2132 .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
2133 QgsDebugError( errorString );
2134 setError( errorString );
2135
2136 return false;
2137 }
2138
2139 projectFile.close();
2140
2141 QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
2142
2143 // get project version string, if any
2144 const QgsProjectVersion fileVersion = getVersion( *doc );
2145 const QgsProjectVersion thisVersion( Qgis::version() );
2146
2147 profile.switchTask( tr( "Updating project file" ) );
2148 if ( thisVersion > fileVersion )
2149 {
2150 const bool isOlderMajorVersion = fileVersion.majorVersion() < thisVersion.majorVersion();
2151
2152 if ( isOlderMajorVersion )
2153 {
2154 QgsLogger::warning( "Loading a file that was saved with an older "
2155 "version of qgis (saved in " + fileVersion.text() +
2156 ", loaded in " + Qgis::version() +
2157 "). Problems may occur." );
2158 }
2159
2160 QgsProjectFileTransform projectFile( *doc, fileVersion );
2161
2162 // Shows a warning when an old project file is read.
2164 emit oldProjectVersionWarning( fileVersion.text() );
2166 emit readVersionMismatchOccurred( fileVersion.text() );
2167
2168 projectFile.updateRevision( thisVersion );
2169 }
2170 else if ( fileVersion > thisVersion )
2171 {
2172 QgsLogger::warning( "Loading a file that was saved with a newer "
2173 "version of qgis (saved in " + fileVersion.text() +
2174 ", loaded in " + Qgis::version() +
2175 "). Problems may occur." );
2176
2177 emit readVersionMismatchOccurred( fileVersion.text() );
2178 }
2179
2180 // start new project, just keep the file name and auxiliary storage
2181 profile.switchTask( tr( "Creating auxiliary storage" ) );
2182 const QString fileName = mFile.fileName();
2183
2184 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
2185 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
2186
2187 // NOTE [ND] -- I suspect this is wrong, as the archive may contain any number of non-auxiliary
2188 // storage related files from the previously loaded project.
2189 std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
2190 std::unique_ptr<QgsArchive> archive = std::move( mArchive );
2191
2192 // don't emit xxxChanged signals during the clear() call, as we'll be emitting
2193 // them again after reading the properties from the project file
2194 mBlockChangeSignalsDuringClear = true;
2195 clear();
2196 mBlockChangeSignalsDuringClear = false;
2197
2198 // this is ugly, but clear() will have created a new archive and started populating it. We
2199 // need to release handles to this archive now as the subsequent call to move will need
2200 // to delete it, and requires free access to do so.
2201 releaseHandlesToProjectArchive();
2202
2203 mAuxiliaryStorage = std::move( aStorage );
2204 mArchive = std::move( archive );
2205
2206 mFile.setFileName( fileName );
2207 mCachedHomePath.clear();
2208 mProjectScope.reset();
2209 mSaveVersion = fileVersion;
2210
2211 // now get any properties
2212 profile.switchTask( tr( "Reading properties" ) );
2213 _getProperties( *doc, mProperties );
2214
2215 // now get the data defined server properties
2216 mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
2217
2218 QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
2219
2220#if 0
2221 dump_( mProperties );
2222#endif
2223
2224 // get older style project title
2225 QString oldTitle;
2226 _getTitle( *doc, oldTitle );
2227
2228 readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
2229
2230 const QDomNodeList homePathNl = doc->elementsByTagName( u"homePath"_s );
2231 if ( homePathNl.count() > 0 )
2232 {
2233 const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
2234 const QString homePath = homePathElement.attribute( u"path"_s );
2235 if ( !homePath.isEmpty() )
2237 }
2238 else
2239 {
2240 emit homePathChanged();
2241 }
2242
2243 const QColor backgroundColor( readNumEntry( u"Gui"_s, u"/CanvasColorRedPart"_s, 255 ),
2244 readNumEntry( u"Gui"_s, u"/CanvasColorGreenPart"_s, 255 ),
2245 readNumEntry( u"Gui"_s, u"/CanvasColorBluePart"_s, 255 ) );
2247 const QColor selectionColor( readNumEntry( u"Gui"_s, u"/SelectionColorRedPart"_s, 255 ),
2248 readNumEntry( u"Gui"_s, u"/SelectionColorGreenPart"_s, 255 ),
2249 readNumEntry( u"Gui"_s, u"/SelectionColorBluePart"_s, 255 ),
2250 readNumEntry( u"Gui"_s, u"/SelectionColorAlphaPart"_s, 255 ) );
2252
2253
2254 const QString distanceUnitString = readEntry( u"Measurement"_s, u"/DistanceUnits"_s, QString() );
2255 if ( !distanceUnitString.isEmpty() )
2256 setDistanceUnits( QgsUnitTypes::decodeDistanceUnit( distanceUnitString ) );
2257
2258 const QString areaUnitString = readEntry( u"Measurement"_s, u"/AreaUnits"_s, QString() );
2259 if ( !areaUnitString.isEmpty() )
2260 setAreaUnits( QgsUnitTypes::decodeAreaUnit( areaUnitString ) );
2261
2262 setScaleMethod( qgsEnumKeyToValue( readEntry( u"Measurement"_s, u"/ScaleMethod"_s, QString() ), Qgis::ScaleCalculationMethod::HorizontalMiddle ) );
2263
2264 QgsReadWriteContext context;
2265 context.setPathResolver( pathResolver() );
2266 context.setProjectTranslator( this );
2267
2268 //crs
2269 QgsCoordinateReferenceSystem projectCrs;
2270 if ( readNumEntry( u"SpatialRefSys"_s, u"/ProjectionsEnabled"_s, 0 ) )
2271 {
2272 // first preference - dedicated projectCrs node
2273 const QDomNode srsNode = doc->documentElement().namedItem( u"projectCrs"_s );
2274 if ( !srsNode.isNull() )
2275 {
2276 projectCrs.readXml( srsNode );
2277 }
2278
2279 if ( !projectCrs.isValid() )
2280 {
2281 const QString projCrsString = readEntry( u"SpatialRefSys"_s, u"/ProjectCRSProj4String"_s );
2282 const long currentCRS = readNumEntry( u"SpatialRefSys"_s, u"/ProjectCRSID"_s, -1 );
2283 const QString authid = readEntry( u"SpatialRefSys"_s, u"/ProjectCrs"_s );
2284
2285 // authid should be prioritized over all
2286 const bool isUserAuthId = authid.startsWith( "USER:"_L1, Qt::CaseInsensitive );
2287 if ( !authid.isEmpty() && !isUserAuthId )
2288 projectCrs = QgsCoordinateReferenceSystem( authid );
2289
2290 // try the CRS
2291 if ( !projectCrs.isValid() && currentCRS >= 0 )
2292 {
2293 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
2294 }
2295
2296 // if that didn't produce a match, try the proj.4 string
2297 if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
2298 {
2299 projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
2300 }
2301
2302 // last just take the given id
2303 if ( !projectCrs.isValid() )
2304 {
2305 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
2306 }
2307 }
2308 }
2309 mCrs = projectCrs;
2310
2311 //vertical CRS
2312 {
2313 QgsCoordinateReferenceSystem verticalCrs;
2314 const QDomNode verticalCrsNode = doc->documentElement().namedItem( u"verticalCrs"_s );
2315 if ( !verticalCrsNode.isNull() )
2316 {
2317 verticalCrs.readXml( verticalCrsNode );
2318 }
2319 mVerticalCrs = verticalCrs;
2320 }
2321 rebuildCrs3D();
2322
2323 QStringList datumErrors;
2324 if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
2325 {
2326 emit missingDatumTransforms( datumErrors );
2327 }
2329
2330 // map shading
2331 const QDomNode elevationShadingNode = doc->documentElement().namedItem( u"elevation-shading-renderer"_s );
2332 if ( !elevationShadingNode.isNull() )
2333 {
2334 mElevationShadingRenderer.readXml( elevationShadingNode.toElement(), context );
2335 }
2337
2338
2339 //add variables defined in project file - do this early in the reading cycle, as other components
2340 //(e.g. layouts) may depend on these variables
2341 const QStringList variableNames = readListEntry( u"Variables"_s, u"/variableNames"_s );
2342 const QStringList variableValues = readListEntry( u"Variables"_s, u"/variableValues"_s );
2343
2344 mCustomVariables.clear();
2345 if ( variableNames.length() == variableValues.length() )
2346 {
2347 for ( int i = 0; i < variableNames.length(); ++i )
2348 {
2349 mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
2350 }
2351 }
2352 else
2353 {
2354 QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
2355 }
2356
2357 // Register expression functions stored in the project.
2358 // They might be using project variables and might be
2359 // in turn being used by other components (e.g., layouts).
2361
2362 QDomElement element = doc->documentElement().firstChildElement( u"projectMetadata"_s );
2363
2364 if ( !element.isNull() )
2365 {
2366 mMetadata.readMetadataXml( element, context );
2367 }
2368 else
2369 {
2370 // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
2371 mMetadata = QgsProjectMetadata();
2372 }
2373 if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
2374 {
2375 // upgrade older title storage to storing within project metadata.
2376 mMetadata.setTitle( oldTitle );
2377 }
2378 emit metadataChanged();
2379 emit titleChanged();
2380
2381 // Transaction mode
2382 element = doc->documentElement().firstChildElement( u"transaction"_s );
2383 if ( !element.isNull() )
2384 {
2385 mTransactionMode = qgsEnumKeyToValue( element.attribute( u"mode"_s ), Qgis::TransactionMode::Disabled );
2386 }
2387 else
2388 {
2389 // maybe older project => try read autotransaction
2390 element = doc->documentElement().firstChildElement( u"autotransaction"_s );
2391 if ( ! element.isNull() )
2392 {
2393 mTransactionMode = static_cast<Qgis::TransactionMode>( element.attribute( u"active"_s, u"0"_s ).toInt() );
2394 }
2395 }
2396
2397 // read the layer tree from project file
2398 profile.switchTask( tr( "Loading layer tree" ) );
2399 mRootGroup->setCustomProperty( u"loading"_s, 1 );
2400
2401 QDomElement layerTreeElem = doc->documentElement().firstChildElement( u"layer-tree-group"_s );
2402 if ( !layerTreeElem.isNull() )
2403 {
2404 // Use a temporary tree to read the nodes to prevent signals being delivered to the models
2405 QgsLayerTree tempTree;
2406 tempTree.readChildrenFromXml( layerTreeElem, context );
2407 mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
2408 }
2409 else
2410 {
2411 QgsLayerTreeUtils::readOldLegend( mRootGroup.get(), doc->documentElement().firstChildElement( u"legend"_s ) );
2412 }
2413
2414 mLayerTreeRegistryBridge->setEnabled( false );
2415
2416 // get the map layers
2417 profile.switchTask( tr( "Reading map layers" ) );
2418
2419 loadProjectFlags( doc.get() );
2420
2421 QList<QDomNode> brokenNodes;
2422 const bool clean = _getMapLayers( *doc, brokenNodes, flags );
2423
2424 // review the integrity of the retrieved map layers
2425 if ( !clean && !( flags & Qgis::ProjectReadFlag::DontResolveLayers ) )
2426 {
2427 QgsDebugError( u"Unable to get map layers from project file."_s );
2428
2429 if ( !brokenNodes.isEmpty() )
2430 {
2431 QgsDebugError( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
2432 }
2433
2434 // we let a custom handler decide what to do with missing layers
2435 // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
2436 mBadLayerHandler->handleBadLayers( brokenNodes );
2437 }
2438
2439 mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( u"main-annotation-layer"_s ), context );
2440 mMainAnnotationLayer->setTransformContext( mTransformContext );
2441
2442 // load embedded groups and layers
2443 profile.switchTask( tr( "Loading embedded layers" ) );
2444 loadEmbeddedNodes( mRootGroup.get(), flags );
2445
2446 // Resolve references to other layers
2447 // Needs to be done here once all dependent layers are loaded
2448 profile.switchTask( tr( "Resolving layer references" ) );
2449 QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
2450 for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
2451 {
2452 it.value()->resolveReferences( this );
2453 }
2454 mMainAnnotationLayer->resolveReferences( this );
2455
2456 mLayerTreeRegistryBridge->setEnabled( true );
2457
2458 // now that layers are loaded, we can resolve layer tree's references to the layers
2459 profile.switchTask( tr( "Resolving references" ) );
2460 mRootGroup->resolveReferences( this );
2461
2462 // we need to migrate old fashion designed QgsSymbolLayerReference to new ones
2463 if ( QgsProjectVersion( 3, 28, 0 ) > mSaveVersion )
2464 {
2468 }
2469
2470 if ( !layerTreeElem.isNull() )
2471 {
2472 mRootGroup->readLayerOrderFromXml( layerTreeElem );
2473 }
2474
2475 // Load pre 3.0 configuration
2476 const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( u"layer-tree-canvas"_s );
2477 if ( !layerTreeCanvasElem.isNull( ) )
2478 {
2479 mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
2480 }
2481
2482 // Convert pre 3.4 to create layers flags
2483 if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
2484 {
2485 const QStringList requiredLayerIds = readListEntry( u"RequiredLayers"_s, u"Layers"_s );
2486 for ( const QString &layerId : requiredLayerIds )
2487 {
2488 if ( QgsMapLayer *layer = mapLayer( layerId ) )
2489 {
2490 layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
2491 }
2492 }
2493 const QStringList disabledLayerIds = readListEntry( u"Identify"_s, u"/disabledLayers"_s );
2494 for ( const QString &layerId : disabledLayerIds )
2495 {
2496 if ( QgsMapLayer *layer = mapLayer( layerId ) )
2497 {
2498 layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
2499 }
2500 }
2501 }
2502
2503 // Convert pre 3.26 default styles
2504 if ( QgsProjectVersion( 3, 26, 0 ) > mSaveVersion )
2505 {
2506 // Convert default symbols
2507 QString styleName = readEntry( u"DefaultStyles"_s, u"/Marker"_s );
2508 if ( !styleName.isEmpty() )
2509 {
2510 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2512 }
2513 styleName = readEntry( u"DefaultStyles"_s, u"/Line"_s );
2514 if ( !styleName.isEmpty() )
2515 {
2516 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2518 }
2519 styleName = readEntry( u"DefaultStyles"_s, u"/Fill"_s );
2520 if ( !styleName.isEmpty() )
2521 {
2522 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
2524 }
2525 styleName = readEntry( u"DefaultStyles"_s, u"/ColorRamp"_s );
2526 if ( !styleName.isEmpty() )
2527 {
2528 std::unique_ptr<QgsColorRamp> colorRamp( QgsStyle::defaultStyle()->colorRamp( styleName ) );
2529 styleSettings()->setDefaultColorRamp( colorRamp.get() );
2530 }
2531
2532 // Convert randomize default symbol fill color
2533 styleSettings()->setRandomizeDefaultSymbolColor( readBoolEntry( u"DefaultStyles"_s, u"/RandomColors"_s, true ) );
2534
2535 // Convert default symbol opacity
2536 double opacity = 1.0;
2537 bool ok = false;
2538 // upgrade old setting
2539 double alpha = readDoubleEntry( u"DefaultStyles"_s, u"/AlphaInt"_s, 255, &ok );
2540 if ( ok )
2541 opacity = alpha / 255.0;
2542 double newOpacity = readDoubleEntry( u"DefaultStyles"_s, u"/Opacity"_s, 1.0, &ok );
2543 if ( ok )
2544 opacity = newOpacity;
2546
2547 // Cleanup
2548 removeEntry( u"DefaultStyles"_s, u"/Marker"_s );
2549 removeEntry( u"DefaultStyles"_s, u"/Line"_s );
2550 removeEntry( u"DefaultStyles"_s, u"/Fill"_s );
2551 removeEntry( u"DefaultStyles"_s, u"/ColorRamp"_s );
2552 removeEntry( u"DefaultStyles"_s, u"/RandomColors"_s );
2553 removeEntry( u"DefaultStyles"_s, u"/AlphaInt"_s );
2554 removeEntry( u"DefaultStyles"_s, u"/Opacity"_s );
2555 }
2556
2557 // After bad layer handling we might still have invalid layers,
2558 // store them in case the user wanted to handle them later
2559 // or wanted to pass them through when saving
2561 {
2562 profile.switchTask( tr( "Storing original layer properties" ) );
2563 QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup.get(), doc.get() );
2564 }
2565
2566 mRootGroup->removeCustomProperty( u"loading"_s );
2567
2568 profile.switchTask( tr( "Loading map themes" ) );
2569 mMapThemeCollection = std::make_unique< QgsMapThemeCollection >( this );
2571 mMapThemeCollection->readXml( *doc );
2572
2573 profile.switchTask( tr( "Loading label settings" ) );
2574 mLabelingEngineSettings->readSettingsFromProject( this );
2575 {
2576 const QDomElement labelEngineSettingsElement = doc->documentElement().firstChildElement( u"labelEngineSettings"_s );
2577 mLabelingEngineSettings->readXml( labelEngineSettingsElement, context );
2578 }
2579 mLabelingEngineSettings->resolveReferences( this );
2580
2582
2583 profile.switchTask( tr( "Loading annotations" ) );
2585 {
2586 mAnnotationManager->readXml( doc->documentElement(), context );
2587 }
2588 else
2589 {
2590 mAnnotationManager->readXmlAndUpgradeToAnnotationLayerItems( doc->documentElement(), context, mMainAnnotationLayer, mTransformContext );
2591 }
2593 {
2594 profile.switchTask( tr( "Loading layouts" ) );
2595 mLayoutManager->readXml( doc->documentElement(), *doc );
2596 }
2597
2598 {
2599 profile.switchTask( tr( "Loading elevation profiles" ) );
2600 mElevationProfileManager->readXml( doc->documentElement(), *doc, context );
2601 mElevationProfileManager->resolveReferences( this );
2602 }
2603
2605 {
2606 profile.switchTask( tr( "Loading 3D Views" ) );
2607 m3DViewsManager->readXml( doc->documentElement(), *doc );
2608 }
2609
2610 profile.switchTask( tr( "Loading bookmarks" ) );
2611 mBookmarkManager->readXml( doc->documentElement(), *doc );
2612
2613 profile.switchTask( tr( "Loading sensors" ) );
2614 mSensorManager->readXml( doc->documentElement(), *doc );
2615
2616 // reassign change dependencies now that all layers are loaded
2617 QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2618 for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
2619 {
2620 it.value()->setDependencies( it.value()->dependencies() );
2621 }
2622
2623 profile.switchTask( tr( "Loading snapping settings" ) );
2624 mSnappingConfig.readProject( *doc );
2625 mAvoidIntersectionsMode = static_cast<Qgis::AvoidIntersectionsMode>( readNumEntry( u"Digitizing"_s, u"/AvoidIntersectionsMode"_s, static_cast<int>( Qgis::AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
2626
2627 profile.switchTask( tr( "Loading view settings" ) );
2628 // restore older project scales settings
2629 mViewSettings->setUseProjectScales( readBoolEntry( u"Scales"_s, u"/useProjectScales"_s ) );
2630 const QStringList scales = readListEntry( u"Scales"_s, u"/ScalesList"_s );
2631 QVector<double> res;
2632 for ( const QString &scale : scales )
2633 {
2634 const QStringList parts = scale.split( ':' );
2635 if ( parts.size() != 2 )
2636 continue;
2637
2638 bool ok = false;
2639 const double denominator = QLocale().toDouble( parts[1], &ok );
2640 if ( ok )
2641 {
2642 res << denominator;
2643 }
2644 }
2645 mViewSettings->setMapScales( res );
2646 const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( u"ProjectViewSettings"_s );
2647 if ( !viewSettingsElement.isNull() )
2648 mViewSettings->readXml( viewSettingsElement, context );
2649
2650 // restore style settings
2651 profile.switchTask( tr( "Loading style properties" ) );
2652 const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( u"ProjectStyleSettings"_s );
2653 if ( !styleSettingsElement.isNull() )
2654 {
2655 mStyleSettings->removeProjectStyle();
2656 mStyleSettings->readXml( styleSettingsElement, context, flags );
2657 }
2658
2659 // restore time settings
2660 profile.switchTask( tr( "Loading temporal settings" ) );
2661 const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( u"ProjectTimeSettings"_s );
2662 if ( !timeSettingsElement.isNull() )
2663 mTimeSettings->readXml( timeSettingsElement, context );
2664
2665
2666 profile.switchTask( tr( "Loading elevation properties" ) );
2667 const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( u"ElevationProperties"_s );
2668 if ( !elevationPropertiesElement.isNull() )
2669 mElevationProperties->readXml( elevationPropertiesElement, context );
2670 mElevationProperties->resolveReferences( this );
2671
2672 profile.switchTask( tr( "Loading display settings" ) );
2673 {
2674 const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( u"ProjectDisplaySettings"_s );
2675 if ( !displaySettingsElement.isNull() )
2676 mDisplaySettings->readXml( displaySettingsElement, context );
2677 }
2678
2679 profile.switchTask( tr( "Loading GPS settings" ) );
2680 {
2681 const QDomElement gpsSettingsElement = doc->documentElement().firstChildElement( u"ProjectGpsSettings"_s );
2682 if ( !gpsSettingsElement.isNull() )
2683 mGpsSettings->readXml( gpsSettingsElement, context );
2684 mGpsSettings->resolveReferences( this );
2685 }
2686
2687 profile.switchTask( tr( "Updating variables" ) );
2689 profile.switchTask( tr( "Updating CRS" ) );
2690 emit crsChanged();
2691 if ( verticalCrs() != oldVerticalCrs )
2692 emit verticalCrsChanged();
2693 if ( mCrs3D != oldCrs3D )
2694 emit crs3DChanged();
2695 emit ellipsoidChanged( ellipsoid() );
2696
2697 // read the project: used by map canvas and legend
2698 profile.switchTask( tr( "Reading external settings" ) );
2699 emit readProject( *doc );
2700 emit readProjectWithContext( *doc, context );
2701
2702 profile.switchTask( tr( "Updating interface" ) );
2703
2704 snapSignalBlock.release();
2705 if ( !mBlockSnappingUpdates )
2706 emit snappingConfigChanged( mSnappingConfig );
2707
2710 emit projectColorsChanged();
2711
2712 // if all went well, we're allegedly in pristine state
2713 if ( clean )
2714 setDirty( false );
2715
2716 QgsDebugMsgLevel( u"Project save user: %1"_s.arg( mSaveUser ), 2 );
2717 QgsDebugMsgLevel( u"Project save user: %1"_s.arg( mSaveUserFull ), 2 );
2718
2722
2723 if ( mTranslator )
2724 {
2725 //project possibly translated -> rename it with locale postfix
2726 const QString newFileName( u"%1/%2.qgs"_s.arg( QFileInfo( mFile ).absolutePath(), localeFileName ) );
2727 setFileName( newFileName );
2728
2729 if ( write() )
2730 {
2731 QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Success );
2732 }
2733 else
2734 {
2735 QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Critical );
2736 }
2737 }
2738
2739 // lastly, make any previously editable layers editable
2740 const QMap<QString, QgsMapLayer *> loadedLayers = mapLayers();
2741 for ( auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2742 {
2743 if ( it.value()->isValid() && it.value()->customProperty( u"_layer_was_editable"_s ).toBool() )
2744 {
2745 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2746 vl->startEditing();
2747 it.value()->removeCustomProperty( u"_layer_was_editable"_s );
2748 }
2749 }
2750
2751 return true;
2752}
2753
2754bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
2755{
2757
2758 bool valid = true;
2759 const auto constChildren = group->children();
2760 for ( QgsLayerTreeNode *child : constChildren )
2761 {
2762 if ( QgsLayerTree::isGroup( child ) )
2763 {
2764 QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
2765 if ( childGroup->customProperty( u"embedded"_s ).toInt() )
2766 {
2767 // make sure to convert the path from relative to absolute
2768 const QString projectPath = readPath( childGroup->customProperty( u"embedded_project"_s ).toString() );
2769 childGroup->setCustomProperty( u"embedded_project"_s, projectPath );
2770 std::unique_ptr< QgsLayerTreeGroup > newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( u"embedded-invisible-layers"_s ).toStringList(), flags );
2771 if ( newGroup )
2772 {
2773 QList<QgsLayerTreeNode *> clonedChildren;
2774 const QList<QgsLayerTreeNode *> constChildren = newGroup->children();
2775 clonedChildren.reserve( constChildren.size() );
2776 for ( QgsLayerTreeNode *newGroupChild : constChildren )
2777 clonedChildren << newGroupChild->clone();
2778
2779 childGroup->insertChildNodes( 0, clonedChildren );
2780 }
2781 }
2782 else
2783 {
2784 loadEmbeddedNodes( childGroup, flags );
2785 }
2786 }
2787 else if ( QgsLayerTree::isLayer( child ) )
2788 {
2789 if ( child->customProperty( u"embedded"_s ).toInt() )
2790 {
2791 QList<QDomNode> brokenNodes;
2792 if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( u"embedded_project"_s ).toString() ), brokenNodes, true, flags ) )
2793 {
2794 valid = valid && false;
2795 }
2796 }
2797 }
2798
2799 }
2800
2801 return valid;
2802}
2803
2805{
2806 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
2808
2809 return mCustomVariables;
2810}
2811
2812void QgsProject::setCustomVariables( const QVariantMap &variables )
2813{
2815
2816 if ( variables == mCustomVariables )
2817 return;
2818
2819 //write variable to project
2820 QStringList variableNames;
2821 QStringList variableValues;
2822
2823 QVariantMap::const_iterator it = variables.constBegin();
2824 for ( ; it != variables.constEnd(); ++it )
2825 {
2826 variableNames << it.key();
2827 variableValues << it.value().toString();
2828 }
2829
2830 writeEntry( u"Variables"_s, u"/variableNames"_s, variableNames );
2831 writeEntry( u"Variables"_s, u"/variableValues"_s, variableValues );
2832
2833 mCustomVariables = variables;
2834 mProjectScope.reset();
2835
2837}
2838
2840{
2842
2843 *mLabelingEngineSettings = settings;
2845}
2846
2848{
2850
2851 return *mLabelingEngineSettings;
2852}
2853
2855{
2857
2858 mProjectScope.reset();
2859 return mLayerStore.get();
2860}
2861
2863{
2865
2866 return mLayerStore.get();
2867}
2868
2869QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
2870{
2872
2873 QList<QgsVectorLayer *> layers;
2874 const QStringList layerIds = readListEntry( u"Digitizing"_s, u"/AvoidIntersectionsList"_s, QStringList() );
2875 const auto constLayerIds = layerIds;
2876 for ( const QString &layerId : constLayerIds )
2877 {
2878 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
2879 layers << vlayer;
2880 }
2881 return layers;
2882}
2883
2884void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
2885{
2887
2888 QStringList list;
2889 list.reserve( layers.size() );
2890
2891 for ( QgsVectorLayer *layer : layers )
2892 {
2893 if ( layer->geometryType() == Qgis::GeometryType::Polygon )
2894 list << layer->id();
2895 }
2896
2897 writeEntry( u"Digitizing"_s, u"/AvoidIntersectionsList"_s, list );
2899}
2900
2912
2914{
2915 // this method is called quite extensively using QgsProject::instance() skip-keyword-check
2917
2918 // MUCH cheaper to clone than build
2919 if ( mProjectScope )
2920 {
2921 auto projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2922
2923 // we can't cache these variables
2924 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_distance_units"_s, QgsUnitTypes::toString( distanceUnits() ), true, true ) );
2925 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_area_units"_s, QgsUnitTypes::toString( areaUnits() ), true, true ) );
2926
2927 // neither this function
2928 projectScope->addFunction( u"sensor_data"_s, new GetSensorData( sensorManager()->sensorsData() ) );
2929
2930 return projectScope.release();
2931 }
2932
2933 mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
2934
2935 const QVariantMap vars = customVariables();
2936
2937 QVariantMap::const_iterator it = vars.constBegin();
2938
2939 for ( ; it != vars.constEnd(); ++it )
2940 {
2941 mProjectScope->setVariable( it.key(), it.value(), true );
2942 }
2943
2944 QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
2945 if ( projectPath.isEmpty() )
2946 projectPath = mOriginalPath;
2947 const QString projectFolder = QFileInfo( projectPath ).path();
2948 const QString projectFilename = QFileInfo( projectPath ).fileName();
2949 const QString projectBasename = baseName();
2950
2951 //add other known project variables
2952 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_title"_s, title(), true, true ) );
2953 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_path"_s, QDir::toNativeSeparators( projectPath ), true, true ) );
2954 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_folder"_s, QDir::toNativeSeparators( projectFolder ), true, true ) );
2955 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_filename"_s, projectFilename, true, true ) );
2956 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_basename"_s, projectBasename, true, true ) );
2957 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_home"_s, QDir::toNativeSeparators( homePath() ), true, true ) );
2958 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_last_saved"_s, mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
2959
2960 const QgsCoordinateReferenceSystem projectCrs = crs();
2961 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_crs"_s, projectCrs.authid(), true, true ) );
2962 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_crs_definition"_s, projectCrs.toProj(), true, true ) );
2963 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_crs_description"_s, projectCrs.description(), true, true ) );
2964 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_crs_acronym"_s, projectCrs.projectionAcronym(), true ) );
2965 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_crs_ellipsoid"_s, projectCrs.ellipsoidAcronym(), true ) );
2966 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_crs_proj4"_s, projectCrs.toProj(), true ) );
2967 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_crs_wkt"_s, projectCrs.toWkt( Qgis::CrsWktVariant::Preferred ), true ) );
2968
2969 const QgsCoordinateReferenceSystem projectVerticalCrs = QgsProject::verticalCrs();
2970 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_vertical_crs"_s, projectVerticalCrs.authid(), true, true ) );
2971 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_vertical_crs_definition"_s, projectVerticalCrs.toProj(), true, true ) );
2972 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_vertical_crs_description"_s, projectVerticalCrs.description(), true, true ) );
2973 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_vertical_crs_wkt"_s, projectVerticalCrs.toWkt( Qgis::CrsWktVariant::Preferred ), true ) );
2974
2975 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_ellipsoid"_s, ellipsoid(), true, true ) );
2976 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"_project_transform_context"_s, QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
2977 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_units"_s, QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
2978
2979 // metadata
2980 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_author"_s, metadata().author(), true, true ) );
2981 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_abstract"_s, metadata().abstract(), true, true ) );
2982 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_creation_date"_s, metadata().creationDateTime(), true, true ) );
2983 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_identifier"_s, metadata().identifier(), true, true ) );
2984
2985 // keywords
2986 QVariantMap keywords;
2987 const QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2988 for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2989 {
2990 keywords.insert( it.key(), it.value() );
2991 }
2992 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"project_keywords"_s, keywords, true, true ) );
2993
2994 // layers
2995 QVariantList layersIds;
2996 QVariantList layers;
2997 const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2998 layersIds.reserve( layersInProject.count() );
2999 layers.reserve( layersInProject.count() );
3000 for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
3001 {
3002 layersIds << it.value()->id();
3004 }
3005 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"layer_ids"_s, layersIds, true ) );
3006 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( u"layers"_s, layers, true ) );
3007
3008 mProjectScope->addFunction( u"project_color"_s, new GetNamedProjectColor( this ) );
3009 mProjectScope->addFunction( u"project_color_object"_s, new GetNamedProjectColorObject( this ) );
3010
3012}
3013
3014void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
3015{
3017
3018 const QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
3019
3020 const auto constLayers = layers;
3021 for ( QgsMapLayer *layer : constLayers )
3022 {
3023 if ( ! layer->isValid() )
3024 return;
3025
3026 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer ) )
3027 {
3028 vlayer->setReadExtentFromXml( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics );
3029 if ( vlayer->dataProvider() )
3030 vlayer->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues,
3032 }
3033
3034 connect( layer, &QgsMapLayer::configChanged, this, [this] { setDirty(); } );
3035
3036 // check if we have to update connections for layers with dependencies
3037 for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
3038 {
3039 const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
3040 if ( deps.contains( layer->id() ) )
3041 {
3042 // reconnect to change signals
3043 it.value()->setDependencies( deps );
3044 }
3045 }
3046 }
3047
3048 updateTransactionGroups();
3049
3050 if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
3051 emit snappingConfigChanged( mSnappingConfig );
3052}
3053
3054void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
3055{
3057
3058 if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
3059 emit snappingConfigChanged( mSnappingConfig );
3060
3061 for ( QgsMapLayer *layer : layers )
3062 {
3063 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
3064 if ( ! vlayer )
3065 continue;
3066
3067 mEditBufferGroup.removeLayer( vlayer );
3068 }
3069}
3070
3071void QgsProject::cleanTransactionGroups( bool force )
3072{
3074
3075 bool changed = false;
3076 for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
3077 {
3078 if ( tg.value()->isEmpty() || force )
3079 {
3080 delete tg.value();
3081 tg = mTransactionGroups.erase( tg );
3082 changed = true;
3083 }
3084 else
3085 {
3086 ++tg;
3087 }
3088 }
3089 if ( changed )
3091}
3092
3093void QgsProject::updateTransactionGroups()
3094{
3096
3097 mEditBufferGroup.clear();
3098
3099 switch ( mTransactionMode )
3100 {
3102 {
3103 cleanTransactionGroups( true );
3104 return;
3105 }
3106 break;
3108 cleanTransactionGroups( true );
3109 break;
3111 cleanTransactionGroups( false );
3112 break;
3113 }
3114
3115 bool tgChanged = false;
3116 const auto constLayers = mapLayers().values();
3117 for ( QgsMapLayer *layer : constLayers )
3118 {
3119 if ( ! layer->isValid() )
3120 continue;
3121
3122 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
3123 if ( ! vlayer )
3124 continue;
3125
3126 switch ( mTransactionMode )
3127 {
3129 Q_ASSERT( false );
3130 break;
3132 {
3134 {
3135 const QString connString = QgsTransaction::connectionString( vlayer->source() );
3136 const QString key = vlayer->providerType();
3137
3138 QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
3139
3140 if ( !tg )
3141 {
3142 tg = new QgsTransactionGroup();
3143 mTransactionGroups.insert( qMakePair( key, connString ), tg );
3144 tgChanged = true;
3145 }
3146 tg->addLayer( vlayer );
3147 }
3148 }
3149 break;
3151 {
3152 if ( vlayer->supportsEditing() )
3153 mEditBufferGroup.addLayer( vlayer );
3154 }
3155 break;
3156 }
3157 }
3158
3159 if ( tgChanged )
3161}
3162
3163bool QgsProject::readLayer( const QDomNode &layerNode )
3164{
3166
3167 QgsReadWriteContext context;
3168 context.setPathResolver( pathResolver() );
3169 context.setProjectTranslator( this );
3170 context.setTransformContext( transformContext() );
3171 context.setCurrentLayerId( layerNode.toElement().firstChildElement( u"id"_s ).text() );
3172 QList<QDomNode> brokenNodes;
3173 if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
3174 {
3175 // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
3176 // added layer for joins
3177 const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
3178 for ( QgsVectorLayer *layer : vectorLayers )
3179 {
3180 // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
3181 layer->resolveReferences( this );
3182
3183 if ( layer->isValid() && layer->customProperty( u"_layer_was_editable"_s ).toBool() )
3184 {
3185 layer->startEditing();
3186 layer->removeCustomProperty( u"_layer_was_editable"_s );
3187 }
3188 }
3189 return true;
3190 }
3191 return false;
3192}
3193
3194bool QgsProject::write( const QString &filename )
3195{
3197
3198 mFile.setFileName( filename );
3199 emit fileNameChanged();
3200 mCachedHomePath.clear();
3201 return write();
3202}
3203
3205{
3207
3208 mProjectScope.reset();
3209 if ( QgsProjectStorage *storage = projectStorage() )
3210 {
3211 QgsReadWriteContext context;
3212 // for projects stored in a custom storage, we have to check for the support
3213 // of relative paths since the storage most likely will not be in a file system
3214 const QString storageFilePath { storage->filePath( mFile.fileName() ) };
3215 if ( storageFilePath.isEmpty() )
3216 {
3218 }
3219 context.setPathResolver( pathResolver() );
3220
3221 const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
3222 const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
3223
3224 if ( !zip( tmpZipFilename ) )
3225 return false; // zip() already calls setError() when returning false
3226
3227 QFile tmpZipFile( tmpZipFilename );
3228 if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
3229 {
3230 setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
3231 return false;
3232 }
3233
3235 if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
3236 {
3237 QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
3238 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
3239 if ( !messages.isEmpty() )
3240 err += u"\n\n"_s + messages.last().message();
3241 setError( err );
3242 return false;
3243 }
3244
3245 tmpZipFile.close();
3246 QFile::remove( tmpZipFilename );
3247
3248 return true;
3249 }
3250
3251 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
3252 {
3253 return zip( mFile.fileName() );
3254 }
3255 else
3256 {
3257 // write project file even if the auxiliary storage is not correctly
3258 // saved
3259 const bool asOk = saveAuxiliaryStorage();
3260 const bool writeOk = writeProjectFile( mFile.fileName() );
3261 bool attachmentsOk = true;
3262 if ( !mArchive->files().isEmpty() )
3263 {
3264 const QFileInfo finfo( mFile.fileName() );
3265 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( u"%1_attachments.zip"_s.arg( finfo.completeBaseName() ) );
3266 attachmentsOk = mArchive->zip( attachmentsZip );
3267 }
3268
3269 // errors raised during writing project file are more important
3270 if ( ( !asOk || !attachmentsOk ) && writeOk )
3271 {
3272 QStringList errorMessage;
3273 if ( !asOk )
3274 {
3275 const QString err = mAuxiliaryStorage->errorString();
3276 errorMessage.append( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
3277 }
3278 if ( !attachmentsOk )
3279 {
3280 errorMessage.append( tr( "Unable to save attachments archive" ) );
3281 }
3282 setError( errorMessage.join( '\n' ) );
3283 }
3284
3285 return asOk && writeOk && attachmentsOk;
3286 }
3287}
3288
3289bool QgsProject::writeProjectFile( const QString &filename )
3290{
3292
3293 QFile projectFile( filename );
3294 clearError();
3295
3296 // if we have problems creating or otherwise writing to the project file,
3297 // let's find out up front before we go through all the hand-waving
3298 // necessary to create all the Dom objects
3299 const QFileInfo myFileInfo( projectFile );
3300 if ( myFileInfo.exists() && !myFileInfo.isWritable() )
3301 {
3302 setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
3303 .arg( projectFile.fileName() ) );
3304 return false;
3305 }
3306
3307 QgsReadWriteContext context;
3308 context.setPathResolver( pathResolver() );
3310
3311 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
3312
3313 const QDomDocumentType documentType =
3314 QDomImplementation().createDocumentType( u"qgis"_s, u"http://mrcc.com/qgis.dtd"_s,
3315 u"SYSTEM"_s );
3316 auto doc = std::make_unique<QDomDocument>( documentType );
3317
3318 QDomElement qgisNode = doc->createElement( u"qgis"_s );
3319 qgisNode.setAttribute( u"projectname"_s, title() );
3320 qgisNode.setAttribute( u"version"_s, Qgis::version() );
3321
3322 if ( !mSettings.value( u"projects/anonymize_saved_projects"_s, false, QgsSettings::Core ).toBool() )
3323 {
3324 const QString newSaveUser = QgsApplication::userLoginName();
3325 const QString newSaveUserFull = QgsApplication::userFullName();
3326 qgisNode.setAttribute( u"saveUser"_s, newSaveUser );
3327 qgisNode.setAttribute( u"saveUserFull"_s, newSaveUserFull );
3328 mSaveUser = newSaveUser;
3329 mSaveUserFull = newSaveUserFull;
3330 if ( mMetadata.author().isEmpty() )
3331 {
3332 mMetadata.setAuthor( QgsApplication::userFullName() );
3333 }
3334 if ( !mMetadata.creationDateTime().isValid() )
3335 {
3336 mMetadata.setCreationDateTime( QDateTime( QDateTime::currentDateTime() ) );
3337 }
3338 mSaveDateTime = QDateTime::currentDateTime();
3339 qgisNode.setAttribute( u"saveDateTime"_s, mSaveDateTime.toString( Qt::ISODate ) );
3340 }
3341 else
3342 {
3343 mSaveUser.clear();
3344 mSaveUserFull.clear();
3345 mMetadata.setAuthor( QString() );
3346 mMetadata.setCreationDateTime( QDateTime() );
3347 mSaveDateTime = QDateTime();
3348 }
3349 doc->appendChild( qgisNode );
3350 mSaveVersion = QgsProjectVersion( Qgis::version() );
3351
3352 QDomElement homePathNode = doc->createElement( u"homePath"_s );
3353 homePathNode.setAttribute( u"path"_s, mHomePath );
3354 qgisNode.appendChild( homePathNode );
3355
3356 // title
3357 QDomElement titleNode = doc->createElement( u"title"_s );
3358 qgisNode.appendChild( titleNode );
3359
3360 QDomElement transactionNode = doc->createElement( u"transaction"_s );
3361 transactionNode.setAttribute( u"mode"_s, qgsEnumValueToKey( mTransactionMode ) );
3362 qgisNode.appendChild( transactionNode );
3363
3364 QDomElement flagsNode = doc->createElement( u"projectFlags"_s );
3365 flagsNode.setAttribute( u"set"_s, qgsFlagValueToKeys( mFlags ) );
3366 qgisNode.appendChild( flagsNode );
3367
3368 const QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
3369 titleNode.appendChild( titleText );
3370
3371 // write project CRS
3372 {
3373 QDomElement srsNode = doc->createElement( u"projectCrs"_s );
3374 mCrs.writeXml( srsNode, *doc );
3375 qgisNode.appendChild( srsNode );
3376 }
3377 {
3378 QDomElement verticalSrsNode = doc->createElement( u"verticalCrs"_s );
3379 mVerticalCrs.writeXml( verticalSrsNode, *doc );
3380 qgisNode.appendChild( verticalSrsNode );
3381 }
3382
3383 QDomElement elevationShadingNode = doc->createElement( u"elevation-shading-renderer"_s );
3384 mElevationShadingRenderer.writeXml( elevationShadingNode, context );
3385 qgisNode.appendChild( elevationShadingNode );
3386
3387 // write layer tree - make sure it is without embedded subgroups
3388 std::unique_ptr< QgsLayerTreeNode > clonedRoot( mRootGroup->clone() );
3390 QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot.get() ), this ); // convert absolute paths to relative paths if required
3391
3392 clonedRoot->writeXml( qgisNode, context );
3393 clonedRoot.reset();
3394
3395 mSnappingConfig.writeProject( *doc );
3396 writeEntry( u"Digitizing"_s, u"/AvoidIntersectionsMode"_s, static_cast<int>( mAvoidIntersectionsMode ) );
3397
3398 // let map canvas and legend write their information
3399 emit writeProject( *doc );
3400
3401 // within top level node save list of layers
3402 const QMap<QString, QgsMapLayer *> layers = mapLayers();
3403
3404 QDomElement annotationLayerNode = doc->createElement( u"main-annotation-layer"_s );
3405 mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
3406 qgisNode.appendChild( annotationLayerNode );
3407
3408 // Iterate over layers in zOrder
3409 // Call writeXml() on each
3410 QDomElement projectLayersNode = doc->createElement( u"projectlayers"_s );
3411
3412 QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
3413 while ( li != layers.end() )
3414 {
3415 QgsMapLayer *ml = li.value();
3416
3417 if ( ml )
3418 {
3419 const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
3420 if ( emIt == mEmbeddedLayers.constEnd() )
3421 {
3422 QDomElement maplayerElem;
3423 // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
3424 // not available, just write what we DO have
3425 if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
3426 {
3427 // general layer metadata
3428 maplayerElem = doc->createElement( u"maplayer"_s );
3429 ml->writeLayerXml( maplayerElem, *doc, context );
3430
3432 maplayerElem.setAttribute( u"editable"_s, u"1"_s );
3433 }
3434 else if ( ! ml->originalXmlProperties().isEmpty() )
3435 {
3436 QDomDocument document;
3437 if ( document.setContent( ml->originalXmlProperties() ) )
3438 {
3439 maplayerElem = document.firstChildElement();
3440 }
3441 else
3442 {
3443 QgsDebugError( u"Could not restore layer properties for layer %1"_s.arg( ml->id() ) );
3444 }
3445 }
3446
3447 emit writeMapLayer( ml, maplayerElem, *doc );
3448
3449 projectLayersNode.appendChild( maplayerElem );
3450 }
3451 else
3452 {
3453 // layer defined in an external project file
3454 // only save embedded layer if not managed by a legend group
3455 if ( emIt.value().second )
3456 {
3457 QDomElement mapLayerElem = doc->createElement( u"maplayer"_s );
3458 mapLayerElem.setAttribute( u"embedded"_s, 1 );
3459 mapLayerElem.setAttribute( u"project"_s, writePath( emIt.value().first ) );
3460 mapLayerElem.setAttribute( u"id"_s, ml->id() );
3461 projectLayersNode.appendChild( mapLayerElem );
3462 }
3463 }
3464 }
3465 li++;
3466 }
3467
3468 qgisNode.appendChild( projectLayersNode );
3469
3470 QDomElement layerOrderNode = doc->createElement( u"layerorder"_s );
3471 const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
3472 for ( QgsMapLayer *layer : constCustomLayerOrder )
3473 {
3474 QDomElement mapLayerElem = doc->createElement( u"layer"_s );
3475 mapLayerElem.setAttribute( u"id"_s, layer->id() );
3476 layerOrderNode.appendChild( mapLayerElem );
3477 }
3478 qgisNode.appendChild( layerOrderNode );
3479
3480 mLabelingEngineSettings->writeSettingsToProject( this );
3481 {
3482 QDomElement labelEngineSettingsElement = doc->createElement( u"labelEngineSettings"_s );
3483 mLabelingEngineSettings->writeXml( *doc, labelEngineSettingsElement, context );
3484 qgisNode.appendChild( labelEngineSettingsElement );
3485 }
3486
3487 writeEntry( u"Gui"_s, u"/CanvasColorRedPart"_s, mBackgroundColor.red() );
3488 writeEntry( u"Gui"_s, u"/CanvasColorGreenPart"_s, mBackgroundColor.green() );
3489 writeEntry( u"Gui"_s, u"/CanvasColorBluePart"_s, mBackgroundColor.blue() );
3490
3491 writeEntry( u"Gui"_s, u"/SelectionColorRedPart"_s, mSelectionColor.red() );
3492 writeEntry( u"Gui"_s, u"/SelectionColorGreenPart"_s, mSelectionColor.green() );
3493 writeEntry( u"Gui"_s, u"/SelectionColorBluePart"_s, mSelectionColor.blue() );
3494 writeEntry( u"Gui"_s, u"/SelectionColorAlphaPart"_s, mSelectionColor.alpha() );
3495
3496 writeEntry( u"Measurement"_s, u"/DistanceUnits"_s, QgsUnitTypes::encodeUnit( mDistanceUnits ) );
3497 writeEntry( u"Measurement"_s, u"/AreaUnits"_s, QgsUnitTypes::encodeUnit( mAreaUnits ) );
3498 writeEntry( u"Measurement"_s, u"/ScaleMethod"_s, qgsEnumValueToKey( mScaleMethod ) );
3499
3500 // now add the optional extra properties
3501#if 0
3502 dump_( mProperties );
3503#endif
3504
3505 QgsDebugMsgLevel( u"there are %1 property scopes"_s.arg( static_cast<int>( mProperties.count() ) ), 2 );
3506
3507 if ( !mProperties.isEmpty() ) // only worry about properties if we
3508 // actually have any properties
3509 {
3510 mProperties.writeXml( u"properties"_s, qgisNode, *doc );
3511 }
3512
3513 QDomElement ddElem = doc->createElement( u"dataDefinedServerProperties"_s );
3514 mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
3515 qgisNode.appendChild( ddElem );
3516
3517 mMapThemeCollection->writeXml( *doc );
3518
3519 mTransformContext.writeXml( qgisNode, context );
3520
3521 QDomElement metadataElem = doc->createElement( u"projectMetadata"_s );
3522 mMetadata.writeMetadataXml( metadataElem, *doc );
3523 qgisNode.appendChild( metadataElem );
3524
3525 {
3526 const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
3527 qgisNode.appendChild( annotationsElem );
3528 }
3529
3530 {
3531 const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
3532 qgisNode.appendChild( layoutElem );
3533 }
3534
3535 {
3536 const QDomElement elevationProfileElem = mElevationProfileManager->writeXml( *doc, context );
3537 qgisNode.appendChild( elevationProfileElem );
3538 }
3539
3540 {
3541 const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
3542 qgisNode.appendChild( views3DElem );
3543 }
3544
3545 {
3546 const QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
3547 qgisNode.appendChild( bookmarkElem );
3548 }
3549
3550 {
3551 const QDomElement sensorElem = mSensorManager->writeXml( *doc );
3552 qgisNode.appendChild( sensorElem );
3553 }
3554
3555 {
3556 const QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
3557 qgisNode.appendChild( viewSettingsElem );
3558 }
3559
3560 {
3561 const QDomElement styleSettingsElem = mStyleSettings->writeXml( *doc, context );
3562 qgisNode.appendChild( styleSettingsElem );
3563 }
3564
3565 {
3566 const QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
3567 qgisNode.appendChild( timeSettingsElement );
3568 }
3569
3570 {
3571 const QDomElement elevationPropertiesElement = mElevationProperties->writeXml( *doc, context );
3572 qgisNode.appendChild( elevationPropertiesElement );
3573 }
3574
3575 {
3576 const QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
3577 qgisNode.appendChild( displaySettingsElem );
3578 }
3579
3580 {
3581 const QDomElement gpsSettingsElem = mGpsSettings->writeXml( *doc, context );
3582 qgisNode.appendChild( gpsSettingsElem );
3583 }
3584
3585 // now wrap it up and ship it to the project file
3586 doc->normalize(); // XXX I'm not entirely sure what this does
3587
3588 // Create backup file
3589 if ( QFile::exists( fileName() ) )
3590 {
3591 QFile backupFile( u"%1~"_s.arg( filename ) );
3592 bool ok = true;
3593 ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
3594 ok &= projectFile.open( QIODevice::ReadOnly );
3595
3596 QByteArray ba;
3597 while ( ok && !projectFile.atEnd() )
3598 {
3599 ba = projectFile.read( 10240 );
3600 ok &= backupFile.write( ba ) == ba.size();
3601 }
3602
3603 projectFile.close();
3604 backupFile.close();
3605
3606 if ( !ok )
3607 {
3608 setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
3609 return false;
3610 }
3611
3612 const QFileInfo fi( fileName() );
3613 struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
3614 utime( backupFile.fileName().toUtf8().constData(), &tb );
3615 }
3616
3617 if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3618 {
3619 projectFile.close(); // even though we got an error, let's make
3620 // sure it's closed anyway
3621
3622 setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
3623 return false;
3624 }
3625
3626 QTemporaryFile tempFile;
3627 bool ok = tempFile.open();
3628 if ( ok )
3629 {
3630 QTextStream projectFileStream( &tempFile );
3631 doc->save( projectFileStream, 2 ); // save as utf-8
3632 ok &= projectFileStream.pos() > -1;
3633
3634 ok &= tempFile.seek( 0 );
3635
3636 QByteArray ba;
3637 while ( ok && !tempFile.atEnd() )
3638 {
3639 ba = tempFile.read( 10240 );
3640 ok &= projectFile.write( ba ) == ba.size();
3641 }
3642
3643 ok &= projectFile.error() == QFile::NoError;
3644
3645 projectFile.close();
3646 }
3647
3648 tempFile.close();
3649
3650 if ( !ok )
3651 {
3652 setError( tr( "Unable to save to file %1. Your project "
3653 "may be corrupted on disk. Try clearing some space on the volume and "
3654 "check file permissions before pressing save again." )
3655 .arg( projectFile.fileName() ) );
3656 return false;
3657 }
3658
3659 setDirty( false ); // reset to pristine state
3660
3661 emit projectSaved();
3662 return true;
3663}
3664
3665bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
3666{
3668
3669 bool propertiesModified;
3670 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3671
3672 if ( propertiesModified )
3673 setDirty( true );
3674
3675 return success;
3676}
3677
3678bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
3679{
3681
3682 bool propertiesModified;
3683 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3684
3685 if ( propertiesModified )
3686 setDirty( true );
3687
3688 return success;
3689}
3690
3691bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
3692{
3694
3695 bool propertiesModified;
3696 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3697
3698 if ( propertiesModified )
3699 setDirty( true );
3700
3701 return success;
3702}
3703
3704bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
3705{
3707
3708 bool propertiesModified;
3709 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3710
3711 if ( propertiesModified )
3712 setDirty( true );
3713
3714 return success;
3715}
3716
3717bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
3718{
3720
3721 bool propertiesModified;
3722 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
3723
3724 if ( propertiesModified )
3725 setDirty( true );
3726
3727 return success;
3728}
3729
3730QStringList QgsProject::readListEntry( const QString &scope,
3731 const QString &key,
3732 const QStringList &def,
3733 bool *ok ) const
3734{
3735 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
3737
3738 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3739
3740 QVariant value;
3741
3742 if ( property )
3743 {
3744 value = property->value();
3745
3746 const bool valid = QMetaType::Type::QStringList == value.userType();
3747 if ( ok )
3748 *ok = valid;
3749
3750 if ( valid )
3751 {
3752 return value.toStringList();
3753 }
3754 }
3755 else if ( ok )
3756 *ok = false;
3757
3758
3759 return def;
3760}
3761
3762QString QgsProject::readEntry( const QString &scope,
3763 const QString &key,
3764 const QString &def,
3765 bool *ok ) const
3766{
3768
3769 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3770
3771 QVariant value;
3772
3773 if ( property )
3774 {
3775 value = property->value();
3776
3777 const bool valid = value.canConvert( QMetaType::Type::QString );
3778 if ( ok )
3779 *ok = valid;
3780
3781 if ( valid )
3782 return value.toString();
3783 }
3784 else if ( ok )
3785 *ok = false;
3786
3787 return def;
3788}
3789
3790int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
3791 bool *ok ) const
3792{
3794
3795 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3796
3797 QVariant value;
3798
3799 if ( property )
3800 {
3801 value = property->value();
3802 }
3803
3804 const bool valid = value.canConvert( QMetaType::Type::Int );
3805
3806 if ( ok )
3807 {
3808 *ok = valid;
3809 }
3810
3811 if ( valid )
3812 {
3813 return value.toInt();
3814 }
3815
3816 return def;
3817}
3818
3819double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
3820 double def,
3821 bool *ok ) const
3822{
3824
3825 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3826 if ( property )
3827 {
3828 const QVariant value = property->value();
3829
3830 const bool valid = value.canConvert( QMetaType::Type::Double );
3831 if ( ok )
3832 *ok = valid;
3833
3834 if ( valid )
3835 return value.toDouble();
3836 }
3837 else if ( ok )
3838 *ok = false;
3839
3840 return def;
3841}
3842
3843bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
3844 bool *ok ) const
3845{
3847
3848 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3849
3850 if ( property )
3851 {
3852 const QVariant value = property->value();
3853
3854 const bool valid = value.canConvert( QMetaType::Type::Bool );
3855 if ( ok )
3856 *ok = valid;
3857
3858 if ( valid )
3859 return value.toBool();
3860 }
3861 else if ( ok )
3862 *ok = false;
3863
3864 return def;
3865}
3866
3867bool QgsProject::removeEntry( const QString &scope, const QString &key )
3868{
3870
3871 if ( findKey_( scope, key, mProperties ) )
3872 {
3873 removeKey_( scope, key, mProperties );
3874 setDirty( true );
3875 }
3876
3877 return !findKey_( scope, key, mProperties );
3878}
3879
3880QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
3881{
3883
3884 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3885
3886 QStringList entries;
3887
3888 if ( foundProperty )
3889 {
3890 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3891
3892 if ( propertyKey )
3893 { propertyKey->entryList( entries ); }
3894 }
3895
3896 return entries;
3897}
3898
3899QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
3900{
3902
3903 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3904
3905 QStringList entries;
3906
3907 if ( foundProperty )
3908 {
3909 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3910
3911 if ( propertyKey )
3912 { propertyKey->subkeyList( entries ); }
3913 }
3914
3915 return entries;
3916}
3917
3919{
3921
3922 dump_( mProperties );
3923}
3924
3926{
3928
3929 QString filePath;
3930 switch ( filePathStorage() )
3931 {
3933 break;
3934
3936 {
3937 // for projects stored in a custom storage, we need to ask to the
3938 // storage for the path, if the storage returns an empty path
3939 // relative paths are not supported
3940 if ( QgsProjectStorage *storage = projectStorage() )
3941 {
3942 filePath = storage->filePath( mFile.fileName() );
3943 }
3944 else
3945 {
3946 filePath = fileName();
3947 }
3948 break;
3949 }
3950 }
3951
3952 return QgsPathResolver( filePath, mArchive->dir() );
3953}
3954
3955QString QgsProject::readPath( const QString &src ) const
3956{
3958
3959 return pathResolver().readPath( src );
3960}
3961
3962QString QgsProject::writePath( const QString &src ) const
3963{
3965
3966 return pathResolver().writePath( src );
3967}
3968
3969void QgsProject::setError( const QString &errorMessage )
3970{
3972
3973 mErrorMessage = errorMessage;
3974}
3975
3976QString QgsProject::error() const
3977{
3979
3980 return mErrorMessage;
3981}
3982
3983void QgsProject::clearError()
3984{
3986
3987 setError( QString() );
3988}
3989
3991{
3993
3994 mBadLayerHandler.reset( handler );
3995}
3996
3997QString QgsProject::layerIsEmbedded( const QString &id ) const
3998{
4000
4001 const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
4002 if ( it == mEmbeddedLayers.constEnd() )
4003 {
4004 return QString();
4005 }
4006 return it.value().first;
4007}
4008
4009bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
4010 bool saveFlag, Qgis::ProjectReadFlags flags )
4011{
4013
4015
4016 static QString sPrevProjectFilePath;
4017 static QDateTime sPrevProjectFileTimestamp;
4018 static QDomDocument sProjectDocument;
4019
4020 QString qgsProjectFile = projectFilePath;
4021 QgsProjectArchive archive;
4022 if ( projectFilePath.endsWith( ".qgz"_L1, Qt::CaseInsensitive ) )
4023 {
4024 archive.unzip( projectFilePath );
4025 qgsProjectFile = archive.projectFile();
4026 }
4027
4028 const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
4029
4030 if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
4031 {
4032 sPrevProjectFilePath.clear();
4033
4034 QFile projectFile( qgsProjectFile );
4035 if ( !projectFile.open( QIODevice::ReadOnly ) )
4036 {
4037 return false;
4038 }
4039
4040 if ( !sProjectDocument.setContent( &projectFile ) )
4041 {
4042 return false;
4043 }
4044
4045 sPrevProjectFilePath = projectFilePath;
4046 sPrevProjectFileTimestamp = projectFileTimestamp;
4047 }
4048
4049 // does project store paths absolute or relative?
4050 bool useAbsolutePaths = true;
4051
4052 const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( u"properties"_s );
4053 if ( !propertiesElem.isNull() )
4054 {
4055 QDomElement e = propertiesElem.firstChildElement( u"Paths"_s );
4056 if ( e.isNull() )
4057 {
4058 e = propertiesElem.firstChildElement( u"properties"_s );
4059 while ( !e.isNull() && e.attribute( u"name"_s ) != "Paths"_L1 )
4060 e = e.nextSiblingElement( u"properties"_s );
4061
4062 e = e.firstChildElement( u"properties"_s );
4063 while ( !e.isNull() && e.attribute( u"name"_s ) != "Absolute"_L1 )
4064 e = e.nextSiblingElement( u"properties"_s );
4065 }
4066 else
4067 {
4068 e = e.firstChildElement( u"Absolute"_s );
4069 }
4070
4071 if ( !e.isNull() )
4072 {
4073 useAbsolutePaths = e.text().compare( "true"_L1, Qt::CaseInsensitive ) == 0;
4074 }
4075 }
4076
4077 QgsReadWriteContext embeddedContext;
4078 if ( !useAbsolutePaths )
4079 embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
4080 embeddedContext.setProjectTranslator( this );
4081 embeddedContext.setTransformContext( transformContext() );
4082 embeddedContext.setCurrentLayerId( layerId );
4083
4084 const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( u"projectlayers"_s );
4085 if ( projectLayersElem.isNull() )
4086 {
4087 return false;
4088 }
4089
4090 QDomElement mapLayerElem = projectLayersElem.firstChildElement( u"maplayer"_s );
4091 while ( ! mapLayerElem.isNull() )
4092 {
4093 // get layer id
4094 const QString id = mapLayerElem.firstChildElement( u"id"_s ).text();
4095 if ( id == layerId )
4096 {
4097 // layer can be embedded only once
4098 if ( mapLayerElem.attribute( u"embedded"_s ) == "1"_L1 )
4099 {
4100 return false;
4101 }
4102
4103 mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
4104
4105 if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
4106 {
4107 return true;
4108 }
4109 else
4110 {
4111 mEmbeddedLayers.remove( layerId );
4112 return false;
4113 }
4114 }
4115 mapLayerElem = mapLayerElem.nextSiblingElement( u"maplayer"_s );
4116 }
4117
4118 return false;
4119}
4120
4121std::unique_ptr<QgsLayerTreeGroup> QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags )
4122{
4124
4125 QString qgsProjectFile = projectFilePath;
4126 QgsProjectArchive archive;
4127 if ( projectFilePath.endsWith( ".qgz"_L1, Qt::CaseInsensitive ) )
4128 {
4129 archive.unzip( projectFilePath );
4130 qgsProjectFile = archive.projectFile();
4131 }
4132
4133 // open project file, get layer ids in group, add the layers
4134 QFile projectFile( qgsProjectFile );
4135 if ( !projectFile.open( QIODevice::ReadOnly ) )
4136 {
4137 return nullptr;
4138 }
4139
4140 QDomDocument projectDocument;
4141 if ( !projectDocument.setContent( &projectFile ) )
4142 {
4143 return nullptr;
4144 }
4145
4146 QgsReadWriteContext context;
4147 context.setPathResolver( pathResolver() );
4148 context.setProjectTranslator( this );
4150
4151 auto root = std::make_unique< QgsLayerTreeGroup >();
4152
4153 QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( u"layer-tree-group"_s );
4154 if ( !layerTreeElem.isNull() )
4155 {
4156 root->readChildrenFromXml( layerTreeElem, context );
4157 }
4158 else
4159 {
4160 QgsLayerTreeUtils::readOldLegend( root.get(), projectDocument.documentElement().firstChildElement( u"legend"_s ) );
4161 }
4162
4163 QgsLayerTreeGroup *group = root->findGroup( groupName );
4164 if ( !group || group->customProperty( u"embedded"_s ).toBool() )
4165 {
4166 // embedded groups cannot be embedded again
4167 return nullptr;
4168 }
4169
4170 // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
4171 std::unique_ptr< QgsLayerTreeGroup > newGroup( QgsLayerTree::toGroup( group->clone() ) );
4172 root.reset();
4173
4174 newGroup->setCustomProperty( u"embedded"_s, 1 );
4175 newGroup->setCustomProperty( u"embedded_project"_s, projectFilePath );
4176
4177 // set "embedded" to all children + load embedded layers
4178 mLayerTreeRegistryBridge->setEnabled( false );
4179 initializeEmbeddedSubtree( projectFilePath, newGroup.get(), flags );
4180 mLayerTreeRegistryBridge->setEnabled( true );
4181
4182 // consider the layers might be identify disabled in its project
4183 const QStringList constFindLayerIds = newGroup->findLayerIds();
4184 for ( const QString &layerId : constFindLayerIds )
4185 {
4186 QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
4187 if ( layer )
4188 {
4189 layer->resolveReferences( this );
4190 layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
4191 }
4192 }
4193
4194 return newGroup;
4195}
4196
4197void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
4198{
4200
4201 const auto constChildren = group->children();
4202 for ( QgsLayerTreeNode *child : constChildren )
4203 {
4204 // all nodes in the subtree will have "embedded" custom property set
4205 child->setCustomProperty( u"embedded"_s, 1 );
4206
4207 if ( QgsLayerTree::isGroup( child ) )
4208 {
4209 initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
4210 }
4211 else if ( QgsLayerTree::isLayer( child ) )
4212 {
4213 // load the layer into our project
4214 QList<QDomNode> brokenNodes;
4215 createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
4216 }
4217 }
4218}
4219
4226
4233
4235{
4237
4238 writeEntry( u"Digitizing"_s, u"/TopologicalEditing"_s, ( enabled ? 1 : 0 ) );
4240}
4241
4243{
4245
4246 return readNumEntry( u"Digitizing"_s, u"/TopologicalEditing"_s, 0 );
4247}
4248
4250{
4252
4253 if ( mDistanceUnits == unit )
4254 return;
4255
4256 mDistanceUnits = unit;
4257
4258 emit distanceUnitsChanged();
4259}
4260
4262{
4264
4265 if ( mAreaUnits == unit )
4266 return;
4267
4268 mAreaUnits = unit;
4269
4270 emit areaUnitsChanged();
4271}
4272
4274{
4276
4277 if ( mScaleMethod == method )
4278 return;
4279
4280 mScaleMethod = method;
4281
4282 emit scaleMethodChanged();
4283}
4284
4286{
4287 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
4289
4290 if ( !mCachedHomePath.isEmpty() )
4291 return mCachedHomePath;
4292
4293 const QFileInfo pfi( fileName() );
4294
4295 if ( !mHomePath.isEmpty() )
4296 {
4297 const QFileInfo homeInfo( mHomePath );
4298 if ( !homeInfo.isRelative() )
4299 {
4300 mCachedHomePath = mHomePath;
4301 return mHomePath;
4302 }
4303 }
4304 else if ( !fileName().isEmpty() )
4305 {
4306
4307 // If it's not stored in the file system, try to get the path from the storage
4308 if ( QgsProjectStorage *storage = projectStorage() )
4309 {
4310 const QString storagePath { storage->filePath( fileName() ) };
4311 if ( ! storagePath.isEmpty() && QFileInfo::exists( storagePath ) )
4312 {
4313 mCachedHomePath = QFileInfo( storagePath ).path();
4314 return mCachedHomePath;
4315 }
4316 }
4317
4318 mCachedHomePath = pfi.path();
4319 return mCachedHomePath;
4320 }
4321
4322 if ( !pfi.exists() )
4323 {
4324 mCachedHomePath = mHomePath;
4325 return mHomePath;
4326 }
4327
4328 if ( !mHomePath.isEmpty() )
4329 {
4330 // path is relative to project file
4331 mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
4332 }
4333 else
4334 {
4335 mCachedHomePath = pfi.canonicalPath();
4336 }
4337 return mCachedHomePath;
4338}
4339
4341{
4343
4344 return mHomePath;
4345}
4346
4348{
4349 // because relation aggregate functions are not thread safe
4351
4352 return mRelationManager.get();
4353}
4354
4356{
4358
4359 return mLayoutManager.get();
4360}
4361
4363{
4365
4366 return mLayoutManager.get();
4367}
4368
4370{
4372
4373 return mElevationProfileManager.get();
4374}
4375
4377{
4379
4380 return mElevationProfileManager.get();
4381}
4382
4384{
4386
4387 return m3DViewsManager.get();
4388}
4389
4391{
4393
4394 return m3DViewsManager.get();
4395}
4396
4398{
4400
4401 return mBookmarkManager;
4402}
4403
4405{
4407
4408 return mBookmarkManager;
4409}
4410
4412{
4414
4415 return mSensorManager;
4416}
4417
4419{
4421
4422 return mSensorManager;
4423}
4424
4426{
4428
4429 return mViewSettings;
4430}
4431
4438
4440{
4442
4443 return mStyleSettings;
4444}
4445
4447{
4448 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
4450
4451 return mStyleSettings;
4452}
4453
4455{
4457
4458 return mTimeSettings;
4459}
4460
4467
4469{
4471
4472 return mElevationProperties;
4473}
4474
4481
4483{
4485
4486 return mDisplaySettings;
4487}
4488
4490{
4492
4493 return mDisplaySettings;
4494}
4495
4497{
4499
4500 return mGpsSettings;
4501}
4502
4509
4511{
4513
4514 return mRootGroup.get();
4515}
4516
4518{
4520
4521 return mMapThemeCollection.get();
4522}
4523
4525{
4527
4528 return mAnnotationManager.get();
4529}
4530
4532{
4534
4535 return mAnnotationManager.get();
4536}
4537
4538void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
4539{
4541
4542 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4543 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4544 {
4545 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
4546 continue;
4547
4548 if ( layers.contains( it.value() ) )
4549 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
4550 else
4551 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
4552 }
4553
4557}
4558
4559void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
4560{
4562
4563 QList<QgsMapLayer *> nonIdentifiableLayers;
4564 nonIdentifiableLayers.reserve( layerIds.count() );
4565 for ( const QString &layerId : layerIds )
4566 {
4567 QgsMapLayer *layer = mapLayer( layerId );
4568 if ( layer )
4569 nonIdentifiableLayers << layer;
4570 }
4574}
4575
4577{
4579
4580 QStringList nonIdentifiableLayers;
4581
4582 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4583 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4584 {
4585 if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
4586 {
4587 nonIdentifiableLayers.append( it.value()->id() );
4588 }
4589 }
4590 return nonIdentifiableLayers;
4591}
4592
4594{
4596
4597 return mTransactionMode == Qgis::TransactionMode::AutomaticGroups;
4598}
4599
4601{
4603
4604 if ( autoTransaction
4605 && mTransactionMode == Qgis::TransactionMode::AutomaticGroups )
4606 return;
4607
4608 if ( ! autoTransaction
4609 && mTransactionMode == Qgis::TransactionMode::Disabled )
4610 return;
4611
4612 if ( autoTransaction )
4614 else
4616
4617 updateTransactionGroups();
4618}
4619
4621{
4623
4624 return mTransactionMode;
4625}
4626
4628{
4630
4631 if ( transactionMode == mTransactionMode )
4632 return true;
4633
4634 // Check that all layer are not in edit mode
4635 const auto constLayers = mapLayers().values();
4636 for ( QgsMapLayer *layer : constLayers )
4637 {
4638 if ( layer->isEditable() )
4639 {
4640 QgsLogger::warning( tr( "Transaction mode can be changed only if all layers are not editable." ) );
4641 return false;
4642 }
4643 }
4644
4645 mTransactionMode = transactionMode;
4646 updateTransactionGroups();
4648 return true;
4649}
4650
4651QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
4652{
4654
4655 return mTransactionGroups;
4656}
4657
4658
4659//
4660// QgsMapLayerStore methods
4661//
4662
4663
4665{
4667
4668 return mLayerStore->count();
4669}
4670
4672{
4674
4675 return mLayerStore->validCount();
4676}
4677
4678QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
4679{
4680 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
4682
4683 if ( mMainAnnotationLayer && layerId == mMainAnnotationLayer->id() )
4684 return mMainAnnotationLayer;
4685
4686 return mLayerStore->mapLayer( layerId );
4687}
4688
4689QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
4690{
4692
4693 return mLayerStore->mapLayersByName( layerName );
4694}
4695
4696QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
4697{
4699
4700 QList<QgsMapLayer *> layers;
4701 const auto constMapLayers { mLayerStore->mapLayers() };
4702 for ( const auto &l : constMapLayers )
4703 {
4704 if ( ! l->serverProperties()->shortName().isEmpty() )
4705 {
4706 if ( l->serverProperties()->shortName() == shortName )
4707 layers << l;
4708 }
4709 else if ( l->name() == shortName )
4710 {
4711 layers << l;
4712 }
4713 }
4714 return layers;
4715}
4716
4717bool QgsProject::unzip( const QString &filename, Qgis::ProjectReadFlags flags )
4718{
4720
4721 clearError();
4722 auto archive = std::make_unique<QgsProjectArchive>();
4723
4724 // unzip the archive
4725 if ( !archive->unzip( filename ) )
4726 {
4727 setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
4728 return false;
4729 }
4730
4731 // test if zip provides a .qgs file
4732 if ( archive->projectFile().isEmpty() )
4733 {
4734 setError( tr( "Zip archive does not provide a project file" ) );
4735 return false;
4736 }
4737
4738 // Keep the archive
4739 releaseHandlesToProjectArchive();
4740 mArchive = std::move( archive );
4741
4742 // load auxiliary storage
4743 if ( !static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile().isEmpty() )
4744 {
4745 // database file is already a copy as it's been unzipped. So we don't open
4746 // auxiliary storage in copy mode in this case
4747 mAuxiliaryStorage = std::make_unique< QgsAuxiliaryStorage >( static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile(), false );
4748 }
4749 else
4750 {
4751 mAuxiliaryStorage = std::make_unique< QgsAuxiliaryStorage >( *this );
4752 }
4753
4754 // read the project file
4755 if ( ! readProjectFile( static_cast<QgsProjectArchive *>( mArchive.get() )->projectFile(), flags ) )
4756 {
4757 setError( tr( "Cannot read unzipped qgs project file" ) + u": "_s + error() );
4758 return false;
4759 }
4760
4761 // Remove the temporary .qgs file
4762 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
4763
4764 return true;
4765}
4766
4767bool QgsProject::zip( const QString &filename )
4768{
4770
4771 clearError();
4772
4773 // save the current project in a temporary .qgs file
4774 auto archive = std::make_unique<QgsProjectArchive>();
4775 const QString baseName = QFileInfo( filename ).baseName();
4776 const QString qgsFileName = u"%1.qgs"_s.arg( baseName );
4777 QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
4778
4779 bool writeOk = false;
4780 if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
4781 {
4782 writeOk = writeProjectFile( qgsFile.fileName() );
4783 qgsFile.close();
4784 }
4785
4786 // stop here with an error message
4787 if ( ! writeOk )
4788 {
4789 setError( tr( "Unable to write temporary qgs file" ) );
4790 return false;
4791 }
4792
4793 // save auxiliary storage
4794 const QFileInfo info( qgsFile );
4795 const QString asExt = u".%1"_s.arg( QgsAuxiliaryStorage::extension() );
4796 const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
4797
4798 bool auxiliaryStorageSavedOk = true;
4799 if ( ! saveAuxiliaryStorage( asFileName ) )
4800 {
4801 const QString err = mAuxiliaryStorage->errorString();
4802 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 ) );
4803 auxiliaryStorageSavedOk = false;
4804
4805 // fixes the current archive and keep the previous version of qgd
4806 if ( !mArchive->exists() )
4807 {
4808 releaseHandlesToProjectArchive();
4809 mArchive = std::make_unique< QgsProjectArchive >();
4810 mArchive->unzip( mFile.fileName() );
4811 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
4812
4813 const QString auxiliaryStorageFile = static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile();
4814 if ( ! auxiliaryStorageFile.isEmpty() )
4815 {
4816 archive->addFile( auxiliaryStorageFile );
4817 mAuxiliaryStorage = std::make_unique< QgsAuxiliaryStorage >( auxiliaryStorageFile, false );
4818 }
4819 }
4820 }
4821 else
4822 {
4823 // in this case, an empty filename means that the auxiliary database is
4824 // empty, so we don't want to save it
4825 if ( QFile::exists( asFileName ) )
4826 {
4827 archive->addFile( asFileName );
4828 }
4829 }
4830
4831 // create the archive
4832 archive->addFile( qgsFile.fileName() );
4833
4834 // Add all other files
4835 const QStringList &files = mArchive->files();
4836 for ( const QString &file : files )
4837 {
4838 if ( !file.endsWith( ".qgs"_L1, Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
4839 {
4840 archive->addFile( file );
4841 }
4842 }
4843
4844 // zip
4845 bool zipOk = true;
4846 if ( !archive->zip( filename ) )
4847 {
4848 setError( tr( "Unable to perform zip" ) );
4849 zipOk = false;
4850 }
4851
4852 return auxiliaryStorageSavedOk && zipOk;
4853}
4854
4856{
4858
4859 return QgsZipUtils::isZipFile( mFile.fileName() );
4860}
4861
4862QList<QgsMapLayer *> QgsProject::addMapLayers(
4863 const QList<QgsMapLayer *> &layers,
4864 bool addToLegend,
4865 bool takeOwnership )
4866{
4868
4869 const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
4870 if ( !myResultList.isEmpty() )
4871 {
4872 // Update transform context
4873 for ( auto &l : myResultList )
4874 {
4875 l->setTransformContext( transformContext() );
4876 }
4877 if ( addToLegend )
4878 {
4879 emit legendLayersAdded( myResultList );
4880 }
4881 else
4882 {
4883 emit layersAddedWithoutLegend( myResultList );
4884 }
4885 }
4886
4887 if ( mAuxiliaryStorage )
4888 {
4889 for ( QgsMapLayer *mlayer : myResultList )
4890 {
4891 if ( mlayer->type() != Qgis::LayerType::Vector )
4892 continue;
4893
4894 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
4895 if ( vl )
4896 {
4897 vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
4898 }
4899 }
4900 }
4901
4902 mProjectScope.reset();
4903
4904 return myResultList;
4905}
4906
4909 bool addToLegend,
4910 bool takeOwnership )
4911{
4913
4914 QList<QgsMapLayer *> addedLayers;
4915 addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
4916 return addedLayers.isEmpty() ? nullptr : addedLayers[0];
4917}
4918
4919void QgsProject::removeAuxiliaryLayer( const QgsMapLayer *ml )
4920{
4922
4923 if ( ! ml || ml->type() != Qgis::LayerType::Vector )
4924 return;
4925
4926 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( ml );
4927 if ( vl && vl->auxiliaryLayer() )
4928 {
4929 const QgsDataSourceUri uri( vl->auxiliaryLayer()->source() );
4931 }
4932}
4933
4934void QgsProject::removeMapLayers( const QStringList &layerIds )
4935{
4937
4938 for ( const auto &layerId : layerIds )
4939 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4940
4941 mProjectScope.reset();
4942 mLayerStore->removeMapLayers( layerIds );
4943}
4944
4945void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
4946{
4948
4949 for ( const auto &layer : layers )
4950 removeAuxiliaryLayer( layer );
4951
4952 mProjectScope.reset();
4953 mLayerStore->removeMapLayers( layers );
4954}
4955
4956void QgsProject::removeMapLayer( const QString &layerId )
4957{
4959
4960 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
4961 mProjectScope.reset();
4962 mLayerStore->removeMapLayer( layerId );
4963}
4964
4966{
4968
4969 removeAuxiliaryLayer( layer );
4970 mProjectScope.reset();
4971 mLayerStore->removeMapLayer( layer );
4972}
4973
4975{
4977
4978 mProjectScope.reset();
4979 return mLayerStore->takeMapLayer( layer );
4980}
4981
4983{
4985
4986 return mMainAnnotationLayer;
4987}
4988
4990{
4992
4993 if ( mLayerStore->count() == 0 )
4994 return;
4995
4996 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
4997 mProjectScope.reset();
4998 mLayerStore->removeAllMapLayers();
4999
5000 snapSingleBlocker.release();
5001 mSnappingConfig.clearIndividualLayerSettings();
5002 if ( !mBlockSnappingUpdates )
5003 emit snappingConfigChanged( mSnappingConfig );
5004}
5005
5007{
5009
5010 const QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
5011 QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
5012 for ( ; it != layers.constEnd(); ++it )
5013 {
5014 it.value()->reload();
5015 }
5016}
5017
5018QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
5019{
5020 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
5022
5023 return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
5024}
5025
5026QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
5027{
5029
5030 return mTransactionGroups.value( qMakePair( providerKey, connString ) );
5031}
5032
5039
5041{
5043
5045
5046 // TODO QGIS 5.0 -- remove this method, and place it somewhere in app (where it belongs)
5047 // 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)
5048 if ( mSettings.value( u"/projections/unknownCrsBehavior"_s, u"NoAction"_s, QgsSettings::App ).toString() == u"UseProjectCrs"_s
5049 || mSettings.value( u"/projections/unknownCrsBehavior"_s, 0, QgsSettings::App ).toString() == "2"_L1 )
5050 {
5051 // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
5052 defaultCrs = crs();
5053 }
5054 else
5055 {
5056 // global crs
5057 const QString layerDefaultCrs = mSettings.value( u"/Projections/layerDefaultCrs"_s, u"EPSG:4326"_s ).toString();
5058 defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
5059 }
5060
5061 return defaultCrs;
5062}
5063
5070
5077
5078bool QgsProject::saveAuxiliaryStorage( const QString &filename )
5079{
5081
5082 const QMap<QString, QgsMapLayer *> layers = mapLayers();
5083 bool empty = true;
5084 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
5085 {
5086 if ( it.value()->type() != Qgis::LayerType::Vector )
5087 continue;
5088
5089 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
5090 if ( vl && vl->auxiliaryLayer() )
5091 {
5092 vl->auxiliaryLayer()->save();
5093 empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
5094 }
5095 }
5096
5097 if ( !mAuxiliaryStorage->exists( *this ) && empty )
5098 {
5099 return true; // it's not an error
5100 }
5101 else if ( !filename.isEmpty() )
5102 {
5103 return mAuxiliaryStorage->saveAs( filename );
5104 }
5105 else
5106 {
5107 return mAuxiliaryStorage->saveAs( *this );
5108 }
5109}
5110
5111QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
5112{
5113 static QgsPropertiesDefinition sPropertyDefinitions
5114 {
5115 {
5117 QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
5118 },
5119 };
5120 return sPropertyDefinitions;
5121}
5122
5128
5130{
5132
5133 return mAuxiliaryStorage.get();
5134}
5135
5137{
5139
5140 return mAuxiliaryStorage.get();
5141}
5142
5143QString QgsProject::createAttachedFile( const QString &nameTemplate )
5144{
5146
5147 const QDir archiveDir( mArchive->dir() );
5148 QTemporaryFile tmpFile( archiveDir.filePath( "XXXXXX_" + nameTemplate ), this );
5149 tmpFile.setAutoRemove( false );
5150 if ( !tmpFile.open() )
5151 {
5152 setError( tr( "Unable to open %1" ).arg( tmpFile.fileName() ) );
5153 return QString();
5154 }
5155 mArchive->addFile( tmpFile.fileName() );
5156 return tmpFile.fileName();
5157}
5158
5159QStringList QgsProject::attachedFiles() const
5160{
5162
5163 QStringList attachments;
5164 const QString baseName = QFileInfo( fileName() ).baseName();
5165 const QStringList files = mArchive->files();
5166 attachments.reserve( files.size() );
5167 for ( const QString &file : files )
5168 {
5169 if ( QFileInfo( file ).baseName() != baseName )
5170 {
5171 attachments.append( file );
5172 }
5173 }
5174 return attachments;
5175}
5176
5177bool QgsProject::removeAttachedFile( const QString &path )
5178{
5180
5181 return mArchive->removeFile( path );
5182}
5183
5184QString QgsProject::attachmentIdentifier( const QString &attachedFile ) const
5185{
5187
5188 return u"attachment:///%1"_s.arg( QFileInfo( attachedFile ).fileName() );
5189}
5190
5191QString QgsProject::resolveAttachmentIdentifier( const QString &identifier ) const
5192{
5194
5195 if ( identifier.startsWith( "attachment:///"_L1 ) )
5196 {
5197 return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
5198 }
5199 return QString();
5200}
5201
5203{
5204 // this method is called quite extensively from other threads via QgsProject::createExpressionContextScope()
5206
5207 return mMetadata;
5208}
5209
5211{
5213
5214 if ( metadata == mMetadata )
5215 return;
5216
5217 mMetadata = metadata;
5218 mProjectScope.reset();
5219
5220 emit metadataChanged();
5221 emit titleChanged();
5222
5223 setDirty( true );
5224}
5225
5226QSet<QgsMapLayer *> QgsProject::requiredLayers() const
5227{
5229
5230 QSet<QgsMapLayer *> requiredLayers;
5231
5232 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
5233 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
5234 {
5235 if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
5236 {
5237 requiredLayers.insert( it.value() );
5238 }
5239 }
5240 return requiredLayers;
5241}
5242
5243void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
5244{
5246
5247 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
5248 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
5249 {
5250 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
5251 continue;
5252
5253 if ( layers.contains( it.value() ) )
5254 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
5255 else
5256 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
5257 }
5258}
5259
5261{
5263
5264 // save colors to project
5265 QStringList customColors;
5266 QStringList customColorLabels;
5267
5268 QgsNamedColorList::const_iterator colorIt = colors.constBegin();
5269 for ( ; colorIt != colors.constEnd(); ++colorIt )
5270 {
5271 const QString color = QgsColorUtils::colorToString( ( *colorIt ).first );
5272 const QString label = ( *colorIt ).second;
5273 customColors.append( color );
5274 customColorLabels.append( label );
5275 }
5276 writeEntry( u"Palette"_s, u"/Colors"_s, customColors );
5277 writeEntry( u"Palette"_s, u"/Labels"_s, customColorLabels );
5278 mProjectScope.reset();
5279 emit projectColorsChanged();
5280}
5281
5282void QgsProject::setBackgroundColor( const QColor &color )
5283{
5285
5286 if ( mBackgroundColor == color )
5287 return;
5288
5289 mBackgroundColor = color;
5291}
5292
5294{
5296
5297 return mBackgroundColor;
5298}
5299
5300void QgsProject::setSelectionColor( const QColor &color )
5301{
5303
5304 if ( mSelectionColor == color )
5305 return;
5306
5307 mSelectionColor = color;
5308 emit selectionColorChanged();
5309}
5310
5312{
5314
5315 return mSelectionColor;
5316}
5317
5318void QgsProject::setMapScales( const QVector<double> &scales )
5319{
5321
5322 mViewSettings->setMapScales( scales );
5323}
5324
5325QVector<double> QgsProject::mapScales() const
5326{
5328
5329 return mViewSettings->mapScales();
5330}
5331
5333{
5335
5336 mViewSettings->setUseProjectScales( enabled );
5337}
5338
5340{
5342
5343 return mViewSettings->useProjectScales();
5344}
5345
5346void QgsProject::generateTsFile( const QString &locale )
5347{
5349
5350 QgsTranslationContext translationContext;
5351 translationContext.setProject( this );
5352 translationContext.setFileName( u"%1/%2.ts"_s.arg( absolutePath(), baseName() ) );
5353
5354 QgsApplication::instance()->collectTranslatableObjects( &translationContext );
5355
5356 translationContext.writeTsFile( locale );
5357}
5358
5359QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
5360{
5362
5363 if ( !mTranslator )
5364 {
5365 return sourceText;
5366 }
5367
5368 QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
5369
5370 if ( result.isEmpty() )
5371 {
5372 return sourceText;
5373 }
5374 return result;
5375}
5376
5378{
5380
5381 const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
5382 if ( !layers.empty() )
5383 {
5384 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
5385 {
5386 // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
5387 if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
5388 {
5389 if ( !( ( *it )->accept( visitor ) ) )
5390 return false;
5391
5392 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
5393 return false;
5394 }
5395 }
5396 }
5397
5398 if ( !mLayoutManager->accept( visitor ) )
5399 return false;
5400
5401 if ( !mAnnotationManager->accept( visitor ) )
5402 return false;
5403
5404 return true;
5405}
5406
5408{
5410
5411 const QString macros = readEntry( u"Macros"_s, u"/pythonCode"_s, QString() );
5412 if ( !macros.isEmpty() )
5413 {
5414 QgsEmbeddedScriptEntity entity( Qgis::EmbeddedScriptType::Macro, tr( "Macros" ), macros );
5415 if ( !visitor->visitEmbeddedScript( entity, context ) )
5416 {
5417 return false;
5418 }
5419 }
5420
5421 const QString expressionFunctions = readEntry( u"ExpressionFunctions"_s, u"/pythonCode"_s );
5422 if ( !expressionFunctions.isEmpty() )
5423 {
5424 QgsEmbeddedScriptEntity entity( Qgis::EmbeddedScriptType::ExpressionFunction, tr( "Expression functions" ), expressionFunctions );
5425 if ( !visitor->visitEmbeddedScript( entity, context ) )
5426 {
5427 return false;
5428 }
5429 }
5430
5431 const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
5432 if ( !layers.empty() )
5433 {
5434 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
5435 {
5436 if ( !( ( *it )->accept( visitor, context ) ) )
5437 {
5438 return false;
5439 }
5440 }
5441 }
5442
5443 return true;
5444}
5445
5447{
5448 return mElevationShadingRenderer;
5449}
5450
5451void QgsProject::loadProjectFlags( const QDomDocument *doc )
5452{
5454
5455 QDomElement element = doc->documentElement().firstChildElement( u"projectFlags"_s );
5457 if ( !element.isNull() )
5458 {
5459 flags = qgsFlagKeysToValue( element.attribute( u"set"_s ), Qgis::ProjectFlags() );
5460 }
5461 else
5462 {
5463 // older project compatibility
5464 element = doc->documentElement().firstChildElement( u"evaluateDefaultValues"_s );
5465 if ( !element.isNull() )
5466 {
5467 if ( element.attribute( u"active"_s, u"0"_s ).toInt() == 1 )
5469 }
5470
5471 // Read trust layer metadata config in the project
5472 element = doc->documentElement().firstChildElement( u"trust"_s );
5473 if ( !element.isNull() )
5474 {
5475 if ( element.attribute( u"active"_s, u"0"_s ).toInt() == 1 )
5477 }
5478 }
5479
5480 setFlags( flags );
5481}
5482
5484{
5486 {
5488 {
5489 const QString projectFunctions = readEntry( u"ExpressionFunctions"_s, u"/pythonCode"_s, QString() );
5490 if ( !projectFunctions.isEmpty() )
5491 {
5492 QgsPythonRunner::run( projectFunctions );
5493 return true;
5494 }
5495 }
5496 }
5497 return false;
5498}
5499
5501{
5503 {
5504 QgsPythonRunner::run( "qgis.utils.clean_project_expression_functions()" );
5505 }
5506}
5507
5509
5510QHash< QString, QColor > loadColorsFromProject( const QgsProject *project )
5511{
5512 QHash< QString, QColor > colors;
5513
5514 //build up color list from project. Do this in advance for speed
5515 QStringList colorStrings = project->readListEntry( u"Palette"_s, u"/Colors"_s );
5516 const QStringList colorLabels = project->readListEntry( u"Palette"_s, u"/Labels"_s );
5517
5518 //generate list from custom colors
5519 int colorIndex = 0;
5520 for ( QStringList::iterator it = colorStrings.begin();
5521 it != colorStrings.end(); ++it )
5522 {
5523 const QColor color = QgsColorUtils::colorFromString( *it );
5524 QString label;
5525 if ( colorLabels.length() > colorIndex )
5526 {
5527 label = colorLabels.at( colorIndex );
5528 }
5529
5530 colors.insert( label.toLower(), color );
5531 colorIndex++;
5532 }
5533
5534 return colors;
5535}
5536
5537
5538GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
5539 : QgsScopedExpressionFunction( u"project_color"_s, 1, u"Color"_s )
5540{
5541 if ( !project )
5542 return;
5543
5544 mColors = loadColorsFromProject( project );
5545}
5546
5547GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
5548 : QgsScopedExpressionFunction( u"project_color"_s, 1, u"Color"_s )
5549 , mColors( colors )
5550{
5551}
5552
5553QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5554{
5555 const QString colorName = values.at( 0 ).toString().toLower();
5556 if ( mColors.contains( colorName ) )
5557 {
5558 return u"%1,%2,%3"_s.arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
5559 }
5560 else
5561 return QVariant();
5562}
5563
5564QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
5565{
5566 return new GetNamedProjectColor( mColors );
5567}
5568
5569GetNamedProjectColorObject::GetNamedProjectColorObject( const QgsProject *project )
5570 : QgsScopedExpressionFunction( u"project_color_object"_s, 1, u"Color"_s )
5571{
5572 if ( !project )
5573 return;
5574
5575 mColors = loadColorsFromProject( project );
5576}
5577
5578GetNamedProjectColorObject::GetNamedProjectColorObject( const QHash<QString, QColor> &colors )
5579 : QgsScopedExpressionFunction( u"project_color_object"_s, 1, u"Color"_s )
5580 , mColors( colors )
5581{
5582}
5583
5584QVariant GetNamedProjectColorObject::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5585{
5586 const QString colorName = values.at( 0 ).toString().toLower();
5587 if ( mColors.contains( colorName ) )
5588 {
5589 return mColors.value( colorName );
5590 }
5591 else
5592 return QVariant();
5593}
5594
5595QgsScopedExpressionFunction *GetNamedProjectColorObject::clone() const
5596{
5597 return new GetNamedProjectColorObject( mColors );
5598}
5599
5600// ----------------
5601
5602GetSensorData::GetSensorData( const QMap<QString, QgsAbstractSensor::SensorData> &sensorData )
5603 : QgsScopedExpressionFunction( u"sensor_data"_s,
5604 QgsExpressionFunction::ParameterList() << QgsExpressionFunction::Parameter( u"name"_s ) << QgsExpressionFunction::Parameter( u"expiration"_s, true, 0 ),
5605 u"Sensors"_s )
5606 , mSensorData( sensorData )
5607{
5608}
5609
5610QVariant GetSensorData::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
5611{
5612 const QString sensorName = values.at( 0 ).toString();
5613 const int expiration = values.at( 1 ).toInt();
5614 const qint64 timestamp = QDateTime::currentMSecsSinceEpoch();
5615 if ( mSensorData.contains( sensorName ) )
5616 {
5617 if ( expiration <= 0 || ( timestamp - mSensorData[sensorName].lastTimestamp.toMSecsSinceEpoch() ) < expiration )
5618 {
5619 return mSensorData[sensorName].lastValue;
5620 }
5621 }
5622
5623 return QVariant();
5624}
5625
5626QgsScopedExpressionFunction *GetSensorData::clone() const
5627{
5628 return new GetSensorData( mSensorData );
5629}
@ ExpressionFunction
Project macros.
Definition qgis.h:460
@ DontLoad3DViews
Skip loading 3D views.
Definition qgis.h:4366
@ DontStoreOriginalStyles
Skip the initial XML style storage for layers. Useful for minimising project load times in non-intera...
Definition qgis.h:4365
@ ForceReadOnlyLayers
Open layers in a read-only mode.
Definition qgis.h:4368
@ TrustLayerMetadata
Trust layer metadata. Improves project read time. Do not use it if layers' extent is not fixed during...
Definition qgis.h:4364
@ DontUpgradeAnnotations
Don't upgrade old annotation items to QgsAnnotationItem.
Definition qgis.h:4369
@ DontLoadLayouts
Don't load print layouts. Improves project read time if layouts are not required, and allows projects...
Definition qgis.h:4363
@ DontResolveLayers
Don't resolve layer paths (i.e. don't load any layer content). Dramatically improves project read tim...
Definition qgis.h:4362
static QString version()
Version string.
Definition qgis.cpp:682
@ Trusted
The project trust has not yet been determined by the user.
Definition qgis.h:473
QFlags< ProjectCapability > ProjectCapabilities
Flags which control project capabilities.
Definition qgis.h:4402
QFlags< ProjectReadFlag > ProjectReadFlags
Project load flags.
Definition qgis.h:4380
DistanceUnit
Units of distance.
Definition qgis.h:5085
@ Meters
Meters.
Definition qgis.h:5086
FilePathType
File path types.
Definition qgis.h:1734
@ Relative
Relative path.
Definition qgis.h:1736
@ Absolute
Absolute path.
Definition qgis.h:1735
TransactionMode
Transaction mode.
Definition qgis.h:4028
@ AutomaticGroups
Automatic transactional editing means that on supported datasources (postgres and geopackage database...
Definition qgis.h:4030
@ BufferedGroups
Buffered transactional editing means that all editable layers in the buffered transaction group are t...
Definition qgis.h:4031
@ Disabled
Edits are buffered locally and sent to the provider when toggling layer editing mode.
Definition qgis.h:4029
AreaUnit
Units of area.
Definition qgis.h:5162
@ SquareMeters
Square meters.
Definition qgis.h:5163
@ Critical
Critical/error message.
Definition qgis.h:162
@ Success
Used for reporting a successful operation.
Definition qgis.h:163
@ Vertical
Vertical CRS.
Definition qgis.h:2391
@ Temporal
Temporal CRS.
Definition qgis.h:2394
@ Compound
Compound (horizontal + vertical) CRS.
Definition qgis.h:2393
@ Projected
Projected CRS.
Definition qgis.h:2392
@ Other
Other type.
Definition qgis.h:2397
@ Bound
Bound CRS.
Definition qgis.h:2396
@ DerivedProjected
Derived projected CRS.
Definition qgis.h:2398
@ Unknown
Unknown type.
Definition qgis.h:2386
@ Engineering
Engineering CRS.
Definition qgis.h:2395
@ Geographic3d
3D geopraphic CRS
Definition qgis.h:2390
@ Geodetic
Geodetic CRS.
Definition qgis.h:2387
@ Geographic2d
2D geographic CRS
Definition qgis.h:2389
@ Geocentric
Geocentric CRS.
Definition qgis.h:2388
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
Definition qgis.h:4332
@ AvoidIntersectionsLayers
Overlap with features from a specified list of layers when digitizing new features not allowed.
Definition qgis.h:4335
@ AllowIntersections
Overlap with any feature allowed when digitizing new features.
Definition qgis.h:4333
ProjectFlag
Flags which control the behavior of QgsProjects.
Definition qgis.h:4151
@ RememberLayerEditStatusBetweenSessions
If set, then any layers set to be editable will be stored in the project and immediately made editabl...
Definition qgis.h:4154
@ EvaluateDefaultValuesOnProviderSide
If set, default values for fields will be evaluated on the provider side when features from the proje...
Definition qgis.h:4152
@ TrustStoredLayerStatistics
If set, then layer statistics (such as the layer extent) will be read from values stored in the proje...
Definition qgis.h:4153
@ Polygon
Polygons.
Definition qgis.h:368
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:505
LayerType
Types of layers that can be added to a map.
Definition qgis.h:193
@ Group
Composite group layer. Added in QGIS 3.24.
Definition qgis.h:201
@ Plugin
Plugin based layer.
Definition qgis.h:196
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
Definition qgis.h:202
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:199
@ Vector
Vector layer.
Definition qgis.h:194
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
Definition qgis.h:198
@ Mesh
Mesh layer. Added in QGIS 3.2.
Definition qgis.h:197
@ Raster
Raster layer.
Definition qgis.h:195
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
Definition qgis.h:200
ScaleCalculationMethod
Scale calculation logic.
Definition qgis.h:5357
@ HorizontalMiddle
Calculate horizontally, across midle of map.
Definition qgis.h:5359
@ SkipCredentialsRequest
Skip credentials if the provided one are not valid, let the provider be invalid, avoiding to block th...
Definition qgis.h:493
@ ParallelThreadLoading
Provider is created in a parallel thread than the one where it will live.
Definition qgis.h:494
QFlags< ProjectFlag > ProjectFlags
Definition qgis.h:4158
@ Container
A container.
Definition qgis.h:5718
@ Marker
Marker symbol.
Definition qgis.h:630
@ Line
Line symbol.
Definition qgis.h:631
@ Fill
Fill symbol.
Definition qgis.h:632
static QString geoNone()
Constant that holds the string representation for "No ellipse/No CRS".
Definition qgis.h:6615
@ Preferred
Preferred format, matching the most recent WKT ISO standard. Currently an alias to WKT2_2019,...
Definition qgis.h:2497
QMap< QString, QStringList > KeywordMap
Map of vocabulary string to keyword list.
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.
QList< QgsAction > actions(const QString &actionScope=QString()) const
Returns a list of actions that are available in the given action scope.
Utility class that encapsulates an action based on vector attributes.
Definition qgsaction.h:38
Represents a map layer containing a set of georeferenced annotations, e.g.
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.
Manages zip/unzip operations for an archive.
Definition qgsarchive.h:36
A container for attribute editors, used to group them visually in the attribute form if it is set to ...
QList< QgsAttributeEditorElement * > children() const
Gets a list of the children elements of this container.
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.
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.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
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.
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs, QString &error)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
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.
QString toWkt(Qgis::CrsWktVariant variant=Qgis::CrsWktVariant::Wkt1Gdal, bool multiline=false, int indentationWidth=4) const
Returns a WKT representation of this CRS.
static QgsCoordinateReferenceSystem fromSrsId(long srsId)
Creates a CRS from a specified QGIS SRS ID.
Contains information about the context in which a coordinate transform is executed.
void readSettings()
Reads the context's state from application settings.
Abstract base class for spatial data provider implementations.
@ EvaluateDefaultValues
Evaluate default values on provider side when calling QgsVectorDataProvider::defaultValue( int index ...
Stores the component parts of a data source URI (e.g.
QgsAttributeEditorContainer * invisibleRootContainer()
Gets the invisible root container for the drag and drop designer form (EditorLayout::TabLayout).
Manages storage of a set of elevation profiles.
Renders elevation shading on an image with different methods (eye dome lighting, hillshading,...
A embedded script entity for QgsObjectEntityVisitorInterface.
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...
An abstract base class for defining QgsExpression functions.
An expression node for expression functions.
Handles parsing and evaluation of expressions (formerly called "search strings").
virtual QgsLegendSymbolList legendSymbolItems() const
Returns a list of symbology items for the legend.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
Container of fields for a vector layer.
Definition qgsfields.h:46
bool isEmpty
Definition qgsfields.h:49
Stores global configuration for labeling engine.
Layer tree group node serves as a container for layers and further groups.
QgsLayerTreeGroup * findGroup(const QString &name)
Find group node with specified name.
void readChildrenFromXml(const QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QString name() const override
Returns the group's name.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
QgsLayerTreeGroup * clone() const override
Returns a clone of the group.
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).
Base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the children, disconnect all the forwarded and external signals and sets their parent to null...
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.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
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).
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.
static QgsLayerTreeLayer * toLayer(QgsLayerTreeNode *node)
Cast node to a layer.
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.
Manages storage of a set of layouts.
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
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:83
QFlags< ReadFlag > ReadFlags
QString source() const
Returns the source for the layer.
QString providerType() const
Returns the provider type (provider key) for this layer.
void configChanged()
Emitted whenever the configuration is changed.
static Qgis::DataProviderReadFlags providerReadFlags(const QDomNode &layerNode, QgsMapLayer::ReadFlags layerReadFlags)
Returns provider read flag deduced from layer read flags layerReadFlags and a dom node layerNode that...
QString id
Definition qgsmaplayer.h:86
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:93
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...
@ FlagReadExtentFromXml
Read extent from xml and skip get extent from provider.
@ 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.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
An interface for classes which can visit various object entity (e.g.
virtual bool visitEmbeddedScript(const QgsEmbeddedScriptEntity &entity, const QgsObjectVisitorContext &context)
Called when the visitor will visit an embedded script entity.
A QgsObjectEntityVisitorInterface context object.
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.
Allows managing the zip/unzip actions on project files.
Definition qgsarchive.h:112
QString projectFile() const
Returns the current .qgs project 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 files.
Contains settings and properties relating to how a QgsProject should display values such as map coord...
Contains elevation properties for a QgsProject.
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.
A structured metadata store for a project.
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.
void subkeyList(QStringList &entries) const
Returns any sub-keys contained by this property which themselves contain other keys.
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.
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.
void setDefaultSymbol(Qgis::SymbolType symbolType, QgsSymbol *symbol)
Sets the project default symbol for a given type.
void setRandomizeDefaultSymbolColor(bool randomized)
Sets whether the default symbol fill color is randomized.
void setDefaultColorRamp(QgsColorRamp *colorRamp)
Sets the project default color ramp.
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...
static Qgis::ProjectTrustStatus checkUserTrust(QgsProject *project)
Returns the current trust status of the specified project.
Describes 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,...
void mapScalesChanged()
Emitted when the list of custom project map scales changes.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:112
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:123
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 void oldProjectVersionWarning(const QString &warning)
Emitted when an old project file is read.
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:130
void layersAddedWithoutLegend(const QList< QgsMapLayer * > &layers)
Emitted when layers were added to the registry without adding to the legend.
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.
void readProjectWithContext(const QDomDocument &document, QgsReadWriteContext &context)
Emitted when a project is being read.
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:215
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:128
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:120
void fileNameChanged()
Emitted when the file name of the project changes.
void titleChanged()
Emitted when the title of the project changes.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QString title
Definition qgsproject.h:115
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...
void setScaleMethod(Qgis::ScaleCalculationMethod method)
Sets the method to use for map scale calculations for the project.
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:131
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 a string using the Qt QTranslator mechanism.
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:122
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.
std::unique_ptr< 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.
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...
bool setVerticalCrs(const QgsCoordinateReferenceSystem &crs, QString *errorMessage=nullptr)
Sets the project's vertical coordinate reference system.
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 crs3DChanged()
Emitted when the crs3D() of the project has changed.
void scaleMethodChanged()
Emitted when the project's scale method is changed.
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:125
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:121
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 specified scope and key.
Qgis::TransactionMode transactionMode
Definition qgsproject.h:133
QgsAnnotationManager * annotationManager()
Returns pointer to the project's annotation manager.
QgsProjectDisplaySettings * displaySettings
Definition qgsproject.h:132
QgsProjectMetadata metadata
Definition qgsproject.h:126
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:119
void transactionModeChanged()
Emitted when the transaction mode has changed.
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.
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.
Q_INVOKABLE QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QString fileName
Definition qgsproject.h:116
QgsCoordinateReferenceSystem crs3D() const
Returns the CRS to use for the project when transforming 3D data, or when z/elevation value handling ...
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:118
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
QStringList nonIdentifiableLayers
Definition qgsproject.h:114
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 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...
Qgis::ProjectCapabilities capabilities() const
Returns the project's capabilities, which dictate optional functionality which can be selectively ena...
Definition qgsproject.h:205
bool loadFunctionsFromProject(bool force=false)
Loads python expression functions stored in the current project.
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...
const QgsElevationProfileManager * elevationProfileManager() const
Returns the project's elevation profile manager, which manages elevation profiles within the project.
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 readProject(const QDomDocument &document)
Emitted when a project is being read.
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
QColor backgroundColor
Definition qgsproject.h:127
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 specified scope and key.
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:129
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.
const QgsProjectViewSettings * viewSettings() const
Returns the project's view settings, which contains settings and properties relating to how a QgsProj...
void cleanFunctionsFromProject()
Unloads python expression functions stored in the current project and reloads local functions from th...
QgsCoordinateReferenceSystem verticalCrs() const
Returns the project's vertical coordinate reference system.
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 layers were 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.
void writeProject(QDomDocument &document)
Emitted when the project is being written.
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:117
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 verticalCrsChanged()
Emitted when the verticalCrs() of the project has changed.
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 an integer key value.
void clear() final
Removes all properties from the collection.
@ String
Any string value.
Definition qgsproperty.h:61
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.
static bool run(const QString &command, const QString &messageOnError=QString())
Execute a Python statement.
static bool isValid()
Returns true if the runner has an instance (and thus is able to run commands).
A container for the context for various read/write operations on objects.
void setCurrentLayerId(const QString &layerId)
Sets the current layer id.
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.
Manages a set of relations between layers.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
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.
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.
Stores configuration of snapping settings for the project.
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.
void triggerIconRebuild()
Triggers emission of the rebuildIconPreviews() signal.
static QgsStyle * defaultStyle(bool initialize=true)
Returns the default application-wide style.
Definition qgsstyle.cpp:150
Represents a transaction group.
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.
Represents a vector layer which manages a vector based dataset.
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.
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...
QgsFeatureRenderer * renderer()
Returns the feature renderer used for rendering the features in the layer in 2D map views.
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...
QgsActionManager * actions()
Returns all layer actions defined on this layer.
QgsEditFormConfig editFormConfig
static bool isZipFile(const QString &filename)
Returns true if the file name is a zipped file ( i.e with a '.qgz' extension, false otherwise.
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
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:7110
#define Q_NOWARN_DEPRECATED_POP
Definition qgis.h:7451
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7091
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition qgis.h:7149
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:7171
#define Q_NOWARN_DEPRECATED_PUSH
Definition qgis.h:7450
#define QgsDebugCall
Definition qgslogger.h:55
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
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.
void dump_(const QgsProjectPropertyKey &topQgsPropertyKey)
QgsProjectProperty * findKey_(const QString &scope, const QString &key, QgsProjectPropertyKey &rootProperty)
Takes the given scope and key and convert them to a string list of key tokens that will be used to na...
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 FONTMARKER_CHR_FIX
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
QDomElement layerElement
QString layerId
Qgis::DataProviderReadFlags flags
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.
Contains information relating to a node (i.e.