QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
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 "qgsrectangle.h"
39#include "qgsrelationmanager.h"
44#include "qgslayerdefinition.h"
45#include "qgsunittypes.h"
46#include "qgstransaction.h"
47#include "qgstransactiongroup.h"
51#include "qgsmeshlayer.h"
52#include "qgslayoutmanager.h"
53#include "qgsbookmarkmanager.h"
54#include "qgsmaplayerstore.h"
55#include "qgsziputils.h"
56#include "qgsauxiliarystorage.h"
57#include "qgssymbollayerutils.h"
58#include "qgsapplication.h"
64#include "qgsvectortilelayer.h"
65#include "qgsruntimeprofiler.h"
66#include "qgsannotationlayer.h"
67#include "qgspointcloudlayer.h"
69#include "qgsgrouplayer.h"
70#include "qgsmapviewsmanager.h"
73
74#include <algorithm>
75#include <QApplication>
76#include <QFileInfo>
77#include <QDomNode>
78#include <QObject>
79#include <QTextStream>
80#include <QTemporaryFile>
81#include <QDir>
82#include <QUrl>
83#include <QStandardPaths>
84#include <QUuid>
85#include <QRegularExpression>
86
87#ifdef _MSC_VER
88#include <sys/utime.h>
89#else
90#include <utime.h>
91#endif
92
93// canonical project instance
94QgsProject *QgsProject::sProject = nullptr;
95
104QStringList makeKeyTokens_( const QString &scope, const QString &key )
105{
106 QStringList keyTokens = QStringList( scope );
107#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
108 keyTokens += key.split( '/', QString::SkipEmptyParts );
109#else
110 keyTokens += key.split( '/', Qt::SkipEmptyParts );
111#endif
112
113 // be sure to include the canonical root node
114 keyTokens.push_front( QStringLiteral( "properties" ) );
115
116 //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.
117 for ( int i = 0; i < keyTokens.size(); ++i )
118 {
119 const QString keyToken = keyTokens.at( i );
120
121 //invalid chars in XML are found at http://www.w3.org/TR/REC-xml/#NT-NameChar
122 //note : it seems \x10000-\xEFFFF is valid, but it when added to the regexp, a lot of unwanted characters remain
123 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}])" ) );
124 if ( keyToken.contains( sInvalidRegexp ) )
125 {
126 const QString errorString = QObject::tr( "Entry token invalid : '%1'. The token will not be saved to file." ).arg( keyToken );
127 QgsMessageLog::logMessage( errorString, QString(), Qgis::MessageLevel::Critical );
128 }
129 }
130
131 return keyTokens;
132}
133
134
135
145QgsProjectProperty *findKey_( const QString &scope,
146 const QString &key,
147 QgsProjectPropertyKey &rootProperty )
148{
149 QgsProjectPropertyKey *currentProperty = &rootProperty;
150 QgsProjectProperty *nextProperty; // link to next property down hierarchy
151
152 QStringList keySequence = makeKeyTokens_( scope, key );
153
154 while ( !keySequence.isEmpty() )
155 {
156 // if the current head of the sequence list matches the property name,
157 // then traverse down the property hierarchy
158 if ( keySequence.first() == currentProperty->name() )
159 {
160 // remove front key since we're traversing down a level
161 keySequence.pop_front();
162
163 if ( 1 == keySequence.count() )
164 {
165 // if we have only one key name left, then return the key found
166 return currentProperty->find( keySequence.front() );
167 }
168 else if ( keySequence.isEmpty() )
169 {
170 // if we're out of keys then the current property is the one we
171 // want; i.e., we're in the rate case of being at the top-most
172 // property node
173 return currentProperty;
174 }
175 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
176 {
177 if ( nextProperty->isKey() )
178 {
179 currentProperty = static_cast<QgsProjectPropertyKey *>( nextProperty );
180 }
181 else if ( nextProperty->isValue() && 1 == keySequence.count() )
182 {
183 // it may be that this may be one of several property value
184 // nodes keyed by QDict string; if this is the last remaining
185 // key token and the next property is a value node, then
186 // that's the situation, so return the currentProperty
187 return currentProperty;
188 }
189 else
190 {
191 // QgsProjectPropertyValue not Key, so return null
192 return nullptr;
193 }
194 }
195 else
196 {
197 // if the next key down isn't found
198 // then the overall key sequence doesn't exist
199 return nullptr;
200 }
201 }
202 else
203 {
204 return nullptr;
205 }
206 }
207
208 return nullptr;
209}
210
211
212
222QgsProjectProperty *addKey_( const QString &scope,
223 const QString &key,
224 QgsProjectPropertyKey *rootProperty,
225 const QVariant &value,
226 bool &propertiesModified )
227{
228 QStringList keySequence = makeKeyTokens_( scope, key );
229
230 // cursor through property key/value hierarchy
231 QgsProjectPropertyKey *currentProperty = rootProperty;
232 QgsProjectProperty *nextProperty; // link to next property down hierarchy
233 QgsProjectPropertyKey *newPropertyKey = nullptr;
234
235 propertiesModified = false;
236 while ( ! keySequence.isEmpty() )
237 {
238 // if the current head of the sequence list matches the property name,
239 // then traverse down the property hierarchy
240 if ( keySequence.first() == currentProperty->name() )
241 {
242 // remove front key since we're traversing down a level
243 keySequence.pop_front();
244
245 // if key sequence has one last element, then we use that as the
246 // name to store the value
247 if ( 1 == keySequence.count() )
248 {
249 QgsProjectProperty *property = currentProperty->find( keySequence.front() );
250 if ( !property || property->value() != value )
251 {
252 currentProperty->setValue( keySequence.front(), value );
253 propertiesModified = true;
254 }
255
256 return currentProperty;
257 }
258 // we're at the top element if popping the keySequence element
259 // will leave it empty; in that case, just add the key
260 else if ( keySequence.isEmpty() )
261 {
262 if ( currentProperty->value() != value )
263 {
264 currentProperty->setValue( value );
265 propertiesModified = true;
266 }
267
268 return currentProperty;
269 }
270 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
271 {
272 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
273
274 if ( currentProperty )
275 {
276 continue;
277 }
278 else // QgsProjectPropertyValue not Key, so return null
279 {
280 return nullptr;
281 }
282 }
283 else // the next subkey doesn't exist, so add it
284 {
285 if ( ( newPropertyKey = currentProperty->addKey( keySequence.first() ) ) )
286 {
287 currentProperty = newPropertyKey;
288 }
289 continue;
290 }
291 }
292 else
293 {
294 return nullptr;
295 }
296 }
297
298 return nullptr;
299}
300
308void removeKey_( const QString &scope,
309 const QString &key,
310 QgsProjectPropertyKey &rootProperty )
311{
312 QgsProjectPropertyKey *currentProperty = &rootProperty;
313
314 QgsProjectProperty *nextProperty = nullptr; // link to next property down hierarchy
315 QgsProjectPropertyKey *previousQgsPropertyKey = nullptr; // link to previous property up hierarchy
316
317 QStringList keySequence = makeKeyTokens_( scope, key );
318
319 while ( ! keySequence.isEmpty() )
320 {
321 // if the current head of the sequence list matches the property name,
322 // then traverse down the property hierarchy
323 if ( keySequence.first() == currentProperty->name() )
324 {
325 // remove front key since we're traversing down a level
326 keySequence.pop_front();
327
328 // if we have only one key name left, then try to remove the key
329 // with that name
330 if ( 1 == keySequence.count() )
331 {
332 currentProperty->removeKey( keySequence.front() );
333 }
334 // if we're out of keys then the current property is the one we
335 // want to remove, but we can't delete it directly; we need to
336 // delete it from the parent property key container
337 else if ( keySequence.isEmpty() )
338 {
339 previousQgsPropertyKey->removeKey( currentProperty->name() );
340 }
341 else if ( ( nextProperty = currentProperty->find( keySequence.first() ) ) )
342 {
343 previousQgsPropertyKey = currentProperty;
344 currentProperty = dynamic_cast<QgsProjectPropertyKey *>( nextProperty );
345
346 if ( currentProperty )
347 {
348 continue;
349 }
350 else // QgsProjectPropertyValue not Key, so return null
351 {
352 return;
353 }
354 }
355 else // if the next key down isn't found
356 {
357 // then the overall key sequence doesn't exist
358 return;
359 }
360 }
361 else
362 {
363 return;
364 }
365 }
366}
367
368QgsProject::QgsProject( QObject *parent, Qgis::ProjectCapabilities capabilities )
369 : QObject( parent )
370 , mCapabilities( capabilities )
371 , mLayerStore( new QgsMapLayerStore( this ) )
372 , mBadLayerHandler( new QgsProjectBadLayerHandler() )
373 , mSnappingConfig( this )
374 , mRelationManager( new QgsRelationManager( this ) )
375 , mAnnotationManager( new QgsAnnotationManager( this ) )
376 , mLayoutManager( new QgsLayoutManager( this ) )
377 , m3DViewsManager( new QgsMapViewsManager( this ) )
378 , mBookmarkManager( QgsBookmarkManager::createProjectBasedManager( this ) )
379 , mViewSettings( new QgsProjectViewSettings( this ) )
380 , mStyleSettings( new QgsProjectStyleSettings( this ) )
381 , mTimeSettings( new QgsProjectTimeSettings( this ) )
382 , mElevationProperties( new QgsProjectElevationProperties( this ) )
383 , mDisplaySettings( new QgsProjectDisplaySettings( this ) )
384 , mRootGroup( new QgsLayerTree )
385 , mLabelingEngineSettings( new QgsLabelingEngineSettings )
386 , mArchive( new QgsArchive() )
387 , mAuxiliaryStorage( new QgsAuxiliaryStorage() )
388{
389 mProperties.setName( QStringLiteral( "properties" ) );
390
391 mMainAnnotationLayer = new QgsAnnotationLayer( QObject::tr( "Annotations" ), QgsAnnotationLayer::LayerOptions( mTransformContext ) );
392 mMainAnnotationLayer->setParent( this );
393
394 clear();
395
396 // bind the layer tree to the map layer registry.
397 // whenever layers are added to or removed from the registry,
398 // layer tree will be updated
399 mLayerTreeRegistryBridge = new QgsLayerTreeRegistryBridge( mRootGroup, this, this );
400 connect( this, &QgsProject::layersAdded, this, &QgsProject::onMapLayersAdded );
401 connect( this, &QgsProject::layersRemoved, this, [ = ] { cleanTransactionGroups(); } );
402 connect( this, qOverload< const QList<QgsMapLayer *> & >( &QgsProject::layersWillBeRemoved ), this, &QgsProject::onMapLayersRemoved );
403
404 // proxy map layer store signals to this
405 connect( mLayerStore.get(), qOverload<const QStringList &>( &QgsMapLayerStore::layersWillBeRemoved ),
406 this, [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
407 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ),
408 this, [ = ]( const QList<QgsMapLayer *> &layers ) { mProjectScope.reset(); emit layersWillBeRemoved( layers ); } );
409 connect( mLayerStore.get(), qOverload< const QString & >( &QgsMapLayerStore::layerWillBeRemoved ),
410 this, [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
411 connect( mLayerStore.get(), qOverload< QgsMapLayer * >( &QgsMapLayerStore::layerWillBeRemoved ),
412 this, [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWillBeRemoved( layer ); } );
413 connect( mLayerStore.get(), qOverload<const QStringList & >( &QgsMapLayerStore::layersRemoved ), this,
414 [ = ]( const QStringList & layers ) { mProjectScope.reset(); emit layersRemoved( layers ); } );
415 connect( mLayerStore.get(), &QgsMapLayerStore::layerRemoved, this,
416 [ = ]( const QString & layer ) { mProjectScope.reset(); emit layerRemoved( layer ); } );
417 connect( mLayerStore.get(), &QgsMapLayerStore::allLayersRemoved, this,
418 [ = ]() { mProjectScope.reset(); emit removeAll(); } );
419 connect( mLayerStore.get(), &QgsMapLayerStore::layersAdded, this,
420 [ = ]( const QList< QgsMapLayer * > &layers ) { mProjectScope.reset(); emit layersAdded( layers ); } );
421 connect( mLayerStore.get(), &QgsMapLayerStore::layerWasAdded, this,
422 [ = ]( QgsMapLayer * layer ) { mProjectScope.reset(); emit layerWasAdded( layer ); } );
423
425 {
427 }
428
429 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersWillBeRemoved ), this,
430 [ = ]( const QList<QgsMapLayer *> &layers )
431 {
432 for ( const auto &layer : layers )
433 {
434 disconnect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
435 }
436 }
437 );
438 connect( mLayerStore.get(), qOverload< const QList<QgsMapLayer *> & >( &QgsMapLayerStore::layersAdded ), this,
439 [ = ]( const QList<QgsMapLayer *> &layers )
440 {
441 for ( const auto &layer : layers )
442 {
443 connect( layer, &QgsMapLayer::dataSourceChanged, mRelationManager, &QgsRelationManager::updateRelationsStatus );
444 }
445 }
446 );
447
451
452 mStyleSettings->combinedStyleModel()->addDefaultStyle();
453}
454
455
457{
458 mIsBeingDeleted = true;
459
460 clear();
461 delete mBadLayerHandler;
462 delete mRelationManager;
463 delete mLayerTreeRegistryBridge;
464 delete mRootGroup;
465 if ( this == sProject )
466 {
467 sProject = nullptr;
468 }
469}
470
472{
473 sProject = project;
474}
475
476
478{
479 if ( !sProject )
480 {
481 sProject = new QgsProject;
482
484 }
485 return sProject;
486}
487
488void QgsProject::setTitle( const QString &title )
489{
490 if ( title == mMetadata.title() )
491 return;
492
493 mMetadata.setTitle( title );
494 mProjectScope.reset();
495 emit metadataChanged();
496
497 setDirty( true );
498}
499
500QString QgsProject::title() const
501{
502 return mMetadata.title();
503}
504
505void QgsProject::setFlags( Qgis::ProjectFlags flags )
506{
507 const bool oldEvaluateDefaultValues = mFlags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
508 const bool newEvaluateDefaultValues = flags & Qgis::ProjectFlag::EvaluateDefaultValuesOnProviderSide;
509 if ( oldEvaluateDefaultValues != newEvaluateDefaultValues )
510 {
511 const QMap<QString, QgsMapLayer *> layers = mapLayers();
512 for ( auto layerIt = layers.constBegin(); layerIt != layers.constEnd(); ++layerIt )
513 {
514 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( layerIt.value() ) )
515 {
516 vl->dataProvider()->setProviderProperty( QgsVectorDataProvider::EvaluateDefaultValues, newEvaluateDefaultValues );
517 }
518 }
519 }
520
521 const bool oldTrustLayerMetadata = mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
522 const bool newTrustLayerMetadata = flags & Qgis::ProjectFlag::TrustStoredLayerStatistics;
523 if ( oldTrustLayerMetadata != newTrustLayerMetadata )
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->setReadExtentFromXml( newTrustLayerMetadata );
531 }
532 }
533 }
534
535 mFlags = flags;
536}
537
538void QgsProject::setFlag( Qgis::ProjectFlag flag, bool enabled )
539{
540 Qgis::ProjectFlags newFlags = mFlags;
541 if ( enabled )
542 newFlags |= flag;
543 else
544 newFlags &= ~( static_cast< int >( flag ) );
545 setFlags( newFlags );
546}
547
548QString QgsProject::saveUser() const
549{
550 return mSaveUser;
551}
552
554{
555 return mSaveUserFull;
556}
557
559{
560 return mSaveDateTime;
561}
562
564{
565 return mSaveVersion;
566}
567
569{
570 return mDirty;
571}
572
573void QgsProject::setDirty( const bool dirty )
574{
575 if ( dirty && mDirtyBlockCount > 0 )
576 return;
577
578 if ( dirty )
579 emit dirtySet();
580
581 if ( mDirty == dirty )
582 return;
583
584 mDirty = dirty;
585 emit isDirtyChanged( mDirty );
586}
587
588void QgsProject::setPresetHomePath( const QString &path )
589{
590 if ( path == mHomePath )
591 return;
592
593 mHomePath = path;
594 mCachedHomePath.clear();
595 mProjectScope.reset();
596
597 emit homePathChanged();
598
599 setDirty( true );
600}
601
602void QgsProject::registerTranslatableContainers( QgsTranslationContext *translationContext, QgsAttributeEditorContainer *parent, const QString &layerId )
603{
604 const QList<QgsAttributeEditorElement *> elements = parent->children();
605
606 for ( QgsAttributeEditorElement *element : elements )
607 {
608 if ( element->type() == QgsAttributeEditorElement::AeTypeContainer )
609 {
610 QgsAttributeEditorContainer *container = dynamic_cast<QgsAttributeEditorContainer *>( element );
611
612 translationContext->registerTranslation( QStringLiteral( "project:layers:%1:formcontainers" ).arg( layerId ), container->name() );
613
614 if ( !container->children().empty() )
615 registerTranslatableContainers( translationContext, container, layerId );
616 }
617 }
618}
619
621{
622 //register layers
623 const QList<QgsLayerTreeLayer *> layers = mRootGroup->findLayers();
624
625 for ( const QgsLayerTreeLayer *layer : layers )
626 {
627 translationContext->registerTranslation( QStringLiteral( "project:layers:%1" ).arg( layer->layerId() ), layer->name() );
628
629 QgsMapLayer *mapLayer = layer->layer();
631 {
632 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer );
633
634 //register aliases and fields
635 const QgsFields fields = vlayer->fields();
636 for ( const QgsField &field : fields )
637 {
638 QString fieldName;
639 if ( field.alias().isEmpty() )
640 fieldName = field.name();
641 else
642 fieldName = field.alias();
643
644 translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fieldaliases" ).arg( vlayer->id() ), fieldName );
645
646 if ( field.editorWidgetSetup().type() == QLatin1String( "ValueRelation" ) )
647 {
648 translationContext->registerTranslation( QStringLiteral( "project:layers:%1:fields:%2:valuerelationvalue" ).arg( vlayer->id(), field.name() ), field.editorWidgetSetup().config().value( QStringLiteral( "Value" ) ).toString() );
649 }
650 }
651
652 //register formcontainers
653 registerTranslatableContainers( translationContext, vlayer->editFormConfig().invisibleRootContainer(), vlayer->id() );
654
655 }
656 }
657
658 //register layergroups
659 const QList<QgsLayerTreeGroup *> groupLayers = mRootGroup->findGroups();
660 for ( const QgsLayerTreeGroup *groupLayer : groupLayers )
661 {
662 translationContext->registerTranslation( QStringLiteral( "project:layergroups" ), groupLayer->name() );
663 }
664
665 //register relations
666 const QList<QgsRelation> &relations = mRelationManager->relations().values();
667 for ( const QgsRelation &relation : relations )
668 {
669 translationContext->registerTranslation( QStringLiteral( "project:relations" ), relation.name() );
670 }
671}
672
674{
675 mDataDefinedServerProperties = properties;
676}
677
679{
680 return mDataDefinedServerProperties;
681}
682
684{
685 switch ( mTransactionMode )
686 {
689 {
690 if ( ! vectorLayer )
691 return false;
692 return vectorLayer->startEditing();
693 }
694
696 return mEditBufferGroup.startEditing();
697 }
698
699 return false;
700}
701
702bool QgsProject::commitChanges( QStringList &commitErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
703{
704 switch ( mTransactionMode )
705 {
708 {
709 if ( ! vectorLayer )
710 {
711 commitErrors.append( tr( "Trying to commit changes without a layer specified. This only works if the transaction mode is buffered" ) );
712 return false;
713 }
714 bool success = vectorLayer->commitChanges( stopEditing );
715 commitErrors = vectorLayer->commitErrors();
716 return success;
717 }
718
720 return mEditBufferGroup.commitChanges( commitErrors, stopEditing );
721 }
722
723 return false;
724}
725
726bool QgsProject::rollBack( QStringList &rollbackErrors, bool stopEditing, QgsVectorLayer *vectorLayer )
727{
728 switch ( mTransactionMode )
729 {
732 {
733 if ( ! vectorLayer )
734 {
735 rollbackErrors.append( tr( "Trying to roll back changes without a layer specified. This only works if the transaction mode is buffered" ) );
736 return false;
737 }
738 bool success = vectorLayer->rollBack( stopEditing );
739 rollbackErrors = vectorLayer->commitErrors();
740 return success;
741 }
742
744 return mEditBufferGroup.rollBack( rollbackErrors, stopEditing );
745 }
746
747 return false;
748}
749
750void QgsProject::setFileName( const QString &name )
751{
752 if ( name == mFile.fileName() )
753 return;
754
755 const QString oldHomePath = homePath();
756
757 mFile.setFileName( name );
758 mCachedHomePath.clear();
759 mProjectScope.reset();
760
761 emit fileNameChanged();
762
763 const QString newHomePath = homePath();
764 if ( newHomePath != oldHomePath )
765 emit homePathChanged();
766
767 setDirty( true );
768}
769
770QString QgsProject::fileName() const
771{
772 return mFile.fileName();
773}
774
775void QgsProject::setOriginalPath( const QString &path )
776{
777 mOriginalPath = path;
778}
779
781{
782 return mOriginalPath;
783}
784
785QFileInfo QgsProject::fileInfo() const
786{
787 return QFileInfo( mFile );
788}
789
791{
793}
794
796{
797 if ( QgsProjectStorage *storage = projectStorage() )
798 {
800 storage->readProjectStorageMetadata( mFile.fileName(), metadata );
801 return metadata.lastModified;
802 }
803 else
804 {
805 return QFileInfo( mFile.fileName() ).lastModified();
806 }
807}
808
810{
811 if ( projectStorage() )
812 return QString();
813
814 if ( mFile.fileName().isEmpty() )
815 return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
816
817 return QFileInfo( mFile.fileName() ).absolutePath();
818}
819
821{
822 if ( projectStorage() )
823 return QString();
824
825 if ( mFile.fileName().isEmpty() )
826 return QString(); // this is to protect ourselves from getting current directory from QFileInfo::absoluteFilePath()
827
828 return QFileInfo( mFile.fileName() ).absoluteFilePath();
829}
830
831QString QgsProject::baseName() const
832{
833 if ( QgsProjectStorage *storage = projectStorage() )
834 {
836 storage->readProjectStorageMetadata( mFile.fileName(), metadata );
837 return metadata.name;
838 }
839 else
840 {
841 return QFileInfo( mFile.fileName() ).completeBaseName();
842 }
843}
844
846{
847 const bool absolutePaths = readBoolEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
849}
850
852{
853 switch ( type )
854 {
856 writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), true );
857 break;
859 writeEntry( QStringLiteral( "Paths" ), QStringLiteral( "/Absolute" ), false );
860 break;
861 }
862}
863
865{
866 return mCrs;
867}
868
869void QgsProject::setCrs( const QgsCoordinateReferenceSystem &crs, bool adjustEllipsoid )
870{
871 if ( crs != mCrs )
872 {
873 mCrs = crs;
874 writeEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), crs.isValid() ? 1 : 0 );
875 mProjectScope.reset();
876
877 // if annotation layer doesn't have a crs (i.e. in a newly created project), it should
878 // initially inherit the project CRS
879 if ( !mMainAnnotationLayer->crs().isValid() || mMainAnnotationLayer->isEmpty() )
880 mMainAnnotationLayer->setCrs( crs );
881
882 setDirty( true );
883 emit crsChanged();
884 }
885
886 if ( adjustEllipsoid )
888}
889
891{
892 if ( !crs().isValid() )
893 return geoNone();
894
895 return readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), geoNone() );
896}
897
898void QgsProject::setEllipsoid( const QString &ellipsoid )
899{
900 if ( ellipsoid == readEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ) ) )
901 return;
902
903 mProjectScope.reset();
904 writeEntry( QStringLiteral( "Measure" ), QStringLiteral( "/Ellipsoid" ), ellipsoid );
906}
907
909{
910 return mTransformContext;
911}
912
914{
915 if ( context == mTransformContext )
916 return;
917
918 mTransformContext = context;
919 mProjectScope.reset();
920
921 mMainAnnotationLayer->setTransformContext( context );
922 for ( auto &layer : mLayerStore.get()->mapLayers() )
923 {
924 layer->setTransformContext( context );
925 }
927}
928
930{
931 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
932
933 mProjectScope.reset();
934 mFile.setFileName( QString() );
935 mProperties.clearKeys();
936 mSaveUser.clear();
937 mSaveUserFull.clear();
938 mSaveDateTime = QDateTime();
939 mSaveVersion = QgsProjectVersion();
940 mHomePath.clear();
941 mCachedHomePath.clear();
942 mTransactionMode = Qgis::TransactionMode::Disabled;
943 mFlags = Qgis::ProjectFlags();
944 mDirty = false;
945 mCustomVariables.clear();
947 mMetadata = QgsProjectMetadata();
948 if ( !mSettings.value( QStringLiteral( "projects/anonymize_new_projects" ), false, QgsSettings::Core ).toBool() )
949 {
950 mMetadata.setCreationDateTime( QDateTime::currentDateTime() );
952 }
953 emit metadataChanged();
954
956 context.readSettings();
957 setTransformContext( context );
958
959 //fallback to QGIS default measurement unit
960 bool ok = false;
961 const QgsUnitTypes::DistanceUnit distanceUnit = QgsUnitTypes::decodeDistanceUnit( mSettings.value( QStringLiteral( "/qgis/measure/displayunits" ) ).toString(), &ok );
962 setDistanceUnits( ok ? distanceUnit : QgsUnitTypes::DistanceMeters );
963 ok = false;
964 const QgsUnitTypes::AreaUnit areaUnits = QgsUnitTypes::decodeAreaUnit( mSettings.value( QStringLiteral( "/qgis/measure/areaunits" ) ).toString(), &ok );
966
967 mEmbeddedLayers.clear();
968 mRelationManager->clear();
969 mAnnotationManager->clear();
970 mLayoutManager->clear();
971 m3DViewsManager->clear();
972 mBookmarkManager->clear();
973 mViewSettings->reset();
974 mTimeSettings->reset();
975 mElevationProperties->reset();
976 mDisplaySettings->reset();
977 mSnappingConfig.reset();
978 mAvoidIntersectionsMode = Qgis::AvoidIntersectionsMode::AllowIntersections;
981
982 mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
984
985 mLabelingEngineSettings->clear();
986
987 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage() );
988 mArchive.reset( new QgsArchive() );
989
990 // must happen after archive reset!
991 mStyleSettings->reset();
992
994
995 if ( !mIsBeingDeleted )
996 {
997 // possibly other signals should also not be thrown on destruction -- e.g. labelEngineSettingsChanged, etc.
999 }
1000
1001 // reset some default project properties
1002 // XXX THESE SHOULD BE MOVED TO STATUSBAR RELATED SOURCE
1003 writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/Automatic" ), true );
1004 writeEntry( QStringLiteral( "PositionPrecision" ), QStringLiteral( "/DecimalPlaces" ), 2 );
1005
1006 const bool defaultRelativePaths = mSettings.value( QStringLiteral( "/qgis/defaultProjectPathsRelative" ), true ).toBool();
1008
1009 int red = mSettings.value( QStringLiteral( "qgis/default_canvas_color_red" ), 255 ).toInt();
1010 int green = mSettings.value( QStringLiteral( "qgis/default_canvas_color_green" ), 255 ).toInt();
1011 int blue = mSettings.value( QStringLiteral( "qgis/default_canvas_color_blue" ), 255 ).toInt();
1012 setBackgroundColor( QColor( red, green, blue ) );
1013
1014 red = mSettings.value( QStringLiteral( "qgis/default_selection_color_red" ), 255 ).toInt();
1015 green = mSettings.value( QStringLiteral( "qgis/default_selection_color_green" ), 255 ).toInt();
1016 blue = mSettings.value( QStringLiteral( "qgis/default_selection_color_blue" ), 0 ).toInt();
1017 const int alpha = mSettings.value( QStringLiteral( "qgis/default_selection_color_alpha" ), 255 ).toInt();
1018 setSelectionColor( QColor( red, green, blue, alpha ) );
1019
1020 mSnappingConfig.clearIndividualLayerSettings();
1021
1023 mRootGroup->clear();
1024 if ( mMainAnnotationLayer )
1025 mMainAnnotationLayer->reset();
1026
1027 snapSingleBlocker.release();
1028
1029 if ( !mBlockSnappingUpdates )
1030 emit snappingConfigChanged( mSnappingConfig );
1031
1032 setDirty( false );
1033 emit homePathChanged();
1034 emit cleared();
1035}
1036
1037// basically a debugging tool to dump property list values
1038void dump_( const QgsProjectPropertyKey &topQgsPropertyKey )
1039{
1040 QgsDebugMsgLevel( QStringLiteral( "current properties:" ), 3 );
1041 topQgsPropertyKey.dump();
1042}
1043
1072void _getProperties( const QDomDocument &doc, QgsProjectPropertyKey &project_properties )
1073{
1074 const QDomElement propertiesElem = doc.documentElement().firstChildElement( QStringLiteral( "properties" ) );
1075
1076 if ( propertiesElem.isNull() ) // no properties found, so we're done
1077 {
1078 return;
1079 }
1080
1081 const QDomNodeList scopes = propertiesElem.childNodes();
1082
1083 if ( propertiesElem.firstChild().isNull() )
1084 {
1085 QgsDebugMsg( QStringLiteral( "empty ``properties'' XML tag ... bailing" ) );
1086 return;
1087 }
1088
1089 if ( ! project_properties.readXml( propertiesElem ) )
1090 {
1091 QgsDebugMsg( QStringLiteral( "Project_properties.readXml() failed" ) );
1092 }
1093}
1094
1101QgsPropertyCollection getDataDefinedServerProperties( const QDomDocument &doc, const QgsPropertiesDefinition &dataDefinedServerPropertyDefinitions )
1102{
1103 QgsPropertyCollection ddServerProperties;
1104 // Read data defined server properties
1105 const QDomElement ddElem = doc.documentElement().firstChildElement( QStringLiteral( "dataDefinedServerProperties" ) );
1106 if ( !ddElem.isNull() )
1107 {
1108 if ( !ddServerProperties.readXml( ddElem, dataDefinedServerPropertyDefinitions ) )
1109 {
1110 QgsDebugMsg( QStringLiteral( "dataDefinedServerProperties.readXml() failed" ) );
1111 }
1112 }
1113 return ddServerProperties;
1114}
1115
1120static void _getTitle( const QDomDocument &doc, QString &title )
1121{
1122 const QDomElement titleNode = doc.documentElement().firstChildElement( QStringLiteral( "title" ) );
1123
1124 title.clear(); // by default the title will be empty
1125
1126 if ( titleNode.isNull() )
1127 {
1128 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1129 return;
1130 }
1131
1132 if ( !titleNode.hasChildNodes() ) // if not, then there's no actual text
1133 {
1134 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1135 return;
1136 }
1137
1138 const QDomNode titleTextNode = titleNode.firstChild(); // should only have one child
1139
1140 if ( !titleTextNode.isText() )
1141 {
1142 QgsDebugMsgLevel( QStringLiteral( "unable to find title element" ), 2 );
1143 return;
1144 }
1145
1146 const QDomText titleText = titleTextNode.toText();
1147
1148 title = titleText.data();
1149
1150}
1151
1152static void readProjectFileMetadata( const QDomDocument &doc, QString &lastUser, QString &lastUserFull, QDateTime &lastSaveDateTime )
1153{
1154 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1155
1156 if ( !nl.count() )
1157 {
1158 QgsDebugMsg( QStringLiteral( "unable to find qgis element" ) );
1159 return;
1160 }
1161
1162 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1163
1164 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1165 lastUser = qgisElement.attribute( QStringLiteral( "saveUser" ), QString() );
1166 lastUserFull = qgisElement.attribute( QStringLiteral( "saveUserFull" ), QString() );
1167 lastSaveDateTime = QDateTime::fromString( qgisElement.attribute( QStringLiteral( "saveDateTime" ), QString() ), Qt::ISODate );
1168}
1169
1170
1171QgsProjectVersion getVersion( const QDomDocument &doc )
1172{
1173 const QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
1174
1175 if ( !nl.count() )
1176 {
1177 QgsDebugMsg( QStringLiteral( " unable to find qgis element in project file" ) );
1178 return QgsProjectVersion( 0, 0, 0, QString() );
1179 }
1180
1181 const QDomNode qgisNode = nl.item( 0 ); // there should only be one, so zeroth element OK
1182
1183 const QDomElement qgisElement = qgisNode.toElement(); // qgis node should be element
1184 QgsProjectVersion projectVersion( qgisElement.attribute( QStringLiteral( "version" ) ) );
1185 return projectVersion;
1186}
1187
1188
1190{
1191 return mSnappingConfig;
1192}
1193
1195{
1196 if ( mSnappingConfig == snappingConfig )
1197 return;
1198
1199 mSnappingConfig = snappingConfig;
1200 setDirty( true );
1201 emit snappingConfigChanged( mSnappingConfig );
1202}
1203
1205{
1206 if ( mAvoidIntersectionsMode == mode )
1207 return;
1208
1209 mAvoidIntersectionsMode = mode;
1211}
1212
1213bool QgsProject::_getMapLayers( const QDomDocument &doc, QList<QDomNode> &brokenNodes, Qgis::ProjectReadFlags flags )
1214{
1215 // Layer order is set by the restoring the legend settings from project file.
1216 // This is done on the 'readProject( ... )' signal
1217
1218 QDomElement layerElement = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
1219
1220 // process the map layer nodes
1221
1222 if ( layerElement.isNull() ) // if we have no layers to process, bail
1223 {
1224 return true; // Decided to return "true" since it's
1225 // possible for there to be a project with no
1226 // layers; but also, more imporantly, this
1227 // would cause the tests/qgsproject to fail
1228 // since the test suite doesn't currently
1229 // support test layers
1230 }
1231
1232 bool returnStatus = true;
1233 int numLayers = 0;
1234
1235 while ( ! layerElement.isNull() )
1236 {
1237 numLayers++;
1238 layerElement = layerElement.nextSiblingElement( QStringLiteral( "maplayer" ) );
1239 }
1240
1241 // order layers based on their dependencies
1242 QgsScopedRuntimeProfile profile( tr( "Sorting layers" ), QStringLiteral( "projectload" ) );
1243 const QgsLayerDefinition::DependencySorter depSorter( doc );
1244 if ( depSorter.hasCycle() )
1245 return false;
1246
1247 // Missing a dependency? We still load all the layers, otherwise the project is completely broken!
1248 if ( depSorter.hasMissingDependency() )
1249 returnStatus = false;
1250
1251 emit layerLoaded( 0, numLayers );
1252
1253 const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
1254 const int totalLayerCount = sortedLayerNodes.count();
1255
1256 int i = 0;
1257 for ( const QDomNode &node : sortedLayerNodes )
1258 {
1259 const QDomElement element = node.toElement();
1260
1261 const QString name = translate( QStringLiteral( "project:layers:%1" ).arg( node.namedItem( QStringLiteral( "id" ) ).toElement().text() ), node.namedItem( QStringLiteral( "layername" ) ).toElement().text() );
1262 if ( !name.isNull() )
1263 emit loadingLayer( tr( "Loading layer %1" ).arg( name ) );
1264
1265 profile.switchTask( name );
1266
1267 if ( element.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
1268 {
1269 createEmbeddedLayer( element.attribute( QStringLiteral( "id" ) ), readPath( element.attribute( QStringLiteral( "project" ) ) ), brokenNodes, true, flags );
1270 }
1271 else
1272 {
1273 QgsReadWriteContext context;
1274 context.setPathResolver( pathResolver() );
1275 context.setProjectTranslator( this );
1277
1278 if ( !addLayer( element, brokenNodes, context, flags ) )
1279 {
1280 returnStatus = false;
1281 }
1282 const auto messages = context.takeMessages();
1283 if ( !messages.isEmpty() )
1284 {
1285 emit loadingLayerMessageReceived( tr( "Loading layer %1" ).arg( name ), messages );
1286 }
1287 }
1288 emit layerLoaded( i + 1, totalLayerCount );
1289 i++;
1290 }
1291
1292 return returnStatus;
1293}
1294
1295bool QgsProject::addLayer( const QDomElement &layerElem, QList<QDomNode> &brokenNodes, QgsReadWriteContext &context, Qgis::ProjectReadFlags flags )
1296{
1297 const QString type = layerElem.attribute( QStringLiteral( "type" ) );
1298 QgsDebugMsgLevel( "Layer type is " + type, 4 );
1299 std::unique_ptr<QgsMapLayer> mapLayer;
1300
1301 QgsScopedRuntimeProfile profile( tr( "Create layer" ), QStringLiteral( "projectload" ) );
1302
1303 bool ok = false;
1304 const QgsMapLayerType layerType( QgsMapLayerFactory::typeFromString( type, ok ) );
1305 if ( !ok )
1306 {
1307 QgsDebugMsg( QStringLiteral( "Unknown layer type \"%1\"" ).arg( type ) );
1308 return false;
1309 }
1310
1311 switch ( layerType )
1312 {
1314 {
1315 mapLayer = std::make_unique<QgsVectorLayer>();
1316 // apply specific settings to vector layer
1317 if ( QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1318 {
1319 vl->setReadExtentFromXml( ( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( flags & Qgis::ProjectReadFlag::TrustLayerMetadata ) );
1320 }
1321 break;
1322 }
1323
1325 mapLayer = std::make_unique<QgsRasterLayer>();
1326 break;
1327
1329 mapLayer = std::make_unique<QgsMeshLayer>();
1330 break;
1331
1333 mapLayer = std::make_unique<QgsVectorTileLayer>();
1334 break;
1335
1337 mapLayer = std::make_unique<QgsPointCloudLayer>();
1338 break;
1339
1341 {
1342 const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
1343 mapLayer.reset( QgsApplication::pluginLayerRegistry()->createLayer( typeName ) );
1344 break;
1345 }
1346
1348 {
1349 const QgsAnnotationLayer::LayerOptions options( mTransformContext );
1350 mapLayer = std::make_unique<QgsAnnotationLayer>( QString(), options );
1351 break;
1352 }
1353
1355 {
1356 const QgsGroupLayer::LayerOptions options( mTransformContext );
1357 mapLayer = std::make_unique<QgsGroupLayer>( QString(), options );
1358 break;
1359 }
1360 }
1361
1362 if ( !mapLayer )
1363 {
1364 QgsDebugMsg( QStringLiteral( "Unable to create layer" ) );
1365 return false;
1366 }
1367
1368 Q_CHECK_PTR( mapLayer ); // NOLINT
1369
1370 // This is tricky: to avoid a leak we need to check if the layer was already in the store
1371 // because if it was, the newly created layer will not be added to the store and it would leak.
1372 const QString layerId { layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text() };
1373 Q_ASSERT( ! layerId.isEmpty() );
1374 const bool layerWasStored { layerStore()->mapLayer( layerId ) != nullptr };
1375
1376 // have the layer restore state that is stored in Dom node
1377 QgsMapLayer::ReadFlags layerFlags = QgsMapLayer::ReadFlags();
1378 if ( flags & Qgis::ProjectReadFlag::DontResolveLayers )
1380 // Propagate trust layer metadata flag
1381 if ( ( mFlags & Qgis::ProjectFlag::TrustStoredLayerStatistics ) || ( flags & Qgis::ProjectReadFlag::TrustLayerMetadata ) )
1383 // Propagate open layers in read-only mode
1385 layerFlags |= QgsMapLayer::FlagForceReadOnly;
1386
1387 profile.switchTask( tr( "Load layer source" ) );
1388 const bool layerIsValid = mapLayer->readLayerXml( layerElem, context, layerFlags ) && mapLayer->isValid();
1389
1390 profile.switchTask( tr( "Add layer to project" ) );
1391 QList<QgsMapLayer *> newLayers;
1392 newLayers << mapLayer.get();
1393 if ( layerIsValid || flags & Qgis::ProjectReadFlag::DontResolveLayers )
1394 {
1395 emit readMapLayer( mapLayer.get(), layerElem );
1396 addMapLayers( newLayers );
1397 // Try to resolve references here (this is necessary to set up joined fields that will be possibly used by
1398 // virtual layers that point to this layer's joined field in their query otherwise they won't be valid ),
1399 // a second attempt to resolve references will be done after all layers are loaded
1400 // see https://github.com/qgis/QGIS/issues/46834
1401 if ( QgsVectorLayer *vLayer = qobject_cast<QgsVectorLayer *>( mapLayer.get() ) )
1402 {
1403 vLayer->joinBuffer()->resolveReferences( this );
1404 }
1405 }
1406 else
1407 {
1408 // It's a bad layer: do not add to legend (the user will decide if she wants to do so)
1409 addMapLayers( newLayers, false );
1410 newLayers.first();
1411 QgsDebugMsg( "Unable to load " + type + " layer" );
1412 brokenNodes.push_back( layerElem );
1413 }
1414
1415 const bool wasEditable = layerElem.attribute( QStringLiteral( "editable" ), QStringLiteral( "0" ) ).toInt();
1416 if ( wasEditable )
1417 {
1418 mapLayer->setCustomProperty( QStringLiteral( "_layer_was_editable" ), true );
1419 }
1420 else
1421 {
1422 mapLayer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
1423 }
1424
1425 // It should be safe to delete the layer now if layer was stored, because all the store
1426 // had to to was to reset the data source in case the validity changed.
1427 if ( ! layerWasStored )
1428 {
1429 mapLayer.release();
1430 }
1431
1432 return layerIsValid;
1433}
1434
1435bool QgsProject::read( const QString &filename, Qgis::ProjectReadFlags flags )
1436{
1437 mFile.setFileName( filename );
1438 mCachedHomePath.clear();
1439 mProjectScope.reset();
1440
1441 return read( flags );
1442}
1443
1444bool QgsProject::read( Qgis::ProjectReadFlags flags )
1445{
1446 const QString filename = mFile.fileName();
1447 bool returnValue;
1448
1449 if ( QgsProjectStorage *storage = projectStorage() )
1450 {
1451 QTemporaryFile inDevice;
1452 if ( !inDevice.open() )
1453 {
1454 setError( tr( "Unable to open %1" ).arg( inDevice.fileName() ) );
1455 return false;
1456 }
1457
1458 QgsReadWriteContext context;
1459 context.setProjectTranslator( this );
1460 if ( !storage->readProject( filename, &inDevice, context ) )
1461 {
1462 QString err = tr( "Unable to open %1" ).arg( filename );
1463 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
1464 if ( !messages.isEmpty() )
1465 err += QStringLiteral( "\n\n" ) + messages.last().message();
1466 setError( err );
1467 return false;
1468 }
1469 returnValue = unzip( inDevice.fileName(), flags ); // calls setError() if returning false
1470 }
1471 else
1472 {
1473 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
1474 {
1475 returnValue = unzip( mFile.fileName(), flags );
1476 }
1477 else
1478 {
1479 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
1480 const QFileInfo finfo( mFile.fileName() );
1481 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
1482 if ( QFile( attachmentsZip ).exists() )
1483 {
1484 std::unique_ptr<QgsArchive> archive( new QgsArchive() );
1485 if ( archive->unzip( attachmentsZip ) )
1486 {
1487 mArchive = std::move( archive );
1488 }
1489 }
1490 returnValue = readProjectFile( mFile.fileName(), flags );
1491 }
1492
1493 //on translation we should not change the filename back
1494 if ( !mTranslator )
1495 {
1496 mFile.setFileName( filename );
1497 mCachedHomePath.clear();
1498 mProjectScope.reset();
1499 }
1500 else
1501 {
1502 //but delete the translator
1503 mTranslator.reset( nullptr );
1504 }
1505 }
1506 emit homePathChanged();
1507 return returnValue;
1508}
1509
1510bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlags flags )
1511{
1512 // avoid multiple emission of snapping updated signals
1513 ScopedIntIncrementor snapSignalBlock( &mBlockSnappingUpdates );
1514
1515 QFile projectFile( filename );
1516 clearError();
1517
1518 QgsApplication::profiler()->clear( QStringLiteral( "projectload" ) );
1519 QgsScopedRuntimeProfile profile( tr( "Setting up translations" ), QStringLiteral( "projectload" ) );
1520
1521 const QString localeFileName = QStringLiteral( "%1_%2" ).arg( QFileInfo( projectFile.fileName() ).baseName(), QgsApplication::settingsLocaleUserLocale.value() );
1522
1523 if ( QFile( QStringLiteral( "%1/%2.qm" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) ).exists() )
1524 {
1525 mTranslator.reset( new QTranslator() );
1526 ( void )mTranslator->load( localeFileName, QFileInfo( projectFile.fileName() ).absolutePath() );
1527 }
1528
1529 profile.switchTask( tr( "Reading project file" ) );
1530 std::unique_ptr<QDomDocument> doc( new QDomDocument( QStringLiteral( "qgis" ) ) );
1531
1532 if ( !projectFile.open( QIODevice::ReadOnly | QIODevice::Text ) )
1533 {
1534 projectFile.close();
1535
1536 setError( tr( "Unable to open %1" ).arg( projectFile.fileName() ) );
1537
1538 return false;
1539 }
1540
1541 // location of problem associated with errorMsg
1542 int line, column;
1543 QString errorMsg;
1544
1545 if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
1546 {
1547 const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
1548 .arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );
1549
1550 QgsDebugMsg( errorString );
1551
1552 projectFile.close();
1553
1554 setError( tr( "%1 for file %2" ).arg( errorString, projectFile.fileName() ) );
1555
1556 return false;
1557 }
1558
1559 projectFile.close();
1560
1561 QgsDebugMsgLevel( "Opened document " + projectFile.fileName(), 2 );
1562
1563 // get project version string, if any
1564 const QgsProjectVersion fileVersion = getVersion( *doc );
1565 const QgsProjectVersion thisVersion( Qgis::version() );
1566
1567 profile.switchTask( tr( "Updating project file" ) );
1568 if ( thisVersion > fileVersion )
1569 {
1570 const bool isOlderMajorVersion = fileVersion.majorVersion() < thisVersion.majorVersion();
1571
1572 if ( isOlderMajorVersion )
1573 {
1574 QgsLogger::warning( "Loading a file that was saved with an older "
1575 "version of qgis (saved in " + fileVersion.text() +
1576 ", loaded in " + Qgis::version() +
1577 "). Problems may occur." );
1578 }
1579
1580 QgsProjectFileTransform projectFile( *doc, fileVersion );
1581
1582 // Shows a warning when an old project file is read.
1584 emit oldProjectVersionWarning( fileVersion.text() );
1586 emit readVersionMismatchOccurred( fileVersion.text() );
1587
1588 projectFile.updateRevision( thisVersion );
1589 }
1590 else if ( fileVersion > thisVersion )
1591 {
1592 QgsLogger::warning( "Loading a file that was saved with a newer "
1593 "version of qgis (saved in " + fileVersion.text() +
1594 ", loaded in " + Qgis::version() +
1595 "). Problems may occur." );
1596
1597 emit readVersionMismatchOccurred( fileVersion.text() );
1598 }
1599
1600 // start new project, just keep the file name and auxiliary storage
1601 profile.switchTask( tr( "Creating auxiliary storage" ) );
1602 const QString fileName = mFile.fileName();
1603 std::unique_ptr<QgsAuxiliaryStorage> aStorage = std::move( mAuxiliaryStorage );
1604 std::unique_ptr<QgsArchive> archive = std::move( mArchive );
1605 clear();
1606 mAuxiliaryStorage = std::move( aStorage );
1607 mArchive = std::move( archive );
1608 mFile.setFileName( fileName );
1609 mCachedHomePath.clear();
1610 mProjectScope.reset();
1611 mSaveVersion = fileVersion;
1612
1613 // now get any properties
1614 profile.switchTask( tr( "Reading properties" ) );
1615 _getProperties( *doc, mProperties );
1616
1617 // now get the data defined server properties
1618 mDataDefinedServerProperties = getDataDefinedServerProperties( *doc, dataDefinedServerPropertyDefinitions() );
1619
1620 QgsDebugMsgLevel( QString::number( mProperties.count() ) + " properties read", 2 );
1621
1622#if 0
1623 dump_( mProperties );
1624#endif
1625
1626 // get older style project title
1627 QString oldTitle;
1628 _getTitle( *doc, oldTitle );
1629
1630 readProjectFileMetadata( *doc, mSaveUser, mSaveUserFull, mSaveDateTime );
1631
1632 const QDomNodeList homePathNl = doc->elementsByTagName( QStringLiteral( "homePath" ) );
1633 if ( homePathNl.count() > 0 )
1634 {
1635 const QDomElement homePathElement = homePathNl.at( 0 ).toElement();
1636 const QString homePath = homePathElement.attribute( QStringLiteral( "path" ) );
1637 if ( !homePath.isEmpty() )
1639 }
1640 else
1641 {
1642 emit homePathChanged();
1643 }
1644
1645 const QColor backgroundColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), 255 ),
1646 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), 255 ),
1647 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), 255 ) );
1649 const QColor selectionColor( readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), 255 ),
1650 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), 255 ),
1651 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), 255 ),
1652 readNumEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), 255 ) );
1654
1655
1656 const QString distanceUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QString() );
1657 if ( !distanceUnitString.isEmpty() )
1658 setDistanceUnits( QgsUnitTypes::decodeDistanceUnit( distanceUnitString ) );
1659
1660 const QString areaUnitString = readEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QString() );
1661 if ( !areaUnitString.isEmpty() )
1662 setAreaUnits( QgsUnitTypes::decodeAreaUnit( areaUnitString ) );
1663
1664 QgsReadWriteContext context;
1665 context.setPathResolver( pathResolver() );
1666 context.setProjectTranslator( this );
1667
1668 //crs
1670 if ( readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectionsEnabled" ), 0 ) )
1671 {
1672 // first preference - dedicated projectCrs node
1673 const QDomNode srsNode = doc->documentElement().namedItem( QStringLiteral( "projectCrs" ) );
1674 if ( !srsNode.isNull() )
1675 {
1676 projectCrs.readXml( srsNode );
1677 }
1678
1679 if ( !projectCrs.isValid() )
1680 {
1681 const QString projCrsString = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSProj4String" ) );
1682 const long currentCRS = readNumEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCRSID" ), -1 );
1683 const QString authid = readEntry( QStringLiteral( "SpatialRefSys" ), QStringLiteral( "/ProjectCrs" ) );
1684
1685 // authid should be prioritized over all
1686 const bool isUserAuthId = authid.startsWith( QLatin1String( "USER:" ), Qt::CaseInsensitive );
1687 if ( !authid.isEmpty() && !isUserAuthId )
1688 projectCrs = QgsCoordinateReferenceSystem( authid );
1689
1690 // try the CRS
1691 if ( !projectCrs.isValid() && currentCRS >= 0 )
1692 {
1693 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1694 }
1695
1696 // if that didn't produce a match, try the proj.4 string
1697 if ( !projCrsString.isEmpty() && ( authid.isEmpty() || isUserAuthId ) && ( !projectCrs.isValid() || projectCrs.toProj() != projCrsString ) )
1698 {
1699 projectCrs = QgsCoordinateReferenceSystem::fromProj( projCrsString );
1700 }
1701
1702 // last just take the given id
1703 if ( !projectCrs.isValid() )
1704 {
1705 projectCrs = QgsCoordinateReferenceSystem::fromSrsId( currentCRS );
1706 }
1707 }
1708 }
1709 mCrs = projectCrs;
1710
1711 QStringList datumErrors;
1712 if ( !mTransformContext.readXml( doc->documentElement(), context, datumErrors ) && !datumErrors.empty() )
1713 {
1714 emit missingDatumTransforms( datumErrors );
1715 }
1717
1718 //add variables defined in project file - do this early in the reading cycle, as other components
1719 //(e.g. layouts) may depend on these variables
1720 const QStringList variableNames = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ) );
1721 const QStringList variableValues = readListEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ) );
1722
1723 mCustomVariables.clear();
1724 if ( variableNames.length() == variableValues.length() )
1725 {
1726 for ( int i = 0; i < variableNames.length(); ++i )
1727 {
1728 mCustomVariables.insert( variableNames.at( i ), variableValues.at( i ) );
1729 }
1730 }
1731 else
1732 {
1733 QgsMessageLog::logMessage( tr( "Project Variables Invalid" ), tr( "The project contains invalid variable settings." ) );
1734 }
1735
1736 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectMetadata" ) );
1737
1738 if ( !element.isNull() )
1739 {
1740 mMetadata.readMetadataXml( element );
1741 }
1742 else
1743 {
1744 // older project, no metadata => remove auto generated metadata which is populated on QgsProject::clear()
1745 mMetadata = QgsProjectMetadata();
1746 }
1747 if ( mMetadata.title().isEmpty() && !oldTitle.isEmpty() )
1748 {
1749 // upgrade older title storage to storing within project metadata.
1750 mMetadata.setTitle( oldTitle );
1751 }
1752 emit metadataChanged();
1753
1754 // Transaction mode
1755 element = doc->documentElement().firstChildElement( QStringLiteral( "transaction" ) );
1756 if ( !element.isNull() )
1757 {
1758 mTransactionMode = qgsEnumKeyToValue( element.attribute( QStringLiteral( "mode" ) ), Qgis::TransactionMode::Disabled );
1759 }
1760 else
1761 {
1762 // maybe older project => try read autotransaction
1763 element = doc->documentElement().firstChildElement( QStringLiteral( "autotransaction" ) );
1764 if ( ! element.isNull() )
1765 {
1766 mTransactionMode = static_cast<Qgis::TransactionMode>( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() );
1767 }
1768 }
1769
1770 // read the layer tree from project file
1771 profile.switchTask( tr( "Loading layer tree" ) );
1772 mRootGroup->setCustomProperty( QStringLiteral( "loading" ), 1 );
1773
1774 QDomElement layerTreeElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
1775 if ( !layerTreeElem.isNull() )
1776 {
1777 // Use a temporary tree to read the nodes to prevent signals being delivered to the models
1778 QgsLayerTree tempTree;
1779 tempTree.readChildrenFromXml( layerTreeElem, context );
1780 mRootGroup->insertChildNodes( -1, tempTree.abandonChildren() );
1781 }
1782 else
1783 {
1784 QgsLayerTreeUtils::readOldLegend( mRootGroup, doc->documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
1785 }
1786
1787 mLayerTreeRegistryBridge->setEnabled( false );
1788
1789 // get the map layers
1790 profile.switchTask( tr( "Reading map layers" ) );
1791
1792 QList<QDomNode> brokenNodes;
1793 const bool clean = _getMapLayers( *doc, brokenNodes, flags );
1794
1795 // review the integrity of the retrieved map layers
1796 if ( !clean && !( flags & Qgis::ProjectReadFlag::DontResolveLayers ) )
1797 {
1798 QgsDebugMsg( QStringLiteral( "Unable to get map layers from project file." ) );
1799
1800 if ( !brokenNodes.isEmpty() )
1801 {
1802 QgsDebugMsg( "there are " + QString::number( brokenNodes.size() ) + " broken layers" );
1803 }
1804
1805 // we let a custom handler decide what to do with missing layers
1806 // (default implementation ignores them, there's also a GUI handler that lets user choose correct path)
1807 mBadLayerHandler->handleBadLayers( brokenNodes );
1808 }
1809
1810 mMainAnnotationLayer->readLayerXml( doc->documentElement().firstChildElement( QStringLiteral( "main-annotation-layer" ) ), context );
1811 mMainAnnotationLayer->setTransformContext( mTransformContext );
1812
1813 // load embedded groups and layers
1814 profile.switchTask( tr( "Loading embedded layers" ) );
1815 loadEmbeddedNodes( mRootGroup, flags );
1816
1817 // Resolve references to other layers
1818 // Needs to be done here once all dependent layers are loaded
1819 profile.switchTask( tr( "Resolving layer references" ) );
1820 QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
1821 for ( QMap<QString, QgsMapLayer *>::iterator it = layers.begin(); it != layers.end(); ++it )
1822 {
1823 it.value()->resolveReferences( this );
1824 }
1825
1826 mLayerTreeRegistryBridge->setEnabled( true );
1827
1828 // now that layers are loaded, we can resolve layer tree's references to the layers
1829 profile.switchTask( tr( "Resolving references" ) );
1830 mRootGroup->resolveReferences( this );
1831
1832 loadProjectFlags( doc.get() );
1833
1834 if ( !layerTreeElem.isNull() )
1835 {
1836 mRootGroup->readLayerOrderFromXml( layerTreeElem );
1837 }
1838
1839 // Load pre 3.0 configuration
1840 const QDomElement layerTreeCanvasElem = doc->documentElement().firstChildElement( QStringLiteral( "layer-tree-canvas" ) );
1841 if ( !layerTreeCanvasElem.isNull( ) )
1842 {
1843 mRootGroup->readLayerOrderFromXml( layerTreeCanvasElem );
1844 }
1845
1846 // Convert pre 3.4 to create layers flags
1847 if ( QgsProjectVersion( 3, 4, 0 ) > mSaveVersion )
1848 {
1849 const QStringList requiredLayerIds = readListEntry( QStringLiteral( "RequiredLayers" ), QStringLiteral( "Layers" ) );
1850 for ( const QString &layerId : requiredLayerIds )
1851 {
1852 if ( QgsMapLayer *layer = mapLayer( layerId ) )
1853 {
1854 layer->setFlags( layer->flags() & ~QgsMapLayer::Removable );
1855 }
1856 }
1857 const QStringList disabledLayerIds = readListEntry( QStringLiteral( "Identify" ), QStringLiteral( "/disabledLayers" ) );
1858 for ( const QString &layerId : disabledLayerIds )
1859 {
1860 if ( QgsMapLayer *layer = mapLayer( layerId ) )
1861 {
1862 layer->setFlags( layer->flags() & ~QgsMapLayer::Identifiable );
1863 }
1864 }
1865 }
1866
1867 // Convert pre 3.26 default styles
1868 if ( QgsProjectVersion( 3, 26, 0 ) > mSaveVersion )
1869 {
1870 // Convert default symbols
1871 QString styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
1872 if ( !styleName.isEmpty() )
1873 {
1874 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1876 }
1877 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
1878 if ( !styleName.isEmpty() )
1879 {
1880 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1882 }
1883 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
1884 if ( !styleName.isEmpty() )
1885 {
1886 std::unique_ptr<QgsSymbol> symbol( QgsStyle::defaultStyle()->symbol( styleName ) );
1888 }
1889 styleName = readEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
1890 if ( !styleName.isEmpty() )
1891 {
1892 std::unique_ptr<QgsColorRamp> colorRamp( QgsStyle::defaultStyle()->colorRamp( styleName ) );
1893 styleSettings()->setDefaultColorRamp( colorRamp.get() );
1894 }
1895
1896 // Convert randomize default symbol fill color
1897 styleSettings()->setRandomizeDefaultSymbolColor( readBoolEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ), true ) );
1898
1899 // Convert default symbol opacity
1900 double opacity = 1.0;
1901 bool ok = false;
1902 // upgrade old setting
1903 double alpha = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ), 255, &ok );
1904 if ( ok )
1905 opacity = alpha / 255.0;
1906 double newOpacity = readDoubleEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ), 1.0, &ok );
1907 if ( ok )
1908 opacity = newOpacity;
1910
1911 // Cleanup
1912 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Marker" ) );
1913 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Line" ) );
1914 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Fill" ) );
1915 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/ColorRamp" ) );
1916 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/RandomColors" ) );
1917 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/AlphaInt" ) );
1918 removeEntry( QStringLiteral( "DefaultStyles" ), QStringLiteral( "/Opacity" ) );
1919 }
1920
1921 // After bad layer handling we might still have invalid layers,
1922 // store them in case the user wanted to handle them later
1923 // or wanted to pass them through when saving
1924 if ( !( flags & Qgis::ProjectReadFlag::DontStoreOriginalStyles ) )
1925 {
1926 profile.switchTask( tr( "Storing original layer properties" ) );
1927 QgsLayerTreeUtils::storeOriginalLayersProperties( mRootGroup, doc.get() );
1928 }
1929
1930 mRootGroup->removeCustomProperty( QStringLiteral( "loading" ) );
1931
1932 profile.switchTask( tr( "Loading map themes" ) );
1933 mMapThemeCollection.reset( new QgsMapThemeCollection( this ) );
1935 mMapThemeCollection->readXml( *doc );
1936
1937 profile.switchTask( tr( "Loading label settings" ) );
1938 mLabelingEngineSettings->readSettingsFromProject( this );
1940
1941 profile.switchTask( tr( "Loading annotations" ) );
1942 mAnnotationManager->readXml( doc->documentElement(), context );
1943 if ( !( flags & Qgis::ProjectReadFlag::DontLoadLayouts ) )
1944 {
1945 profile.switchTask( tr( "Loading layouts" ) );
1946 mLayoutManager->readXml( doc->documentElement(), *doc );
1947 }
1948
1949 if ( !( flags & Qgis::ProjectReadFlag::DontLoad3DViews ) )
1950 {
1951 profile.switchTask( tr( "Loading 3D Views" ) );
1952 m3DViewsManager->readXml( doc->documentElement(), *doc );
1953 }
1954
1955 profile.switchTask( tr( "Loading bookmarks" ) );
1956 mBookmarkManager->readXml( doc->documentElement(), *doc );
1957
1958 // reassign change dependencies now that all layers are loaded
1959 QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
1960 for ( QMap<QString, QgsMapLayer *>::iterator it = existingMaps.begin(); it != existingMaps.end(); ++it )
1961 {
1962 it.value()->setDependencies( it.value()->dependencies() );
1963 }
1964
1965 profile.switchTask( tr( "Loading snapping settings" ) );
1966 mSnappingConfig.readProject( *doc );
1967 mAvoidIntersectionsMode = static_cast<Qgis::AvoidIntersectionsMode>( readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( Qgis::AvoidIntersectionsMode::AvoidIntersectionsLayers ) ) );
1968
1969 profile.switchTask( tr( "Loading view settings" ) );
1970 // restore older project scales settings
1971 mViewSettings->setUseProjectScales( readBoolEntry( QStringLiteral( "Scales" ), QStringLiteral( "/useProjectScales" ) ) );
1972 const QStringList scales = readListEntry( QStringLiteral( "Scales" ), QStringLiteral( "/ScalesList" ) );
1973 QVector<double> res;
1974 for ( const QString &scale : scales )
1975 {
1976 const QStringList parts = scale.split( ':' );
1977 if ( parts.size() != 2 )
1978 continue;
1979
1980 bool ok = false;
1981 const double denominator = QLocale().toDouble( parts[1], &ok );
1982 if ( ok )
1983 {
1984 res << denominator;
1985 }
1986 }
1987 mViewSettings->setMapScales( res );
1988 const QDomElement viewSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectViewSettings" ) );
1989 if ( !viewSettingsElement.isNull() )
1990 mViewSettings->readXml( viewSettingsElement, context );
1991
1992 // restore style settings
1993 profile.switchTask( tr( "Loading style properties" ) );
1994 const QDomElement styleSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectStyleSettings" ) );
1995 if ( !styleSettingsElement.isNull() )
1996 mStyleSettings->readXml( styleSettingsElement, context, flags );
1997
1998 // restore time settings
1999 profile.switchTask( tr( "Loading temporal settings" ) );
2000 const QDomElement timeSettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectTimeSettings" ) );
2001 if ( !timeSettingsElement.isNull() )
2002 mTimeSettings->readXml( timeSettingsElement, context );
2003
2004
2005 profile.switchTask( tr( "Loading elevation properties" ) );
2006 const QDomElement elevationPropertiesElement = doc->documentElement().firstChildElement( QStringLiteral( "ElevationProperties" ) );
2007 if ( !elevationPropertiesElement.isNull() )
2008 mElevationProperties->readXml( elevationPropertiesElement, context );
2009 mElevationProperties->resolveReferences( this );
2010
2011 profile.switchTask( tr( "Loading display settings" ) );
2012 const QDomElement displaySettingsElement = doc->documentElement().firstChildElement( QStringLiteral( "ProjectDisplaySettings" ) );
2013 if ( !displaySettingsElement.isNull() )
2014 mDisplaySettings->readXml( displaySettingsElement, context );
2015
2016 profile.switchTask( tr( "Updating variables" ) );
2018 profile.switchTask( tr( "Updating CRS" ) );
2019 emit crsChanged();
2020 emit ellipsoidChanged( ellipsoid() );
2021
2022 // read the project: used by map canvas and legend
2023 profile.switchTask( tr( "Reading external settings" ) );
2024 emit readProject( *doc );
2025 emit readProjectWithContext( *doc, context );
2026
2027 profile.switchTask( tr( "Updating interface" ) );
2028
2029 snapSignalBlock.release();
2030 if ( !mBlockSnappingUpdates )
2031 emit snappingConfigChanged( mSnappingConfig );
2032
2035 emit projectColorsChanged();
2036
2037 // if all went well, we're allegedly in pristine state
2038 if ( clean )
2039 setDirty( false );
2040
2041 QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUser ), 2 );
2042 QgsDebugMsgLevel( QStringLiteral( "Project save user: %1" ).arg( mSaveUserFull ), 2 );
2043
2047
2048 if ( mTranslator )
2049 {
2050 //project possibly translated -> rename it with locale postfix
2051 const QString newFileName( QStringLiteral( "%1/%2.qgs" ).arg( QFileInfo( projectFile.fileName() ).absolutePath(), localeFileName ) );
2052 setFileName( newFileName );
2053
2054 if ( write() )
2055 {
2056 setTitle( localeFileName );
2057 QgsMessageLog::logMessage( tr( "Translated project saved with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Success );
2058 }
2059 else
2060 {
2061 QgsMessageLog::logMessage( tr( "Error saving translated project with locale prefix %1" ).arg( newFileName ), QObject::tr( "Project translation" ), Qgis::MessageLevel::Critical );
2062 }
2063 }
2064
2065 // lastly, make any previously editable layers editable
2066 const QMap<QString, QgsMapLayer *> loadedLayers = mapLayers();
2067 for ( auto it = loadedLayers.constBegin(); it != loadedLayers.constEnd(); ++it )
2068 {
2069 if ( it.value()->isValid() && it.value()->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2070 {
2071 if ( QgsVectorLayer *vl = qobject_cast< QgsVectorLayer * >( it.value() ) )
2072 vl->startEditing();
2073 it.value()->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2074 }
2075 }
2076
2077 return true;
2078}
2079
2080
2081bool QgsProject::loadEmbeddedNodes( QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
2082{
2083 bool valid = true;
2084 const auto constChildren = group->children();
2085 for ( QgsLayerTreeNode *child : constChildren )
2086 {
2087 if ( QgsLayerTree::isGroup( child ) )
2088 {
2089 QgsLayerTreeGroup *childGroup = QgsLayerTree::toGroup( child );
2090 if ( childGroup->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2091 {
2092 // make sure to convert the path from relative to absolute
2093 const QString projectPath = readPath( childGroup->customProperty( QStringLiteral( "embedded_project" ) ).toString() );
2094 childGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectPath );
2095 QgsLayerTreeGroup *newGroup = createEmbeddedGroup( childGroup->name(), projectPath, childGroup->customProperty( QStringLiteral( "embedded-invisible-layers" ) ).toStringList(), flags );
2096 if ( newGroup )
2097 {
2098 QList<QgsLayerTreeNode *> clonedChildren;
2099 const QList<QgsLayerTreeNode *> constChildren = newGroup->children();
2100 clonedChildren.reserve( constChildren.size() );
2101 for ( QgsLayerTreeNode *newGroupChild : constChildren )
2102 clonedChildren << newGroupChild->clone();
2103 delete newGroup;
2104
2105 childGroup->insertChildNodes( 0, clonedChildren );
2106 }
2107 }
2108 else
2109 {
2110 loadEmbeddedNodes( childGroup, flags );
2111 }
2112 }
2113 else if ( QgsLayerTree::isLayer( child ) )
2114 {
2115 if ( child->customProperty( QStringLiteral( "embedded" ) ).toInt() )
2116 {
2117 QList<QDomNode> brokenNodes;
2118 if ( ! createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), readPath( child->customProperty( QStringLiteral( "embedded_project" ) ).toString() ), brokenNodes, true, flags ) )
2119 {
2120 valid = valid && false;
2121 }
2122 }
2123 }
2124
2125 }
2126
2127 return valid;
2128}
2129
2131{
2132 return mCustomVariables;
2133}
2134
2135void QgsProject::setCustomVariables( const QVariantMap &variables )
2136{
2137 if ( variables == mCustomVariables )
2138 return;
2139
2140 //write variable to project
2141 QStringList variableNames;
2142 QStringList variableValues;
2143
2144 QVariantMap::const_iterator it = variables.constBegin();
2145 for ( ; it != variables.constEnd(); ++it )
2146 {
2147 variableNames << it.key();
2148 variableValues << it.value().toString();
2149 }
2150
2151 writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableNames" ), variableNames );
2152 writeEntry( QStringLiteral( "Variables" ), QStringLiteral( "/variableValues" ), variableValues );
2153
2154 mCustomVariables = variables;
2155 mProjectScope.reset();
2156
2158}
2159
2161{
2162 *mLabelingEngineSettings = settings;
2164}
2165
2167{
2168 return *mLabelingEngineSettings;
2169}
2170
2172{
2173 mProjectScope.reset();
2174 return mLayerStore.get();
2175}
2176
2178{
2179 return mLayerStore.get();
2180}
2181
2182QList<QgsVectorLayer *> QgsProject::avoidIntersectionsLayers() const
2183{
2184 QList<QgsVectorLayer *> layers;
2185 const QStringList layerIds = readListEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), QStringList() );
2186 const auto constLayerIds = layerIds;
2187 for ( const QString &layerId : constLayerIds )
2188 {
2189 if ( QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( mapLayer( layerId ) ) )
2190 layers << vlayer;
2191 }
2192 return layers;
2193}
2194
2195void QgsProject::setAvoidIntersectionsLayers( const QList<QgsVectorLayer *> &layers )
2196{
2197 QStringList list;
2198 list.reserve( layers.size() );
2199 for ( QgsVectorLayer *layer : layers )
2200 list << layer->id();
2201 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsList" ), list );
2203}
2204
2206{
2207 QgsExpressionContext context;
2208
2211
2212 return context;
2213}
2214
2216{
2217 // MUCH cheaper to clone than build
2218 if ( mProjectScope )
2219 {
2220 std::unique_ptr< QgsExpressionContextScope > projectScope = std::make_unique< QgsExpressionContextScope >( *mProjectScope );
2221 // we can't cache these
2222 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_distance_units" ), QgsUnitTypes::toString( distanceUnits() ), true, true ) );
2223 projectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_area_units" ), QgsUnitTypes::toString( areaUnits() ), true, true ) );
2224 return projectScope.release();
2225 }
2226
2227 mProjectScope = std::make_unique< QgsExpressionContextScope >( QObject::tr( "Project" ) );
2228
2229 const QVariantMap vars = customVariables();
2230
2231 QVariantMap::const_iterator it = vars.constBegin();
2232
2233 for ( ; it != vars.constEnd(); ++it )
2234 {
2235 mProjectScope->setVariable( it.key(), it.value(), true );
2236 }
2237
2238 QString projectPath = projectStorage() ? fileName() : absoluteFilePath();
2239 if ( projectPath.isEmpty() )
2240 projectPath = mOriginalPath;
2241 const QString projectFolder = QFileInfo( projectPath ).path();
2242 const QString projectFilename = QFileInfo( projectPath ).fileName();
2243 const QString projectBasename = baseName();
2244
2245 //add other known project variables
2246 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_title" ), title(), true, true ) );
2247 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_path" ), QDir::toNativeSeparators( projectPath ), true, true ) );
2248 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_folder" ), QDir::toNativeSeparators( projectFolder ), true, true ) );
2249 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_filename" ), projectFilename, true, true ) );
2250 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_basename" ), projectBasename, true, true ) );
2251 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_home" ), QDir::toNativeSeparators( homePath() ), true, true ) );
2252 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_last_saved" ), mSaveDateTime.isNull() ? QVariant() : QVariant( mSaveDateTime ), true, true ) );
2253 const QgsCoordinateReferenceSystem projectCrs = crs();
2254 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs" ), projectCrs.authid(), true, true ) );
2255 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_definition" ), projectCrs.toProj(), true, true ) );
2256 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_description" ), projectCrs.description(), true, true ) );
2257 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_ellipsoid" ), ellipsoid(), true, true ) );
2258 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "_project_transform_context" ), QVariant::fromValue<QgsCoordinateTransformContext>( transformContext() ), true, true ) );
2259 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_units" ), QgsUnitTypes::toString( projectCrs.mapUnits() ), true ) );
2260 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_acronym" ), projectCrs.projectionAcronym(), true ) );
2261 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_ellipsoid" ), projectCrs.ellipsoidAcronym(), true ) );
2262 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_proj4" ), projectCrs.toProj(), true ) );
2263 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_crs_wkt" ), projectCrs.toWkt( QgsCoordinateReferenceSystem::WKT_PREFERRED ), true ) );
2264
2265 // metadata
2266 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_author" ), metadata().author(), true, true ) );
2267 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_abstract" ), metadata().abstract(), true, true ) );
2268 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_creation_date" ), metadata().creationDateTime(), true, true ) );
2269 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_identifier" ), metadata().identifier(), true, true ) );
2270
2271 // keywords
2272 QVariantMap keywords;
2273 const QgsAbstractMetadataBase::KeywordMap metadataKeywords = metadata().keywords();
2274 for ( auto it = metadataKeywords.constBegin(); it != metadataKeywords.constEnd(); ++it )
2275 {
2276 keywords.insert( it.key(), it.value() );
2277 }
2278 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "project_keywords" ), keywords, true, true ) );
2279
2280 // layers
2281 QVariantList layersIds;
2282 QVariantList layers;
2283 const QMap<QString, QgsMapLayer *> layersInProject = mLayerStore->mapLayers();
2284 layersIds.reserve( layersInProject.count() );
2285 layers.reserve( layersInProject.count() );
2286 for ( auto it = layersInProject.constBegin(); it != layersInProject.constEnd(); ++it )
2287 {
2288 layersIds << it.value()->id();
2289 layers << QVariant::fromValue<QgsWeakMapLayerPointer>( QgsWeakMapLayerPointer( it.value() ) );
2290 }
2291 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layer_ids" ), layersIds, true ) );
2292 mProjectScope->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "layers" ), layers, true ) );
2293
2294 mProjectScope->addFunction( QStringLiteral( "project_color" ), new GetNamedProjectColor( this ) );
2295
2297}
2298
2299void QgsProject::onMapLayersAdded( const QList<QgsMapLayer *> &layers )
2300{
2301 const QMap<QString, QgsMapLayer *> existingMaps = mapLayers();
2302
2303 const auto constLayers = layers;
2304 for ( QgsMapLayer *layer : constLayers )
2305 {
2306 if ( ! layer->isValid() )
2307 return;
2308
2309 connect( layer, &QgsMapLayer::configChanged, this, [ = ] { setDirty(); } );
2310
2311 // check if we have to update connections for layers with dependencies
2312 for ( QMap<QString, QgsMapLayer *>::const_iterator it = existingMaps.cbegin(); it != existingMaps.cend(); ++it )
2313 {
2314 const QSet<QgsMapLayerDependency> deps = it.value()->dependencies();
2315 if ( deps.contains( layer->id() ) )
2316 {
2317 // reconnect to change signals
2318 it.value()->setDependencies( deps );
2319 }
2320 }
2321 }
2322
2323 updateTransactionGroups();
2324
2325 if ( !mBlockSnappingUpdates && mSnappingConfig.addLayers( layers ) )
2326 emit snappingConfigChanged( mSnappingConfig );
2327}
2328
2329void QgsProject::onMapLayersRemoved( const QList<QgsMapLayer *> &layers )
2330{
2331 if ( !mBlockSnappingUpdates && mSnappingConfig.removeLayers( layers ) )
2332 emit snappingConfigChanged( mSnappingConfig );
2333}
2334
2335void QgsProject::cleanTransactionGroups( bool force )
2336{
2337 bool changed = false;
2338 for ( QMap< QPair< QString, QString>, QgsTransactionGroup *>::Iterator tg = mTransactionGroups.begin(); tg != mTransactionGroups.end(); )
2339 {
2340 if ( tg.value()->isEmpty() || force )
2341 {
2342 delete tg.value();
2343 tg = mTransactionGroups.erase( tg );
2344 changed = true;
2345 }
2346 else
2347 {
2348 ++tg;
2349 }
2350 }
2351 if ( changed )
2353}
2354
2355void QgsProject::updateTransactionGroups()
2356{
2357 mEditBufferGroup.clear();
2358
2359 switch ( mTransactionMode )
2360 {
2362 {
2363 cleanTransactionGroups( true );
2364 return;
2365 }
2366 break;
2368 cleanTransactionGroups( true );
2369 break;
2371 cleanTransactionGroups( false );
2372 break;
2373 }
2374
2375 bool tgChanged = false;
2376 const auto constLayers = mapLayers().values();
2377 for ( QgsMapLayer *layer : constLayers )
2378 {
2379 if ( ! layer->isValid() )
2380 continue;
2381
2382 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
2383 if ( ! vlayer )
2384 continue;
2385
2386 switch ( mTransactionMode )
2387 {
2389 Q_ASSERT( false );
2390 break;
2392 {
2394 {
2395 const QString connString = QgsTransaction::connectionString( vlayer->source() );
2396 const QString key = vlayer->providerType();
2397
2398 QgsTransactionGroup *tg = mTransactionGroups.value( qMakePair( key, connString ) );
2399
2400 if ( !tg )
2401 {
2402 tg = new QgsTransactionGroup();
2403 mTransactionGroups.insert( qMakePair( key, connString ), tg );
2404 tgChanged = true;
2405 }
2406 tg->addLayer( vlayer );
2407 }
2408 }
2409 break;
2411 {
2412 if ( vlayer->supportsEditing() )
2413 mEditBufferGroup.addLayer( vlayer );
2414 }
2415 break;
2416 }
2417 }
2418
2419 if ( tgChanged )
2421}
2422
2423bool QgsProject::readLayer( const QDomNode &layerNode )
2424{
2425 QgsReadWriteContext context;
2426 context.setPathResolver( pathResolver() );
2427 context.setProjectTranslator( this );
2429 QList<QDomNode> brokenNodes;
2430 if ( addLayer( layerNode.toElement(), brokenNodes, context ) )
2431 {
2432 // have to try to update joins for all layers now - a previously added layer may be dependent on this newly
2433 // added layer for joins
2434 const QVector<QgsVectorLayer *> vectorLayers = layers<QgsVectorLayer *>();
2435 for ( QgsVectorLayer *layer : vectorLayers )
2436 {
2437 // TODO: should be only done later - and with all layers (other layers may have referenced this layer)
2438 layer->resolveReferences( this );
2439
2440 if ( layer->isValid() && layer->customProperty( QStringLiteral( "_layer_was_editable" ) ).toBool() )
2441 {
2442 layer->startEditing();
2443 layer->removeCustomProperty( QStringLiteral( "_layer_was_editable" ) );
2444 }
2445 }
2446 return true;
2447 }
2448 return false;
2449}
2450
2451bool QgsProject::write( const QString &filename )
2452{
2453 mFile.setFileName( filename );
2454 mCachedHomePath.clear();
2455 return write();
2456}
2457
2459{
2460 mProjectScope.reset();
2461 if ( QgsProjectStorage *storage = projectStorage() )
2462 {
2463 QgsReadWriteContext context;
2464 // for projects stored in a custom storage, we have to check for the support
2465 // of relative paths since the storage most likely will not be in a file system
2466 const QString storageFilePath { storage->filePath( mFile.fileName() ) };
2467 if ( storageFilePath.isEmpty() )
2468 {
2470 }
2471 context.setPathResolver( pathResolver() );
2472
2473 const QString tempPath = QStandardPaths::standardLocations( QStandardPaths::TempLocation ).at( 0 );
2474 const QString tmpZipFilename( tempPath + QDir::separator() + QUuid::createUuid().toString() );
2475
2476 if ( !zip( tmpZipFilename ) )
2477 return false; // zip() already calls setError() when returning false
2478
2479 QFile tmpZipFile( tmpZipFilename );
2480 if ( !tmpZipFile.open( QIODevice::ReadOnly ) )
2481 {
2482 setError( tr( "Unable to read file %1" ).arg( tmpZipFilename ) );
2483 return false;
2484 }
2485
2487 if ( !storage->writeProject( mFile.fileName(), &tmpZipFile, context ) )
2488 {
2489 QString err = tr( "Unable to save project to storage %1" ).arg( mFile.fileName() );
2490 QList<QgsReadWriteContext::ReadWriteMessage> messages = context.takeMessages();
2491 if ( !messages.isEmpty() )
2492 err += QStringLiteral( "\n\n" ) + messages.last().message();
2493 setError( err );
2494 return false;
2495 }
2496
2497 tmpZipFile.close();
2498 QFile::remove( tmpZipFilename );
2499
2500 return true;
2501 }
2502
2503 if ( QgsZipUtils::isZipFile( mFile.fileName() ) )
2504 {
2505 return zip( mFile.fileName() );
2506 }
2507 else
2508 {
2509 // write project file even if the auxiliary storage is not correctly
2510 // saved
2511 const bool asOk = saveAuxiliaryStorage();
2512 const bool writeOk = writeProjectFile( mFile.fileName() );
2513 bool attachmentsOk = true;
2514 if ( !mArchive->files().isEmpty() )
2515 {
2516 const QFileInfo finfo( mFile.fileName() );
2517 const QString attachmentsZip = finfo.absoluteDir().absoluteFilePath( QStringLiteral( "%1_attachments.zip" ).arg( finfo.completeBaseName() ) );
2518 attachmentsOk = mArchive->zip( attachmentsZip );
2519 }
2520
2521 // errors raised during writing project file are more important
2522 if ( ( !asOk || !attachmentsOk ) && writeOk )
2523 {
2524 QStringList errorMessage;
2525 if ( !asOk )
2526 {
2527 const QString err = mAuxiliaryStorage->errorString();
2528 errorMessage.append( tr( "Unable to save auxiliary storage ('%1')" ).arg( err ) );
2529 }
2530 if ( !attachmentsOk )
2531 {
2532 errorMessage.append( tr( "Unable to save attachments archive" ) );
2533 }
2534 setError( errorMessage.join( '\n' ) );
2535 }
2536
2537 return asOk && writeOk && attachmentsOk;
2538 }
2539}
2540
2541bool QgsProject::writeProjectFile( const QString &filename )
2542{
2543 QFile projectFile( filename );
2544 clearError();
2545
2546 // if we have problems creating or otherwise writing to the project file,
2547 // let's find out up front before we go through all the hand-waving
2548 // necessary to create all the Dom objects
2549 const QFileInfo myFileInfo( projectFile );
2550 if ( myFileInfo.exists() && !myFileInfo.isWritable() )
2551 {
2552 setError( tr( "%1 is not writable. Please adjust permissions (if possible) and try again." )
2553 .arg( projectFile.fileName() ) );
2554 return false;
2555 }
2556
2557 QgsReadWriteContext context;
2558 context.setPathResolver( pathResolver() );
2560
2561 QDomImplementation::setInvalidDataPolicy( QDomImplementation::DropInvalidChars );
2562
2563 const QDomDocumentType documentType =
2564 QDomImplementation().createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ),
2565 QStringLiteral( "SYSTEM" ) );
2566 std::unique_ptr<QDomDocument> doc( new QDomDocument( documentType ) );
2567
2568 QDomElement qgisNode = doc->createElement( QStringLiteral( "qgis" ) );
2569 qgisNode.setAttribute( QStringLiteral( "projectname" ), title() );
2570 qgisNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
2571
2572 if ( !mSettings.value( QStringLiteral( "projects/anonymize_saved_projects" ), false, QgsSettings::Core ).toBool() )
2573 {
2574 const QString newSaveUser = QgsApplication::userLoginName();
2575 const QString newSaveUserFull = QgsApplication::userFullName();
2576 qgisNode.setAttribute( QStringLiteral( "saveUser" ), newSaveUser );
2577 qgisNode.setAttribute( QStringLiteral( "saveUserFull" ), newSaveUserFull );
2578 mSaveUser = newSaveUser;
2579 mSaveUserFull = newSaveUserFull;
2580 mSaveDateTime = QDateTime::currentDateTime();
2581 qgisNode.setAttribute( QStringLiteral( "saveDateTime" ), mSaveDateTime.toString( Qt::ISODate ) );
2582 }
2583 else
2584 {
2585 mSaveUser.clear();
2586 mSaveUserFull.clear();
2587 mSaveDateTime = QDateTime();
2588 }
2589 doc->appendChild( qgisNode );
2590 mSaveVersion = QgsProjectVersion( Qgis::version() );
2591
2592 QDomElement homePathNode = doc->createElement( QStringLiteral( "homePath" ) );
2593 homePathNode.setAttribute( QStringLiteral( "path" ), mHomePath );
2594 qgisNode.appendChild( homePathNode );
2595
2596 // title
2597 QDomElement titleNode = doc->createElement( QStringLiteral( "title" ) );
2598 qgisNode.appendChild( titleNode );
2599
2600 QDomElement transactionNode = doc->createElement( QStringLiteral( "transaction" ) );
2601 transactionNode.setAttribute( QStringLiteral( "mode" ), qgsEnumValueToKey( mTransactionMode ) );
2602 qgisNode.appendChild( transactionNode );
2603
2604 QDomElement flagsNode = doc->createElement( QStringLiteral( "projectFlags" ) );
2605 flagsNode.setAttribute( QStringLiteral( "set" ), qgsFlagValueToKeys( mFlags ) );
2606 qgisNode.appendChild( flagsNode );
2607
2608 const QDomText titleText = doc->createTextNode( title() ); // XXX why have title TWICE?
2609 titleNode.appendChild( titleText );
2610
2611 // write project CRS
2612 QDomElement srsNode = doc->createElement( QStringLiteral( "projectCrs" ) );
2613 mCrs.writeXml( srsNode, *doc );
2614 qgisNode.appendChild( srsNode );
2615
2616 // write layer tree - make sure it is without embedded subgroups
2617 QgsLayerTreeNode *clonedRoot = mRootGroup->clone();
2619 QgsLayerTreeUtils::updateEmbeddedGroupsProjectPath( QgsLayerTree::toGroup( clonedRoot ), this ); // convert absolute paths to relative paths if required
2620
2621 clonedRoot->writeXml( qgisNode, context );
2622 delete clonedRoot;
2623
2624 mSnappingConfig.writeProject( *doc );
2625 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/AvoidIntersectionsMode" ), static_cast<int>( mAvoidIntersectionsMode ) );
2626
2627 // let map canvas and legend write their information
2628 emit writeProject( *doc );
2629
2630 // within top level node save list of layers
2631 const QMap<QString, QgsMapLayer *> layers = mapLayers();
2632
2633 QDomElement annotationLayerNode = doc->createElement( QStringLiteral( "main-annotation-layer" ) );
2634 mMainAnnotationLayer->writeLayerXml( annotationLayerNode, *doc, context );
2635 qgisNode.appendChild( annotationLayerNode );
2636
2637 // Iterate over layers in zOrder
2638 // Call writeXml() on each
2639 QDomElement projectLayersNode = doc->createElement( QStringLiteral( "projectlayers" ) );
2640
2641 QMap<QString, QgsMapLayer *>::ConstIterator li = layers.constBegin();
2642 while ( li != layers.end() )
2643 {
2644 QgsMapLayer *ml = li.value();
2645
2646 if ( ml )
2647 {
2648 const QHash< QString, QPair< QString, bool> >::const_iterator emIt = mEmbeddedLayers.constFind( ml->id() );
2649 if ( emIt == mEmbeddedLayers.constEnd() )
2650 {
2651 QDomElement maplayerElem;
2652 // If layer is not valid, prefer to restore saved properties from invalidLayerProperties. But if that's
2653 // not available, just write what we DO have
2654 if ( ml->isValid() || ml->originalXmlProperties().isEmpty() )
2655 {
2656 // general layer metadata
2657 maplayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2658 ml->writeLayerXml( maplayerElem, *doc, context );
2659
2661 maplayerElem.setAttribute( QStringLiteral( "editable" ), QStringLiteral( "1" ) );
2662 }
2663 else if ( ! ml->originalXmlProperties().isEmpty() )
2664 {
2665 QDomDocument document;
2666 if ( document.setContent( ml->originalXmlProperties() ) )
2667 {
2668 maplayerElem = document.firstChildElement();
2669 }
2670 else
2671 {
2672 QgsDebugMsg( QStringLiteral( "Could not restore layer properties for layer %1" ).arg( ml->id() ) );
2673 }
2674 }
2675
2676 emit writeMapLayer( ml, maplayerElem, *doc );
2677
2678 projectLayersNode.appendChild( maplayerElem );
2679 }
2680 else
2681 {
2682 // layer defined in an external project file
2683 // only save embedded layer if not managed by a legend group
2684 if ( emIt.value().second )
2685 {
2686 QDomElement mapLayerElem = doc->createElement( QStringLiteral( "maplayer" ) );
2687 mapLayerElem.setAttribute( QStringLiteral( "embedded" ), 1 );
2688 mapLayerElem.setAttribute( QStringLiteral( "project" ), writePath( emIt.value().first ) );
2689 mapLayerElem.setAttribute( QStringLiteral( "id" ), ml->id() );
2690 projectLayersNode.appendChild( mapLayerElem );
2691 }
2692 }
2693 }
2694 li++;
2695 }
2696
2697 qgisNode.appendChild( projectLayersNode );
2698
2699 QDomElement layerOrderNode = doc->createElement( QStringLiteral( "layerorder" ) );
2700 const auto constCustomLayerOrder = mRootGroup->customLayerOrder();
2701 for ( QgsMapLayer *layer : constCustomLayerOrder )
2702 {
2703 QDomElement mapLayerElem = doc->createElement( QStringLiteral( "layer" ) );
2704 mapLayerElem.setAttribute( QStringLiteral( "id" ), layer->id() );
2705 layerOrderNode.appendChild( mapLayerElem );
2706 }
2707 qgisNode.appendChild( layerOrderNode );
2708
2709 mLabelingEngineSettings->writeSettingsToProject( this );
2710
2711 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorRedPart" ), mBackgroundColor.red() );
2712 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorGreenPart" ), mBackgroundColor.green() );
2713 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/CanvasColorBluePart" ), mBackgroundColor.blue() );
2714
2715 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorRedPart" ), mSelectionColor.red() );
2716 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorGreenPart" ), mSelectionColor.green() );
2717 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorBluePart" ), mSelectionColor.blue() );
2718 writeEntry( QStringLiteral( "Gui" ), QStringLiteral( "/SelectionColorAlphaPart" ), mSelectionColor.alpha() );
2719
2720 writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/DistanceUnits" ), QgsUnitTypes::encodeUnit( mDistanceUnits ) );
2721 writeEntry( QStringLiteral( "Measurement" ), QStringLiteral( "/AreaUnits" ), QgsUnitTypes::encodeUnit( mAreaUnits ) );
2722
2723 // now add the optional extra properties
2724#if 0
2725 dump_( mProperties );
2726#endif
2727
2728 QgsDebugMsgLevel( QStringLiteral( "there are %1 property scopes" ).arg( static_cast<int>( mProperties.count() ) ), 2 );
2729
2730 if ( !mProperties.isEmpty() ) // only worry about properties if we
2731 // actually have any properties
2732 {
2733 mProperties.writeXml( QStringLiteral( "properties" ), qgisNode, *doc );
2734 }
2735
2736 QDomElement ddElem = doc->createElement( QStringLiteral( "dataDefinedServerProperties" ) );
2737 mDataDefinedServerProperties.writeXml( ddElem, dataDefinedServerPropertyDefinitions() );
2738 qgisNode.appendChild( ddElem );
2739
2740 mMapThemeCollection->writeXml( *doc );
2741
2742 mTransformContext.writeXml( qgisNode, context );
2743
2744 QDomElement metadataElem = doc->createElement( QStringLiteral( "projectMetadata" ) );
2745 mMetadata.writeMetadataXml( metadataElem, *doc );
2746 qgisNode.appendChild( metadataElem );
2747
2748 const QDomElement annotationsElem = mAnnotationManager->writeXml( *doc, context );
2749 qgisNode.appendChild( annotationsElem );
2750
2751 const QDomElement layoutElem = mLayoutManager->writeXml( *doc );
2752 qgisNode.appendChild( layoutElem );
2753
2754 const QDomElement views3DElem = m3DViewsManager->writeXml( *doc );
2755 qgisNode.appendChild( views3DElem );
2756
2757 const QDomElement bookmarkElem = mBookmarkManager->writeXml( *doc );
2758 qgisNode.appendChild( bookmarkElem );
2759
2760 const QDomElement viewSettingsElem = mViewSettings->writeXml( *doc, context );
2761 qgisNode.appendChild( viewSettingsElem );
2762
2763 const QDomElement styleSettingsElem = mStyleSettings->writeXml( *doc, context );
2764 qgisNode.appendChild( styleSettingsElem );
2765
2766 const QDomElement timeSettingsElement = mTimeSettings->writeXml( *doc, context );
2767 qgisNode.appendChild( timeSettingsElement );
2768
2769 const QDomElement elevationPropertiesElement = mElevationProperties->writeXml( *doc, context );
2770 qgisNode.appendChild( elevationPropertiesElement );
2771
2772 const QDomElement displaySettingsElem = mDisplaySettings->writeXml( *doc, context );
2773 qgisNode.appendChild( displaySettingsElem );
2774
2775 // now wrap it up and ship it to the project file
2776 doc->normalize(); // XXX I'm not entirely sure what this does
2777
2778 // Create backup file
2779 if ( QFile::exists( fileName() ) )
2780 {
2781 QFile backupFile( QStringLiteral( "%1~" ).arg( filename ) );
2782 bool ok = true;
2783 ok &= backupFile.open( QIODevice::WriteOnly | QIODevice::Truncate );
2784 ok &= projectFile.open( QIODevice::ReadOnly );
2785
2786 QByteArray ba;
2787 while ( ok && !projectFile.atEnd() )
2788 {
2789 ba = projectFile.read( 10240 );
2790 ok &= backupFile.write( ba ) == ba.size();
2791 }
2792
2793 projectFile.close();
2794 backupFile.close();
2795
2796 if ( !ok )
2797 {
2798 setError( tr( "Unable to create backup file %1" ).arg( backupFile.fileName() ) );
2799 return false;
2800 }
2801
2802 const QFileInfo fi( fileName() );
2803 struct utimbuf tb = { static_cast<time_t>( fi.lastRead().toSecsSinceEpoch() ), static_cast<time_t>( fi.lastModified().toSecsSinceEpoch() ) };
2804 utime( backupFile.fileName().toUtf8().constData(), &tb );
2805 }
2806
2807 if ( !projectFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
2808 {
2809 projectFile.close(); // even though we got an error, let's make
2810 // sure it's closed anyway
2811
2812 setError( tr( "Unable to save to file %1" ).arg( projectFile.fileName() ) );
2813 return false;
2814 }
2815
2816 QTemporaryFile tempFile;
2817 bool ok = tempFile.open();
2818 if ( ok )
2819 {
2820 QTextStream projectFileStream( &tempFile );
2821 doc->save( projectFileStream, 2 ); // save as utf-8
2822 ok &= projectFileStream.pos() > -1;
2823
2824 ok &= tempFile.seek( 0 );
2825
2826 QByteArray ba;
2827 while ( ok && !tempFile.atEnd() )
2828 {
2829 ba = tempFile.read( 10240 );
2830 ok &= projectFile.write( ba ) == ba.size();
2831 }
2832
2833 ok &= projectFile.error() == QFile::NoError;
2834
2835 projectFile.close();
2836 }
2837
2838 tempFile.close();
2839
2840 if ( !ok )
2841 {
2842 setError( tr( "Unable to save to file %1. Your project "
2843 "may be corrupted on disk. Try clearing some space on the volume and "
2844 "check file permissions before pressing save again." )
2845 .arg( projectFile.fileName() ) );
2846 return false;
2847 }
2848
2849 setDirty( false ); // reset to pristine state
2850
2851 emit projectSaved();
2852 return true;
2853}
2854
2855bool QgsProject::writeEntry( const QString &scope, QString const &key, bool value )
2856{
2857 bool propertiesModified;
2858 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2859
2860 if ( propertiesModified )
2861 setDirty( true );
2862
2863 return success;
2864}
2865
2866bool QgsProject::writeEntry( const QString &scope, const QString &key, double value )
2867{
2868 bool propertiesModified;
2869 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2870
2871 if ( propertiesModified )
2872 setDirty( true );
2873
2874 return success;
2875}
2876
2877bool QgsProject::writeEntry( const QString &scope, QString const &key, int value )
2878{
2879 bool propertiesModified;
2880 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2881
2882 if ( propertiesModified )
2883 setDirty( true );
2884
2885 return success;
2886}
2887
2888bool QgsProject::writeEntry( const QString &scope, const QString &key, const QString &value )
2889{
2890 bool propertiesModified;
2891 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2892
2893 if ( propertiesModified )
2894 setDirty( true );
2895
2896 return success;
2897}
2898
2899bool QgsProject::writeEntry( const QString &scope, const QString &key, const QStringList &value )
2900{
2901 bool propertiesModified;
2902 const bool success = addKey_( scope, key, &mProperties, value, propertiesModified );
2903
2904 if ( propertiesModified )
2905 setDirty( true );
2906
2907 return success;
2908}
2909
2910QStringList QgsProject::readListEntry( const QString &scope,
2911 const QString &key,
2912 const QStringList &def,
2913 bool *ok ) const
2914{
2915 QgsProjectProperty *property = findKey_( scope, key, mProperties );
2916
2917 QVariant value;
2918
2919 if ( property )
2920 {
2921 value = property->value();
2922
2923 const bool valid = QVariant::StringList == value.type();
2924 if ( ok )
2925 *ok = valid;
2926
2927 if ( valid )
2928 {
2929 return value.toStringList();
2930 }
2931 }
2932 else if ( ok )
2933 *ok = false;
2934
2935
2936 return def;
2937}
2938
2939
2940QString QgsProject::readEntry( const QString &scope,
2941 const QString &key,
2942 const QString &def,
2943 bool *ok ) const
2944{
2945 QgsProjectProperty *property = findKey_( scope, key, mProperties );
2946
2947 QVariant value;
2948
2949 if ( property )
2950 {
2951 value = property->value();
2952
2953 const bool valid = value.canConvert( QVariant::String );
2954 if ( ok )
2955 *ok = valid;
2956
2957 if ( valid )
2958 return value.toString();
2959 }
2960 else if ( ok )
2961 *ok = false;
2962
2963 return def;
2964}
2965
2966int QgsProject::readNumEntry( const QString &scope, const QString &key, int def,
2967 bool *ok ) const
2968{
2969 QgsProjectProperty *property = findKey_( scope, key, mProperties );
2970
2971 QVariant value;
2972
2973 if ( property )
2974 {
2975 value = property->value();
2976 }
2977
2978 const bool valid = value.canConvert( QVariant::Int );
2979
2980 if ( ok )
2981 {
2982 *ok = valid;
2983 }
2984
2985 if ( valid )
2986 {
2987 return value.toInt();
2988 }
2989
2990 return def;
2991}
2992
2993double QgsProject::readDoubleEntry( const QString &scope, const QString &key,
2994 double def,
2995 bool *ok ) const
2996{
2997 QgsProjectProperty *property = findKey_( scope, key, mProperties );
2998 if ( property )
2999 {
3000 const QVariant value = property->value();
3001
3002 const bool valid = value.canConvert( QVariant::Double );
3003 if ( ok )
3004 *ok = valid;
3005
3006 if ( valid )
3007 return value.toDouble();
3008 }
3009 else if ( ok )
3010 *ok = false;
3011
3012 return def;
3013}
3014
3015bool QgsProject::readBoolEntry( const QString &scope, const QString &key, bool def,
3016 bool *ok ) const
3017{
3018 QgsProjectProperty *property = findKey_( scope, key, mProperties );
3019
3020 if ( property )
3021 {
3022 const QVariant value = property->value();
3023
3024 const bool valid = value.canConvert( QVariant::Bool );
3025 if ( ok )
3026 *ok = valid;
3027
3028 if ( valid )
3029 return value.toBool();
3030 }
3031 else if ( ok )
3032 *ok = false;
3033
3034 return def;
3035}
3036
3037bool QgsProject::removeEntry( const QString &scope, const QString &key )
3038{
3039 if ( findKey_( scope, key, mProperties ) )
3040 {
3041 removeKey_( scope, key, mProperties );
3042 setDirty( true );
3043 }
3044
3045 return !findKey_( scope, key, mProperties );
3046}
3047
3048
3049QStringList QgsProject::entryList( const QString &scope, const QString &key ) const
3050{
3051 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3052
3053 QStringList entries;
3054
3055 if ( foundProperty )
3056 {
3057 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3058
3059 if ( propertyKey )
3060 { propertyKey->entryList( entries ); }
3061 }
3062
3063 return entries;
3064}
3065
3066QStringList QgsProject::subkeyList( const QString &scope, const QString &key ) const
3067{
3068 QgsProjectProperty *foundProperty = findKey_( scope, key, mProperties );
3069
3070 QStringList entries;
3071
3072 if ( foundProperty )
3073 {
3074 QgsProjectPropertyKey *propertyKey = dynamic_cast<QgsProjectPropertyKey *>( foundProperty );
3075
3076 if ( propertyKey )
3077 { propertyKey->subkeyList( entries ); }
3078 }
3079
3080 return entries;
3081}
3082
3084{
3085 dump_( mProperties );
3086}
3087
3089{
3090 QString filePath;
3091 switch ( filePathStorage() )
3092 {
3094 break;
3095
3097 {
3098 // for projects stored in a custom storage, we need to ask to the
3099 // storage for the path, if the storage returns an empty path
3100 // relative paths are not supported
3101 if ( QgsProjectStorage *storage = projectStorage() )
3102 {
3103 filePath = storage->filePath( mFile.fileName() );
3104 }
3105 else
3106 {
3107 filePath = fileName();
3108 }
3109 break;
3110 }
3111 }
3112
3113 return QgsPathResolver( filePath, mArchive->dir() );
3114}
3115
3116QString QgsProject::readPath( const QString &src ) const
3117{
3118 return pathResolver().readPath( src );
3119}
3120
3121QString QgsProject::writePath( const QString &src ) const
3122{
3123 return pathResolver().writePath( src );
3124}
3125
3126void QgsProject::setError( const QString &errorMessage )
3127{
3128 mErrorMessage = errorMessage;
3129}
3130
3131QString QgsProject::error() const
3132{
3133 return mErrorMessage;
3134}
3135
3136void QgsProject::clearError()
3137{
3138 setError( QString() );
3139}
3140
3142{
3143 delete mBadLayerHandler;
3144 mBadLayerHandler = handler;
3145}
3146
3147QString QgsProject::layerIsEmbedded( const QString &id ) const
3148{
3149 const QHash< QString, QPair< QString, bool > >::const_iterator it = mEmbeddedLayers.find( id );
3150 if ( it == mEmbeddedLayers.constEnd() )
3151 {
3152 return QString();
3153 }
3154 return it.value().first;
3155}
3156
3157bool QgsProject::createEmbeddedLayer( const QString &layerId, const QString &projectFilePath, QList<QDomNode> &brokenNodes,
3158 bool saveFlag, Qgis::ProjectReadFlags flags )
3159{
3161
3162 static QString sPrevProjectFilePath;
3163 static QDateTime sPrevProjectFileTimestamp;
3164 static QDomDocument sProjectDocument;
3165
3166 QString qgsProjectFile = projectFilePath;
3167 QgsProjectArchive archive;
3168 if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3169 {
3170 archive.unzip( projectFilePath );
3171 qgsProjectFile = archive.projectFile();
3172 }
3173
3174 const QDateTime projectFileTimestamp = QFileInfo( projectFilePath ).lastModified();
3175
3176 if ( projectFilePath != sPrevProjectFilePath || projectFileTimestamp != sPrevProjectFileTimestamp )
3177 {
3178 sPrevProjectFilePath.clear();
3179
3180 QFile projectFile( qgsProjectFile );
3181 if ( !projectFile.open( QIODevice::ReadOnly ) )
3182 {
3183 return false;
3184 }
3185
3186 if ( !sProjectDocument.setContent( &projectFile ) )
3187 {
3188 return false;
3189 }
3190
3191 sPrevProjectFilePath = projectFilePath;
3192 sPrevProjectFileTimestamp = projectFileTimestamp;
3193 }
3194
3195 // does project store paths absolute or relative?
3196 bool useAbsolutePaths = true;
3197
3198 const QDomElement propertiesElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "properties" ) );
3199 if ( !propertiesElem.isNull() )
3200 {
3201 const QDomElement absElem = propertiesElem.firstChildElement( QStringLiteral( "Paths" ) ).firstChildElement( QStringLiteral( "Absolute" ) );
3202 if ( !absElem.isNull() )
3203 {
3204 useAbsolutePaths = absElem.text().compare( QLatin1String( "true" ), Qt::CaseInsensitive ) == 0;
3205 }
3206 }
3207
3208 QgsReadWriteContext embeddedContext;
3209 if ( !useAbsolutePaths )
3210 embeddedContext.setPathResolver( QgsPathResolver( projectFilePath ) );
3211 embeddedContext.setProjectTranslator( this );
3212 embeddedContext.setTransformContext( transformContext() );
3213
3214 const QDomElement projectLayersElem = sProjectDocument.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) );
3215 if ( projectLayersElem.isNull() )
3216 {
3217 return false;
3218 }
3219
3220 QDomElement mapLayerElem = projectLayersElem.firstChildElement( QStringLiteral( "maplayer" ) );
3221 while ( ! mapLayerElem.isNull() )
3222 {
3223 // get layer id
3224 const QString id = mapLayerElem.firstChildElement( QStringLiteral( "id" ) ).text();
3225 if ( id == layerId )
3226 {
3227 // layer can be embedded only once
3228 if ( mapLayerElem.attribute( QStringLiteral( "embedded" ) ) == QLatin1String( "1" ) )
3229 {
3230 return false;
3231 }
3232
3233 mEmbeddedLayers.insert( layerId, qMakePair( projectFilePath, saveFlag ) );
3234
3235 if ( addLayer( mapLayerElem, brokenNodes, embeddedContext, flags ) )
3236 {
3237 return true;
3238 }
3239 else
3240 {
3241 mEmbeddedLayers.remove( layerId );
3242 return false;
3243 }
3244 }
3245 mapLayerElem = mapLayerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
3246 }
3247
3248 return false;
3249}
3250
3251
3252QgsLayerTreeGroup *QgsProject::createEmbeddedGroup( const QString &groupName, const QString &projectFilePath, const QStringList &invisibleLayers, Qgis::ProjectReadFlags flags )
3253{
3254 QString qgsProjectFile = projectFilePath;
3255 QgsProjectArchive archive;
3256 if ( projectFilePath.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
3257 {
3258 archive.unzip( projectFilePath );
3259 qgsProjectFile = archive.projectFile();
3260 }
3261
3262 // open project file, get layer ids in group, add the layers
3263 QFile projectFile( qgsProjectFile );
3264 if ( !projectFile.open( QIODevice::ReadOnly ) )
3265 {
3266 return nullptr;
3267 }
3268
3269 QDomDocument projectDocument;
3270 if ( !projectDocument.setContent( &projectFile ) )
3271 {
3272 return nullptr;
3273 }
3274
3275 QgsReadWriteContext context;
3276 context.setPathResolver( pathResolver() );
3277 context.setProjectTranslator( this );
3279
3281
3282 QDomElement layerTreeElem = projectDocument.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
3283 if ( !layerTreeElem.isNull() )
3284 {
3285 root->readChildrenFromXml( layerTreeElem, context );
3286 }
3287 else
3288 {
3289 QgsLayerTreeUtils::readOldLegend( root, projectDocument.documentElement().firstChildElement( QStringLiteral( "legend" ) ) );
3290 }
3291
3292 QgsLayerTreeGroup *group = root->findGroup( groupName );
3293 if ( !group || group->customProperty( QStringLiteral( "embedded" ) ).toBool() )
3294 {
3295 // embedded groups cannot be embedded again
3296 delete root;
3297 return nullptr;
3298 }
3299
3300 // clone the group sub-tree (it is used already in a tree, we cannot just tear it off)
3301 QgsLayerTreeGroup *newGroup = QgsLayerTree::toGroup( group->clone() );
3302 delete root;
3303 root = nullptr;
3304
3305 newGroup->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3306 newGroup->setCustomProperty( QStringLiteral( "embedded_project" ), projectFilePath );
3307
3308 // set "embedded" to all children + load embedded layers
3309 mLayerTreeRegistryBridge->setEnabled( false );
3310 initializeEmbeddedSubtree( projectFilePath, newGroup, flags );
3311 mLayerTreeRegistryBridge->setEnabled( true );
3312
3313 // consider the layers might be identify disabled in its project
3314 const auto constFindLayerIds = newGroup->findLayerIds();
3315 for ( const QString &layerId : constFindLayerIds )
3316 {
3317 QgsLayerTreeLayer *layer = newGroup->findLayer( layerId );
3318 if ( layer )
3319 {
3320 layer->resolveReferences( this );
3321 layer->setItemVisibilityChecked( !invisibleLayers.contains( layerId ) );
3322 }
3323 }
3324
3325 return newGroup;
3326}
3327
3328void QgsProject::initializeEmbeddedSubtree( const QString &projectFilePath, QgsLayerTreeGroup *group, Qgis::ProjectReadFlags flags )
3329{
3330 const auto constChildren = group->children();
3331 for ( QgsLayerTreeNode *child : constChildren )
3332 {
3333 // all nodes in the subtree will have "embedded" custom property set
3334 child->setCustomProperty( QStringLiteral( "embedded" ), 1 );
3335
3336 if ( QgsLayerTree::isGroup( child ) )
3337 {
3338 initializeEmbeddedSubtree( projectFilePath, QgsLayerTree::toGroup( child ), flags );
3339 }
3340 else if ( QgsLayerTree::isLayer( child ) )
3341 {
3342 // load the layer into our project
3343 QList<QDomNode> brokenNodes;
3344 createEmbeddedLayer( QgsLayerTree::toLayer( child )->layerId(), projectFilePath, brokenNodes, false, flags );
3345 }
3346 }
3347}
3348
3350{
3352}
3353
3354void QgsProject::setEvaluateDefaultValues( bool evaluateDefaultValues )
3355{
3357}
3358
3360{
3361 writeEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), ( enabled ? 1 : 0 ) );
3363}
3364
3366{
3367 return readNumEntry( QStringLiteral( "Digitizing" ), QStringLiteral( "/TopologicalEditing" ), 0 );
3368}
3369
3371{
3372 if ( mDistanceUnits == unit )
3373 return;
3374
3375 mDistanceUnits = unit;
3376
3377 emit distanceUnitsChanged();
3378}
3379
3381{
3382 if ( mAreaUnits == unit )
3383 return;
3384
3385 mAreaUnits = unit;
3386
3387 emit areaUnitsChanged();
3388}
3389
3391{
3392 if ( !mCachedHomePath.isEmpty() )
3393 return mCachedHomePath;
3394
3395 const QFileInfo pfi( fileName() );
3396
3397 if ( !mHomePath.isEmpty() )
3398 {
3399 const QFileInfo homeInfo( mHomePath );
3400 if ( !homeInfo.isRelative() )
3401 {
3402 mCachedHomePath = mHomePath;
3403 return mHomePath;
3404 }
3405 }
3406 else if ( !fileName().isEmpty() )
3407 {
3408 mCachedHomePath = pfi.path();
3409
3410 return mCachedHomePath;
3411 }
3412
3413 if ( !pfi.exists() )
3414 {
3415 mCachedHomePath = mHomePath;
3416 return mHomePath;
3417 }
3418
3419 if ( !mHomePath.isEmpty() )
3420 {
3421 // path is relative to project file
3422 mCachedHomePath = QDir::cleanPath( pfi.path() + '/' + mHomePath );
3423 }
3424 else
3425 {
3426 mCachedHomePath = pfi.canonicalPath();
3427 }
3428 return mCachedHomePath;
3429}
3430
3432{
3433 return mHomePath;
3434}
3435
3437{
3438 return mRelationManager;
3439}
3440
3442{
3443 return mLayoutManager.get();
3444}
3445
3447{
3448 return mLayoutManager.get();
3449}
3450
3452{
3453 return m3DViewsManager.get();
3454}
3455
3457{
3458 return m3DViewsManager.get();
3459}
3460
3462{
3463 return mBookmarkManager;
3464}
3465
3467{
3468 return mBookmarkManager;
3469}
3470
3472{
3473 return mViewSettings;
3474}
3475
3477{
3478 return mViewSettings;
3479}
3480
3482{
3483 return mStyleSettings;
3484}
3485
3487{
3488 return mStyleSettings;
3489}
3490
3492{
3493 return mTimeSettings;
3494}
3495
3497{
3498 return mTimeSettings;
3499}
3500
3502{
3503 return mElevationProperties;
3504}
3505
3507{
3508 return mElevationProperties;
3509}
3510
3512{
3513 return mDisplaySettings;
3514}
3515
3517{
3518 return mDisplaySettings;
3519}
3520
3522{
3523 return mRootGroup;
3524}
3525
3527{
3528 return mMapThemeCollection.get();
3529}
3530
3532{
3533 return mAnnotationManager.get();
3534}
3535
3537{
3538 return mAnnotationManager.get();
3539}
3540
3541void QgsProject::setNonIdentifiableLayers( const QList<QgsMapLayer *> &layers )
3542{
3543 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
3544 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
3545 {
3546 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3547 continue;
3548
3549 if ( layers.contains( it.value() ) )
3550 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Identifiable );
3551 else
3552 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Identifiable );
3553 }
3554
3558}
3559
3560void QgsProject::setNonIdentifiableLayers( const QStringList &layerIds )
3561{
3562 QList<QgsMapLayer *> nonIdentifiableLayers;
3563 nonIdentifiableLayers.reserve( layerIds.count() );
3564 for ( const QString &layerId : layerIds )
3565 {
3566 QgsMapLayer *layer = mapLayer( layerId );
3567 if ( layer )
3568 nonIdentifiableLayers << layer;
3569 }
3573}
3574
3576{
3577 QStringList nonIdentifiableLayers;
3578
3579 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
3580 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
3581 {
3582 if ( !it.value()->flags().testFlag( QgsMapLayer::Identifiable ) )
3583 {
3584 nonIdentifiableLayers.append( it.value()->id() );
3585 }
3586 }
3587 return nonIdentifiableLayers;
3588}
3589
3591{
3592 return mTransactionMode == Qgis::TransactionMode::AutomaticGroups;
3593}
3594
3595void QgsProject::setAutoTransaction( bool autoTransaction )
3596{
3597 if ( autoTransaction
3598 && mTransactionMode == Qgis::TransactionMode::AutomaticGroups )
3599 return;
3600
3601 if ( ! autoTransaction
3602 && mTransactionMode == Qgis::TransactionMode::Disabled )
3603 return;
3604
3605 if ( autoTransaction )
3606 mTransactionMode = Qgis::TransactionMode::AutomaticGroups;
3607 else
3608 mTransactionMode = Qgis::TransactionMode::Disabled;
3609
3610 updateTransactionGroups();
3611}
3612
3614{
3615 return mTransactionMode;
3616}
3617
3619{
3620 if ( transactionMode == mTransactionMode )
3621 return true;
3622
3623 // Check that all layer are not in edit mode
3624 const auto constLayers = mapLayers().values();
3625 for ( QgsMapLayer *layer : constLayers )
3626 {
3627 if ( layer->isEditable() )
3628 {
3629 QgsLogger::warning( tr( "Transaction mode can be changed only if all layers are not editable." ) );
3630 return false;
3631 }
3632 }
3633
3634 mTransactionMode = transactionMode;
3635 updateTransactionGroups();
3636 return true;
3637}
3638
3639QMap<QPair<QString, QString>, QgsTransactionGroup *> QgsProject::transactionGroups()
3640{
3641 return mTransactionGroups;
3642}
3643
3644
3645//
3646// QgsMapLayerStore methods
3647//
3648
3649
3651{
3652 return mLayerStore->count();
3653}
3654
3656{
3657 return mLayerStore->validCount();
3658}
3659
3660QgsMapLayer *QgsProject::mapLayer( const QString &layerId ) const
3661{
3662 return mLayerStore->mapLayer( layerId );
3663}
3664
3665QList<QgsMapLayer *> QgsProject::mapLayersByName( const QString &layerName ) const
3666{
3667 return mLayerStore->mapLayersByName( layerName );
3668}
3669
3670QList<QgsMapLayer *> QgsProject::mapLayersByShortName( const QString &shortName ) const
3671{
3672 QList<QgsMapLayer *> layers;
3673 const auto constMapLayers { mLayerStore->mapLayers() };
3674 for ( const auto &l : constMapLayers )
3675 {
3676 if ( ! l->shortName().isEmpty() )
3677 {
3678 if ( l->shortName() == shortName )
3679 layers << l;
3680 }
3681 else if ( l->name() == shortName )
3682 {
3683 layers << l;
3684 }
3685 }
3686 return layers;
3687}
3688
3689bool QgsProject::unzip( const QString &filename, Qgis::ProjectReadFlags flags )
3690{
3691 clearError();
3692 std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3693
3694 // unzip the archive
3695 if ( !archive->unzip( filename ) )
3696 {
3697 setError( tr( "Unable to unzip file '%1'" ).arg( filename ) );
3698 return false;
3699 }
3700
3701 // test if zip provides a .qgs file
3702 if ( archive->projectFile().isEmpty() )
3703 {
3704 setError( tr( "Zip archive does not provide a project file" ) );
3705 return false;
3706 }
3707
3708 // Keep the archive
3709 mArchive = std::move( archive );
3710
3711 // load auxiliary storage
3712 if ( !static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile().isEmpty() )
3713 {
3714 // database file is already a copy as it's been unzipped. So we don't open
3715 // auxiliary storage in copy mode in this case
3716 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile(), false ) );
3717 }
3718 else
3719 {
3720 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( *this ) );
3721 }
3722
3723 // read the project file
3724 if ( ! readProjectFile( static_cast<QgsProjectArchive *>( mArchive.get() )->projectFile(), flags ) )
3725 {
3726 setError( tr( "Cannot read unzipped qgs project file" ) );
3727 return false;
3728 }
3729
3730 // Remove the temporary .qgs file
3731 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3732
3733 return true;
3734}
3735
3736bool QgsProject::zip( const QString &filename )
3737{
3738 clearError();
3739
3740 // save the current project in a temporary .qgs file
3741 std::unique_ptr<QgsProjectArchive> archive( new QgsProjectArchive() );
3742 const QString baseName = QFileInfo( filename ).baseName();
3743 const QString qgsFileName = QStringLiteral( "%1.qgs" ).arg( baseName );
3744 QFile qgsFile( QDir( archive->dir() ).filePath( qgsFileName ) );
3745
3746 bool writeOk = false;
3747 if ( qgsFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
3748 {
3749 writeOk = writeProjectFile( qgsFile.fileName() );
3750 qgsFile.close();
3751 }
3752
3753 // stop here with an error message
3754 if ( ! writeOk )
3755 {
3756 setError( tr( "Unable to write temporary qgs file" ) );
3757 return false;
3758 }
3759
3760 // save auxiliary storage
3761 const QFileInfo info( qgsFile );
3762 const QString asExt = QStringLiteral( ".%1" ).arg( QgsAuxiliaryStorage::extension() );
3763 const QString asFileName = info.path() + QDir::separator() + info.completeBaseName() + asExt;
3764
3765 bool auxiliaryStorageSavedOk = true;
3766 if ( ! saveAuxiliaryStorage( asFileName ) )
3767 {
3768 const QString err = mAuxiliaryStorage->errorString();
3769 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 ) );
3770 auxiliaryStorageSavedOk = false;
3771
3772 // fixes the current archive and keep the previous version of qgd
3773 if ( !mArchive->exists() )
3774 {
3775 mArchive.reset( new QgsProjectArchive() );
3776 mArchive->unzip( mFile.fileName() );
3777 static_cast<QgsProjectArchive *>( mArchive.get() )->clearProjectFile();
3778
3779 const QString auxiliaryStorageFile = static_cast<QgsProjectArchive *>( mArchive.get() )->auxiliaryStorageFile();
3780 if ( ! auxiliaryStorageFile.isEmpty() )
3781 {
3782 archive->addFile( auxiliaryStorageFile );
3783 mAuxiliaryStorage.reset( new QgsAuxiliaryStorage( auxiliaryStorageFile, false ) );
3784 }
3785 }
3786 }
3787 else
3788 {
3789 // in this case, an empty filename means that the auxiliary database is
3790 // empty, so we don't want to save it
3791 if ( QFile::exists( asFileName ) )
3792 {
3793 archive->addFile( asFileName );
3794 }
3795 }
3796
3797 // create the archive
3798 archive->addFile( qgsFile.fileName() );
3799
3800 // Add all other files
3801 const QStringList &files = mArchive->files();
3802 for ( const QString &file : files )
3803 {
3804 if ( !file.endsWith( QLatin1String( ".qgs" ), Qt::CaseInsensitive ) && !file.endsWith( asExt, Qt::CaseInsensitive ) )
3805 {
3806 archive->addFile( file );
3807 }
3808 }
3809
3810 // zip
3811 bool zipOk = true;
3812 if ( !archive->zip( filename ) )
3813 {
3814 setError( tr( "Unable to perform zip" ) );
3815 zipOk = false;
3816 }
3817
3818 return auxiliaryStorageSavedOk && zipOk;
3819}
3820
3822{
3823 return QgsZipUtils::isZipFile( mFile.fileName() );
3824}
3825
3826QList<QgsMapLayer *> QgsProject::addMapLayers(
3827 const QList<QgsMapLayer *> &layers,
3828 bool addToLegend,
3829 bool takeOwnership )
3830{
3831 const QList<QgsMapLayer *> myResultList { mLayerStore->addMapLayers( layers, takeOwnership ) };
3832 if ( !myResultList.isEmpty() )
3833 {
3834 // Update transform context
3835 for ( auto &l : myResultList )
3836 {
3837 l->setTransformContext( transformContext() );
3838 }
3839 if ( addToLegend )
3840 {
3841 emit legendLayersAdded( myResultList );
3842 }
3843 }
3844
3845 if ( mAuxiliaryStorage )
3846 {
3847 for ( QgsMapLayer *mlayer : myResultList )
3848 {
3849 if ( mlayer->type() != QgsMapLayerType::VectorLayer )
3850 continue;
3851
3852 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( mlayer );
3853 if ( vl )
3854 {
3855 vl->loadAuxiliaryLayer( *mAuxiliaryStorage );
3856 }
3857 }
3858 }
3859
3860 mProjectScope.reset();
3861
3862 return myResultList;
3863}
3864
3867 bool addToLegend,
3868 bool takeOwnership )
3869{
3870 QList<QgsMapLayer *> addedLayers;
3871 addedLayers = addMapLayers( QList<QgsMapLayer *>() << layer, addToLegend, takeOwnership );
3872 return addedLayers.isEmpty() ? nullptr : addedLayers[0];
3873}
3874
3875void QgsProject::removeAuxiliaryLayer( const QgsMapLayer *ml )
3876{
3877 if ( ! ml || ml->type() != QgsMapLayerType::VectorLayer )
3878 return;
3879
3880 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( ml );
3881 if ( vl && vl->auxiliaryLayer() )
3882 {
3883 const QgsDataSourceUri uri( vl->auxiliaryLayer()->source() );
3885 }
3886}
3887
3888void QgsProject::removeMapLayers( const QStringList &layerIds )
3889{
3890 for ( const auto &layerId : layerIds )
3891 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
3892
3893 mProjectScope.reset();
3894 mLayerStore->removeMapLayers( layerIds );
3895}
3896
3897void QgsProject::removeMapLayers( const QList<QgsMapLayer *> &layers )
3898{
3899 for ( const auto &layer : layers )
3900 removeAuxiliaryLayer( layer );
3901
3902 mProjectScope.reset();
3903 mLayerStore->removeMapLayers( layers );
3904}
3905
3906void QgsProject::removeMapLayer( const QString &layerId )
3907{
3908 removeAuxiliaryLayer( mLayerStore->mapLayer( layerId ) );
3909 mProjectScope.reset();
3910 mLayerStore->removeMapLayer( layerId );
3911}
3912
3914{
3915 removeAuxiliaryLayer( layer );
3916 mProjectScope.reset();
3917 mLayerStore->removeMapLayer( layer );
3918}
3919
3921{
3922 mProjectScope.reset();
3923 return mLayerStore->takeMapLayer( layer );
3924}
3925
3927{
3928 return mMainAnnotationLayer;
3929}
3930
3932{
3933 if ( mLayerStore->count() == 0 )
3934 return;
3935
3936 ScopedIntIncrementor snapSingleBlocker( &mBlockSnappingUpdates );
3937 mProjectScope.reset();
3938 mLayerStore->removeAllMapLayers();
3939
3940 snapSingleBlocker.release();
3941 mSnappingConfig.clearIndividualLayerSettings();
3942 if ( !mBlockSnappingUpdates )
3943 emit snappingConfigChanged( mSnappingConfig );
3944}
3945
3947{
3948 const QMap<QString, QgsMapLayer *> layers = mLayerStore->mapLayers();
3949 QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin();
3950 for ( ; it != layers.constEnd(); ++it )
3951 {
3952 it.value()->reload();
3953 }
3954}
3955
3956QMap<QString, QgsMapLayer *> QgsProject::mapLayers( const bool validOnly ) const
3957{
3958 return validOnly ? mLayerStore->validMapLayers() : mLayerStore->mapLayers();
3959}
3960
3961QgsTransactionGroup *QgsProject::transactionGroup( const QString &providerKey, const QString &connString )
3962{
3963 return mTransactionGroups.value( qMakePair( providerKey, connString ) );
3964}
3965
3967{
3968 return &mEditBufferGroup;
3969}
3970
3972{
3974
3975 // TODO QGIS 4.0 -- remove this method, and place it somewhere in app (where it belongs)
3976 // 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)
3977 if ( mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), QStringLiteral( "NoAction" ), QgsSettings::App ).toString() == QStringLiteral( "UseProjectCrs" )
3978 || mSettings.value( QStringLiteral( "/projections/unknownCrsBehavior" ), 0, QgsSettings::App ).toString() == QLatin1String( "2" ) )
3979 {
3980 // for new layers if the new layer crs method is set to either prompt or use project, then we use the project crs
3981 defaultCrs = crs();
3982 }
3983 else
3984 {
3985 // global crs
3986 const QString layerDefaultCrs = mSettings.value( QStringLiteral( "/Projections/layerDefaultCrs" ), geoEpsgCrsAuthId() ).toString();
3987 defaultCrs = QgsCoordinateReferenceSystem::fromOgcWmsCrs( layerDefaultCrs );
3988 }
3989
3990 return defaultCrs;
3991}
3992
3994{
3996}
3997
3999{
4001}
4002
4003bool QgsProject::saveAuxiliaryStorage( const QString &filename )
4004{
4005 const QMap<QString, QgsMapLayer *> layers = mapLayers();
4006 bool empty = true;
4007 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4008 {
4009 if ( it.value()->type() != QgsMapLayerType::VectorLayer )
4010 continue;
4011
4012 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( it.value() );
4013 if ( vl && vl->auxiliaryLayer() )
4014 {
4015 vl->auxiliaryLayer()->save();
4016 empty &= vl->auxiliaryLayer()->auxiliaryFields().isEmpty();
4017 }
4018 }
4019
4020 if ( !mAuxiliaryStorage->exists( *this ) && empty )
4021 {
4022 return true; // it's not an error
4023 }
4024 else if ( !filename.isEmpty() )
4025 {
4026 return mAuxiliaryStorage->saveAs( filename );
4027 }
4028 else
4029 {
4030 return mAuxiliaryStorage->saveAs( *this );
4031 }
4032}
4033
4034QgsPropertiesDefinition &QgsProject::dataDefinedServerPropertyDefinitions()
4035{
4036 static QgsPropertiesDefinition sPropertyDefinitions
4037 {
4038 {
4039 QgsProject::DataDefinedServerProperty::WMSOnlineResource,
4040 QgsPropertyDefinition( "WMSOnlineResource", QObject::tr( "WMS Online Resource" ), QgsPropertyDefinition::String )
4041 },
4042 };
4043 return sPropertyDefinitions;
4044}
4045
4047{
4048 return mAuxiliaryStorage.get();
4049}
4050
4052{
4053 return mAuxiliaryStorage.get();
4054}
4055
4056QString QgsProject::createAttachedFile( const QString &nameTemplate )
4057{
4058 const QDir archiveDir( mArchive->dir() );
4059 QTemporaryFile tmpFile( archiveDir.filePath( "XXXXXX_" + nameTemplate ), this );
4060 tmpFile.setAutoRemove( false );
4061 tmpFile.open();
4062 mArchive->addFile( tmpFile.fileName() );
4063 return tmpFile.fileName();
4064}
4065
4066QStringList QgsProject::attachedFiles() const
4067{
4068 QStringList attachments;
4069 const QString baseName = QFileInfo( fileName() ).baseName();
4070 const QStringList files = mArchive->files();
4071 attachments.reserve( files.size() );
4072 for ( const QString &file : files )
4073 {
4074 if ( QFileInfo( file ).baseName() != baseName )
4075 {
4076 attachments.append( file );
4077 }
4078 }
4079 return attachments;
4080}
4081
4082bool QgsProject::removeAttachedFile( const QString &path )
4083{
4084 return mArchive->removeFile( path );
4085}
4086
4087QString QgsProject::attachmentIdentifier( const QString &attachedFile ) const
4088{
4089 return QStringLiteral( "attachment:///%1" ).arg( QFileInfo( attachedFile ).fileName() );
4090}
4091
4092QString QgsProject::resolveAttachmentIdentifier( const QString &identifier ) const
4093{
4094 if ( identifier.startsWith( QLatin1String( "attachment:///" ) ) )
4095 {
4096 return QDir( mArchive->dir() ).absoluteFilePath( identifier.mid( 14 ) );
4097 }
4098 return QString();
4099}
4100
4102{
4103 return mMetadata;
4104}
4105
4107{
4108 if ( metadata == mMetadata )
4109 return;
4110
4111 mMetadata = metadata;
4112 mProjectScope.reset();
4113
4114 emit metadataChanged();
4115
4116 setDirty( true );
4117}
4118
4119QSet<QgsMapLayer *> QgsProject::requiredLayers() const
4120{
4121 QSet<QgsMapLayer *> requiredLayers;
4122
4123 const QMap<QString, QgsMapLayer *> &layers = mapLayers();
4124 for ( QMap<QString, QgsMapLayer *>::const_iterator it = layers.constBegin(); it != layers.constEnd(); ++it )
4125 {
4126 if ( !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4127 {
4128 requiredLayers.insert( it.value() );
4129 }
4130 }
4131 return requiredLayers;
4132}
4133
4134void QgsProject::setRequiredLayers( const QSet<QgsMapLayer *> &layers )
4135{
4136 const QMap<QString, QgsMapLayer *> &projectLayers = mapLayers();
4137 for ( QMap<QString, QgsMapLayer *>::const_iterator it = projectLayers.constBegin(); it != projectLayers.constEnd(); ++it )
4138 {
4139 if ( layers.contains( it.value() ) == !it.value()->flags().testFlag( QgsMapLayer::Removable ) )
4140 continue;
4141
4142 if ( layers.contains( it.value() ) )
4143 it.value()->setFlags( it.value()->flags() & ~QgsMapLayer::Removable );
4144 else
4145 it.value()->setFlags( it.value()->flags() | QgsMapLayer::Removable );
4146 }
4147}
4148
4150{
4151 // save colors to project
4152 QStringList customColors;
4153 QStringList customColorLabels;
4154
4155 QgsNamedColorList::const_iterator colorIt = colors.constBegin();
4156 for ( ; colorIt != colors.constEnd(); ++colorIt )
4157 {
4158 const QString color = QgsSymbolLayerUtils::encodeColor( ( *colorIt ).first );
4159 const QString label = ( *colorIt ).second;
4160 customColors.append( color );
4161 customColorLabels.append( label );
4162 }
4163 writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ), customColors );
4164 writeEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ), customColorLabels );
4165 mProjectScope.reset();
4166 emit projectColorsChanged();
4167}
4168
4169void QgsProject::setBackgroundColor( const QColor &color )
4170{
4171 if ( mBackgroundColor == color )
4172 return;
4173
4174 mBackgroundColor = color;
4176}
4177
4179{
4180 return mBackgroundColor;
4181}
4182
4183void QgsProject::setSelectionColor( const QColor &color )
4184{
4185 if ( mSelectionColor == color )
4186 return;
4187
4188 mSelectionColor = color;
4189 emit selectionColorChanged();
4190}
4191
4193{
4194 return mSelectionColor;
4195}
4196
4197void QgsProject::setMapScales( const QVector<double> &scales )
4198{
4199 mViewSettings->setMapScales( scales );
4200}
4201
4202QVector<double> QgsProject::mapScales() const
4203{
4204 return mViewSettings->mapScales();
4205}
4206
4208{
4209 mViewSettings->setUseProjectScales( enabled );
4210}
4211
4213{
4214 return mViewSettings->useProjectScales();
4215}
4216
4217void QgsProject::generateTsFile( const QString &locale )
4218{
4219 QgsTranslationContext translationContext;
4220 translationContext.setProject( this );
4221 translationContext.setFileName( QStringLiteral( "%1/%2.ts" ).arg( absolutePath(), baseName() ) );
4222
4223 QgsApplication::instance()->collectTranslatableObjects( &translationContext );
4224
4225 translationContext.writeTsFile( locale );
4226}
4227
4228QString QgsProject::translate( const QString &context, const QString &sourceText, const char *disambiguation, int n ) const
4229{
4230 if ( !mTranslator )
4231 {
4232 return sourceText;
4233 }
4234
4235 QString result = mTranslator->translate( context.toUtf8(), sourceText.toUtf8(), disambiguation, n );
4236
4237 if ( result.isEmpty() )
4238 {
4239 return sourceText;
4240 }
4241 return result;
4242}
4243
4245{
4246 const QMap<QString, QgsMapLayer *> layers = mapLayers( false );
4247 if ( !layers.empty() )
4248 {
4249 for ( auto it = layers.constBegin(); it != layers.constEnd(); ++it )
4250 {
4251 // NOTE: if visitEnter returns false it means "don't visit this layer", not "abort all further visitations"
4252 if ( visitor->visitEnter( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4253 {
4254 if ( !( ( *it )->accept( visitor ) ) )
4255 return false;
4256
4257 if ( !visitor->visitExit( QgsStyleEntityVisitorInterface::Node( QgsStyleEntityVisitorInterface::NodeType::Layer, ( *it )->id(), ( *it )->name() ) ) )
4258 return false;
4259 }
4260 }
4261 }
4262
4263 if ( !mLayoutManager->accept( visitor ) )
4264 return false;
4265
4266 if ( !mAnnotationManager->accept( visitor ) )
4267 return false;
4268
4269 return true;
4270}
4271
4272void QgsProject::loadProjectFlags( const QDomDocument *doc )
4273{
4274 QDomElement element = doc->documentElement().firstChildElement( QStringLiteral( "projectFlags" ) );
4275 Qgis::ProjectFlags flags;
4276 if ( !element.isNull() )
4277 {
4278 flags = qgsFlagKeysToValue( element.attribute( QStringLiteral( "set" ) ), Qgis::ProjectFlags() );
4279 }
4280 else
4281 {
4282 // older project compatibility
4283 element = doc->documentElement().firstChildElement( QStringLiteral( "evaluateDefaultValues" ) );
4284 if ( !element.isNull() )
4285 {
4286 if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
4288 }
4289
4290 // Read trust layer metadata config in the project
4291 element = doc->documentElement().firstChildElement( QStringLiteral( "trust" ) );
4292 if ( !element.isNull() )
4293 {
4294 if ( element.attribute( QStringLiteral( "active" ), QStringLiteral( "0" ) ).toInt() == 1 )
4296 }
4297 }
4298
4299 setFlags( flags );
4300}
4301
4303GetNamedProjectColor::GetNamedProjectColor( const QgsProject *project )
4304 : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
4305{
4306 if ( !project )
4307 return;
4308
4309 //build up color list from project. Do this in advance for speed
4310 QStringList colorStrings = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
4311 const QStringList colorLabels = project->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
4312
4313 //generate list from custom colors
4314 int colorIndex = 0;
4315 for ( QStringList::iterator it = colorStrings.begin();
4316 it != colorStrings.end(); ++it )
4317 {
4318 const QColor color = QgsSymbolLayerUtils::decodeColor( *it );
4319 QString label;
4320 if ( colorLabels.length() > colorIndex )
4321 {
4322 label = colorLabels.at( colorIndex );
4323 }
4324
4325 mColors.insert( label.toLower(), color );
4326 colorIndex++;
4327 }
4328}
4329
4330GetNamedProjectColor::GetNamedProjectColor( const QHash<QString, QColor> &colors )
4331 : QgsScopedExpressionFunction( QStringLiteral( "project_color" ), 1, QStringLiteral( "Color" ) )
4332 , mColors( colors )
4333{
4334}
4335
4336QVariant GetNamedProjectColor::func( const QVariantList &values, const QgsExpressionContext *, QgsExpression *, const QgsExpressionNodeFunction * )
4337{
4338 const QString colorName = values.at( 0 ).toString().toLower();
4339 if ( mColors.contains( colorName ) )
4340 {
4341 return QStringLiteral( "%1,%2,%3" ).arg( mColors.value( colorName ).red() ).arg( mColors.value( colorName ).green() ).arg( mColors.value( colorName ).blue() );
4342 }
4343 else
4344 return QVariant();
4345}
4346
4347QgsScopedExpressionFunction *GetNamedProjectColor::clone() const
4348{
4349 return new GetNamedProjectColor( mColors );
4350}
@ ForceReadOnlyLayers
Open layers in a read-only mode. (since QGIS 3.28)
static QString version()
Version string.
Definition: qgis.cpp:277
FilePathType
File path types.
Definition: qgis.h:772
@ Relative
Relative path.
@ Absolute
Absolute path.
TransactionMode
Transaction mode.
Definition: qgis.h:1914
@ 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.
AvoidIntersectionsMode
Flags which control how intersections of pre-existing feature are handled when digitizing new feature...
Definition: qgis.h:2085
@ 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:1978
@ 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...
@ 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 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.
static const QgsSettingsEntryString settingsLocaleUserLocale
Settings entry locale user locale.
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.
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.
Q_GADGET QgsUnitTypes::DistanceUnit mapUnits
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.
@ 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
Single scope for storing variables and functions for use within a QgsExpressionContext.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
An 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:51
QString name
Definition: qgsfield.h:60
QString alias
Definition: qgsfield.h:61
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:631
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:122
static QgsMapLayerType 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.
QgsMapLayerType type
Definition: qgsmaplayer.h:80
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 ...
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 readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
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:145
@ Removable
If the layer can be removed from the project. The layer will not be removable from the legend menu en...
Definition: qgsmaplayer.h:146
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
Definition: qgsmaplayer.h:641
@ FlagForceReadOnly
Force open as read only.
Definition: qgsmaplayer.h:643
@ FlagDontResolveLayers
Don't resolve layer paths or create data providers for layers.
Definition: qgsmaplayer.h:640
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
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.
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 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:104
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:114
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:368
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.
void setAreaUnits(QgsUnitTypes::AreaUnit unit)
Sets the default area measurement units for the project.
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.
Definition: qgsproject.cpp:929
~QgsProject() override
Definition: qgsproject.cpp:456
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:202
Q_DECL_DEPRECATED QFileInfo fileInfo() const
Returns QFileInfo object for the project's associated file.
Definition: qgsproject.cpp:785
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:869
QColor selectionColor
Definition: qgsproject.h:119
QString title() const
Returns the project's title.
Definition: qgsproject.cpp:500
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:702
void mapThemeCollectionChanged()
Emitted when the map theme collection changes.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:477
Qgis::FilePathType filePathStorage() const
Returns the type of paths used when storing file paths in a QGS/QGZ project file.
Definition: qgsproject.cpp:845
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:111
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.
QgsUnitTypes::DistanceUnit distanceUnits
Definition: qgsproject.h:121
void writeMapLayer(QgsMapLayer *mapLayer, QDomElement &layerElem, QDomDocument &doc)
Emitted when a layer is being saved.
void setSnappingConfig(const QgsSnappingConfig &snappingConfig)
The snapping configuration for this project.
void setDistanceUnits(QgsUnitTypes::DistanceUnit unit)
Sets the default distance measurement units for the 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:678
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:726
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.
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:113
void setFileName(const QString &name)
Sets the file name associated with the project.
Definition: qgsproject.cpp:750
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:673
void registerTranslatableObjects(QgsTranslationContext *translationContext)
Registers the objects that require translation into the translationContext.
Definition: qgsproject.cpp:620
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:683
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.
QgsUnitTypes::AreaUnit areaUnits
Definition: qgsproject.h:122
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:809
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:116
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:831
void ellipsoidChanged(const QString &ellipsoid)
Emitted when the project ellipsoid is changed.
QgsMapThemeCollection * mapThemeCollection
Definition: qgsproject.h:112
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:123
Qgis::TransactionMode transactionMode() const
Returns the transaction mode.
QgsProjectMetadata metadata
Definition: qgsproject.h:117
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:548
QVector< T > layers() const
Returns a list of registered map layers with a specified layer type.
Definition: qgsproject.h:1178
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:110
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:780
void setOriginalPath(const QString &path)
Sets the original path associated with the project.
Definition: qgsproject.cpp:775
void dumpProperties() const
Dump out current project properties to stderr.
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:471
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...
QList< QgsMapLayer * > mapLayersByName(const QString &layerName) const
Retrieve a list of matching registered layers by layer name.
QString fileName
Definition: qgsproject.h:107
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:109
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:106
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 setTitle(const QString &title)
Sets the project's title.
Definition: qgsproject.cpp:488
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:538
QDateTime lastModified() const
Returns last modified time of the project file as returned by the file system (or other project stora...
Definition: qgsproject.cpp:795
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:820
QDateTime lastSaveDateTime() const
Returns the date and time when the project was last saved.
Definition: qgsproject.cpp:558
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:898
void setTransformContext(const QgsCoordinateTransformContext &context)
Sets the project's coordinate transform context, which stores various information regarding which dat...
Definition: qgsproject.cpp:913
QColor backgroundColor
Definition: qgsproject.h:118
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:120
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:573
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:602
void setFilePathStorage(Qgis::FilePathType type)
Sets the type of paths used when storing file paths in a QGS/QGZ project file.
Definition: qgsproject.cpp:851
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:553
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:108
bool isDirty() const
Returns true if the project has been modified since the last write()
Definition: qgsproject.cpp:568
QgsMapLayer * takeMapLayer(QgsMapLayer *layer)
Takes a layer from the registry.
void isDirtyChanged(bool dirty)
Emitted when the project dirty status changes.
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:588
void setFlags(Qgis::ProjectFlags flags)
Sets the project's flags, which dictate the behavior of the project.
Definition: qgsproject.cpp:505
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:790
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:563
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
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.
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.
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.
T value(const QString &dynamicKeyPart=QString()) const
Returns settings value.
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:3055
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 setFileName(const QString &fileName)
Sets the fileName of the TS file.
void writeTsFile(const QString &locale) const
Writes the Ts-file.
void setProject(QgsProject *project)
Sets the project being translated.
DistanceUnit
Units of distance.
Definition: qgsunittypes.h:68
@ DistanceMeters
Meters.
Definition: qgsunittypes.h:69
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::DistanceUnit decodeDistanceUnit(const QString &string, bool *ok=nullptr)
Decodes a distance unit from a string.
static Q_INVOKABLE QString toString(QgsUnitTypes::DistanceUnit unit)
Returns a translated string representing a distance unit.
static Q_INVOKABLE QgsUnitTypes::AreaUnit decodeAreaUnit(const QString &string, bool *ok=nullptr)
Decodes an areal unit from a string.
AreaUnit
Units of area.
Definition: qgsunittypes.h:94
@ AreaSquareMeters
Square meters.
Definition: qgsunittypes.h:95
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.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
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
CONSTLATIN1STRING geoNone()
Constant that holds the string representation for "No ellips/No CRS".
Definition: qgis.h:2979
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:2700
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:3061
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:2681
QString qgsFlagValueToKeys(const T &value, bool *returnOk=nullptr)
Returns the value for the given keys of a flag.
Definition: qgis.h:2739
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:2761
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:3060
CONSTLATIN1STRING geoEpsgCrsAuthId()
Geographic coord sys from EPSG authority.
Definition: qgis.h:2973
const QgsField & field
Definition: qgsfield.h:463
#define QgsDebugCall
Definition: qgslogger.h:37
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QPointer< QgsMapLayer > QgsWeakMapLayerPointer
Weak pointer for QgsMapLayer.
Definition: qgsmaplayer.h:2147
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:308
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:104
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:145
QgsProjectProperty * addKey_(const QString &scope, const QString &key, QgsProjectPropertyKey *rootProperty, const QVariant &value, bool &propertiesModified)
Adds the given key and value.
Definition: qgsproject.cpp:222
QMap< int, QgsPropertyDefinition > QgsPropertiesDefinition
Definition of available properties.
const QgsCoordinateReferenceSystem & crs
const QString & typeName
Setting options for loading annotation layers.
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.