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