QGIS API Documentation 3.99.0-Master (21b3aa880ba)
Loading...
Searching...
No Matches
qgsmaplayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmaplayer.cpp - description
3 -------------------
4 begin : Fri Jun 28 2002
5 copyright : (C) 2002 by Gary E.Sherman
6 email : sherman at mrcc.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
19#include "qgsmaplayer.h"
20
21#include <sqlite3.h>
22
25#include "qgsapplication.h"
26#include "qgsauthmanager.h"
29#include "qgsdatasourceuri.h"
30#include "qgsdatums.h"
31#include "qgsfileutils.h"
32#include "qgslayernotesutils.h"
33#include "qgslogger.h"
35#include "qgsmaplayerlegend.h"
38#include "qgsmessagelog.h"
39#include "qgsobjectvisitor.h"
40#include "qgspathresolver.h"
41#include "qgsproject.h"
43#include "qgsprojoperation.h"
44#include "qgsprovidermetadata.h"
45#include "qgsproviderregistry.h"
46#include "qgsrasterlayer.h"
47#include "qgsreadwritecontext.h"
48#include "qgsrectangle.h"
49#include "qgsscaleutils.h"
50#include "qgssldexportcontext.h"
51#include "qgssqliteutils.h"
52#include "qgsstringutils.h"
53#include "qgsthreadingutils.h"
54#include "qgsunittypes.h"
55#include "qgsvectorlayer.h"
56#include "qgsxmlutils.h"
57
58#include <QDir>
59#include <QDomDocument>
60#include <QDomElement>
61#include <QDomImplementation>
62#include <QDomNode>
63#include <QFile>
64#include <QFileInfo>
65#include <QLocale>
66#include <QRegularExpression>
67#include <QStandardPaths>
68#include <QTextStream>
69#include <QTimer>
70#include <QUrl>
71#include <QUuid>
72#include <QXmlStreamReader>
73
74#include "moc_qgsmaplayer.cpp"
75
77{
78 switch ( type )
79 {
80 case Metadata:
81 return QStringLiteral( ".qmd" );
82
83 case Style:
84 return QStringLiteral( ".qml" );
85 }
86 return QString();
87}
88
90 const QString &lyrname,
91 const QString &source )
93 , mLayerName( lyrname )
94 , mLayerType( type )
95 , mServerProperties( std::make_unique<QgsMapLayerServerProperties>( this ) )
96 , mUndoStack( new QUndoStack( this ) )
97 , mUndoStackStyles( new QUndoStack( this ) )
98 , mStyleManager( std::make_unique<QgsMapLayerStyleManager>( this ) )
99 , mRefreshTimer( new QTimer( this ) )
100{
101 mID = generateId( lyrname );
102 connect( this, &QgsMapLayer::crsChanged, this, &QgsMapLayer::configChanged );
104 connect( mRefreshTimer, &QTimer::timeout, this, [this]
105 {
106
107 switch ( mAutoRefreshMode )
108 {
110 break;
112 triggerRepaint( true );
113 break;
115 reload();
116 break;
117 }
118 } );
119}
120
122{
123 if ( project() && project()->pathResolver().writePath( mDataSource ).startsWith( "attachment:" ) )
124 {
126 }
127
128
129
130
131}
132
133void QgsMapLayer::clone( QgsMapLayer *layer ) const
134{
136
137 QgsDebugMsgLevel( QStringLiteral( "Cloning layer '%1'" ).arg( name() ), 3 );
138 layer->setBlendMode( blendMode() );
139
140 const auto constStyles = styleManager()->styles();
141 for ( const QString &s : constStyles )
142 {
143 layer->styleManager()->addStyle( s, styleManager()->style( s ) );
144 }
145
146 layer->setName( name() );
147
148 if ( layer->dataProvider() && layer->dataProvider()->elevationProperties() )
149 {
151 layer->mExtent3D = mExtent3D;
152 else
153 layer->mExtent2D = mExtent2D;
154 }
155
156 layer->setMaximumScale( maximumScale() );
157 layer->setMinimumScale( minimumScale() );
159 layer->setDependencies( dependencies() );
161 layer->setCrs( crs() );
162 layer->setCustomProperties( mCustomProperties );
163 layer->setOpacity( mLayerOpacity );
164 layer->setMetadata( mMetadata );
165 layer->serverProperties()->copyTo( mServerProperties.get() );
166}
167
169{
170 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
172
173 return mLayerType;
174}
175
182
184{
186
187 if ( flags == mFlags )
188 return;
189
190 mFlags = flags;
191 emit flagsChanged();
192}
193
200
201QString QgsMapLayer::id() const
202{
203 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
205
206 return mID;
207}
208
209bool QgsMapLayer::setId( const QString &id )
210{
212 if ( qobject_cast< QgsMapLayerStore * >( parent() ) )
213 {
214 // layer is already registered, cannot change id
215 return false;
216 }
217
218 if ( id == mID )
219 return false;
220
221 mID = id;
222 emit idChanged( id );
223 return true;
224}
225
226void QgsMapLayer::setName( const QString &name )
227{
229
230 if ( name == mLayerName )
231 return;
232
234
235 emit nameChanged();
236}
237
238QString QgsMapLayer::name() const
239{
240 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
242
243 QgsDebugMsgLevel( "returning name '" + mLayerName + '\'', 4 );
244 return mLayerName;
245}
246
253
255{
257
258 return nullptr;
259}
260
265
267{
269
270 mServerProperties->setShortName( shortName );
271}
272
274{
276
277 return mServerProperties->shortName();
278}
279
280void QgsMapLayer::setTitle( const QString &title )
281{
283
284 mServerProperties->setTitle( title );
285}
286
287QString QgsMapLayer::title() const
288{
290
291 return mServerProperties->title();
292}
293
294void QgsMapLayer::setAbstract( const QString &abstract )
295{
297
298 mServerProperties->setAbstract( abstract );
299}
300
302{
304
305 return mServerProperties->abstract();
306}
307
308void QgsMapLayer::setKeywordList( const QString &keywords )
309{
311
312 mServerProperties->setKeywordList( keywords );
313}
314
316{
318
319 return mServerProperties->keywordList();
320}
321
322void QgsMapLayer::setDataUrl( const QString &dataUrl )
323{
325
326 mServerProperties->setDataUrl( dataUrl );
327}
328
329QString QgsMapLayer::dataUrl() const
330{
332
333 return mServerProperties->dataUrl();
334}
335
337{
339
340 mServerProperties->setDataUrlFormat( dataUrlFormat );
341}
342
344{
346
347 return mServerProperties->dataUrlFormat();
348}
349
350void QgsMapLayer::setAttribution( const QString &attrib )
351{
353
354 mServerProperties->setAttribution( attrib );
355}
356
358{
360
361 return mServerProperties->attribution();
362}
363
364void QgsMapLayer::setAttributionUrl( const QString &attribUrl )
365{
367
368 mServerProperties->setAttributionUrl( attribUrl );
369}
370
372{
374
375 return mServerProperties->attributionUrl();
376}
377
379{
381
382 mServerProperties->setLegendUrl( legendUrl );
383}
384
386{
388
389 return mServerProperties->legendUrl();
390}
391
393{
395
396 mServerProperties->setLegendUrlFormat( legendUrlFormat );
397}
398
400{
402
403 return mServerProperties->legendUrlFormat();
404}
405
406void QgsMapLayer::setMetadataUrl( const QString &metaUrl )
407{
409
410 QList<QgsMapLayerServerProperties::MetadataUrl> urls = serverProperties()->metadataUrls();
411 if ( urls.isEmpty() )
412 {
413 const QgsMapLayerServerProperties::MetadataUrl newItem = QgsMapLayerServerProperties::MetadataUrl( metaUrl, QLatin1String(), QLatin1String() );
414 urls.prepend( newItem );
415 }
416 else
417 {
418 const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst();
419 const QgsMapLayerServerProperties::MetadataUrl newItem( metaUrl, old.type, old.format );
420 urls.prepend( newItem );
421 }
423}
424
426{
428
429 if ( mServerProperties->metadataUrls().isEmpty() )
430 {
431 return QLatin1String();
432 }
433 else
434 {
435 return mServerProperties->metadataUrls().first().url;
436 }
437}
438
439void QgsMapLayer::setMetadataUrlType( const QString &metaUrlType )
440{
442
443 QList<QgsMapLayerServerProperties::MetadataUrl> urls = mServerProperties->metadataUrls();
444 if ( urls.isEmpty() )
445 {
446 const QgsMapLayerServerProperties::MetadataUrl newItem( QLatin1String(), metaUrlType, QLatin1String() );
447 urls.prepend( newItem );
448 }
449 else
450 {
451 const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst();
452 const QgsMapLayerServerProperties::MetadataUrl newItem( old.url, metaUrlType, old.format );
453 urls.prepend( newItem );
454 }
455 mServerProperties->setMetadataUrls( urls );
456}
457
459{
461
462 if ( mServerProperties->metadataUrls().isEmpty() )
463 {
464 return QLatin1String();
465 }
466 else
467 {
468 return mServerProperties->metadataUrls().first().type;
469 }
470}
471
472void QgsMapLayer::setMetadataUrlFormat( const QString &metaUrlFormat )
473{
475
476 QList<QgsMapLayerServerProperties::MetadataUrl> urls = mServerProperties->metadataUrls();
477 if ( urls.isEmpty() )
478 {
479 const QgsMapLayerServerProperties::MetadataUrl newItem( QLatin1String(), QLatin1String(), metaUrlFormat );
480 urls.prepend( newItem );
481 }
482 else
483 {
484 const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst( );
485 const QgsMapLayerServerProperties::MetadataUrl newItem( old.url, old.type, metaUrlFormat );
486 urls.prepend( newItem );
487 }
488 mServerProperties->setMetadataUrls( urls );
489}
490
492{
494
495 if ( mServerProperties->metadataUrls().isEmpty() )
496 {
497 return QString();
498 }
499 else
500 {
501 return mServerProperties->metadataUrls().first().format;
502 }
503}
504
505QString QgsMapLayer::publicSource( bool redactCredentials ) const
506{
508
509 // Redo this every time we're asked for it, as we don't know if
510 // dataSource has changed.
512 {
514 }
515 else
516 {
517 return QgsDataSourceUri::removePassword( mDataSource, redactCredentials );
518 }
519}
520
521QString QgsMapLayer::source() const
522{
524
525 return mDataSource;
526}
527
529{
531
532 return mExtent2D.isNull() ? mExtent3D.toRectangle() : mExtent2D;
533}
534
536{
538
539 return mExtent3D;
540}
541
542void QgsMapLayer::setBlendMode( const QPainter::CompositionMode blendMode )
543{
545
546 if ( mBlendMode == blendMode )
547 return;
548
549 mBlendMode = blendMode;
552}
553
554QPainter::CompositionMode QgsMapLayer::blendMode() const
555{
556 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
558
559 return mBlendMode;
560}
561
572
574{
575 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
577
578 return mLayerOpacity;
579}
580
581bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags, QgsDataProvider *preloadedProvider )
582{
584
585 mPreloadedProvider.reset( preloadedProvider );
586
587 bool layerError;
589
590 QDomNode mnl;
591 QDomElement mne;
592
593 // read provider
594 QString provider;
595 mnl = layerElement.namedItem( QStringLiteral( "provider" ) );
596 mne = mnl.toElement();
597 provider = mne.text();
598
599 // set data source
600 mnl = layerElement.namedItem( QStringLiteral( "datasource" ) );
601 mne = mnl.toElement();
602 const QString dataSourceRaw = mne.text();
603
604 // if the layer needs authentication, ensure the master password is set
605 const thread_local QRegularExpression rx( "authcfg=([a-z]|[A-Z]|[0-9]){7}" );
606 if ( rx.match( dataSourceRaw ).hasMatch()
607 && !QgsApplication::authManager()->setMasterPassword( true ) )
608 {
609 return false;
610 }
611
612 mDataSource = decodedSource( dataSourceRaw, provider, context );
613
614 // Set the CRS from project file, asking the user if necessary.
615 // Make it the saved CRS to have WMS layer projected correctly.
616 // We will still overwrite whatever GDAL etc picks up anyway
617 // further down this function.
618 mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
619 mne = mnl.toElement();
620
622 CUSTOM_CRS_VALIDATION savedValidation;
623
624 const QDomNode srsNode = layerElement.namedItem( QStringLiteral( "srs" ) );
625 mCRS.readXml( srsNode );
626 mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
628 mCRS.validate();
629 savedCRS = mCRS;
630
631 // Do not validate any projections in children, they will be overwritten anyway.
632 // No need to ask the user for a projections when it is overwritten, is there?
635
636 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Layer" ), mne.text() );
637
638 // the internal name is just the data source basename
639 //QFileInfo dataSourceFileInfo( mDataSource );
640 //internalName = dataSourceFileInfo.baseName();
641
642 // set ID
643 mnl = layerElement.namedItem( QStringLiteral( "id" ) );
644 if ( ! mnl.isNull() )
645 {
646 mne = mnl.toElement();
647 if ( ! mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
648 {
649 const QString newId = mne.text();
650 if ( newId != mID )
651 {
652 mID = mne.text();
653 emit idChanged( mID );
654 }
655 }
656 }
657
658 // set name
659 mnl = layerElement.namedItem( QStringLiteral( "layername" ) );
660 mne = mnl.toElement();
661
662 //name can be translated
663 setName( context.projectTranslator()->translate( QStringLiteral( "project:layers:%1" ).arg( layerElement.namedItem( QStringLiteral( "id" ) ).toElement().text() ), mne.text() ) );
664
665 // now let the children grab what they need from the Dom node.
666 layerError = !readXml( layerElement, context );
667
668 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
669 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
670
671 // overwrite CRS with what we read from project file before the raster/vector
672 // file reading functions changed it. They will if projections is specified in the file.
673 // FIXME: is this necessary? Yes, it is (autumn 2019)
675 mCRS = savedCRS;
676
677 //vertical CRS
678 {
680 const QDomNode verticalCrsNode = layerElement.firstChildElement( QStringLiteral( "verticalCrs" ) );
681 if ( !verticalCrsNode.isNull() )
682 {
683 verticalCrs.readXml( verticalCrsNode );
684 }
685 mVerticalCrs = verticalCrs;
686 }
687 rebuildCrs3D();
688
689 serverProperties()->readXml( layerElement );
690
691 // mMetadata.readFromLayer( this );
692 const QDomElement metadataElem = layerElement.firstChildElement( QStringLiteral( "resourceMetadata" ) );
693 mMetadata.readMetadataXml( metadataElem, context );
694
695 setAutoRefreshInterval( layerElement.attribute( QStringLiteral( "autoRefreshTime" ), QStringLiteral( "0" ) ).toInt() );
696 if ( layerElement.hasAttribute( QStringLiteral( "autoRefreshMode" ) ) )
697 {
698 setAutoRefreshMode( qgsEnumKeyToValue( layerElement.attribute( QStringLiteral( "autoRefreshMode" ) ), Qgis::AutoRefreshMode::Disabled ) );
699 }
700 else
701 {
702 setAutoRefreshMode( layerElement.attribute( QStringLiteral( "autoRefreshEnabled" ), QStringLiteral( "0" ) ).toInt() ? Qgis::AutoRefreshMode::RedrawOnly : Qgis::AutoRefreshMode::Disabled );
703 }
704 setRefreshOnNofifyMessage( layerElement.attribute( QStringLiteral( "refreshOnNotifyMessage" ), QString() ) );
705 setRefreshOnNotifyEnabled( layerElement.attribute( QStringLiteral( "refreshOnNotifyEnabled" ), QStringLiteral( "0" ) ).toInt() );
706
707 // geographic extent is read only if necessary
709 {
710 const QDomNode wgs84ExtentNode = layerElement.namedItem( QStringLiteral( "wgs84extent" ) );
711 if ( !wgs84ExtentNode.isNull() )
712 mWgs84Extent = QgsXmlUtils::readRectangle( wgs84ExtentNode.toElement() );
713 }
714
715 mLegendPlaceholderImage = layerElement.attribute( QStringLiteral( "legendPlaceholderImage" ) );
716
717 if ( verticalCrs() != oldVerticalCrs )
718 emit verticalCrsChanged();
719 if ( mCrs3D != oldCrs3D )
720 emit crs3DChanged();
721
722 return ! layerError;
723} // bool QgsMapLayer::readLayerXML
724
725
726bool QgsMapLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
727{
729
730 Q_UNUSED( layer_node )
731 Q_UNUSED( context )
732 // NOP by default; children will over-ride with behavior specific to them
733
734 // read Extent
736 {
737 const QDomNode extent3DNode = layer_node.namedItem( QStringLiteral( "extent3D" ) );
738 if ( extent3DNode.isNull() )
739 {
740 const QDomNode extentNode = layer_node.namedItem( QStringLiteral( "extent" ) );
741 if ( !extentNode.isNull() )
742 {
743 mExtent2D = QgsXmlUtils::readRectangle( extentNode.toElement() );
744 }
745 }
746 else
747 {
748 mExtent3D = QgsXmlUtils::readBox3D( extent3DNode.toElement() );
749 }
750 }
751
752 return true;
753} // void QgsMapLayer::readXml
754
755
756bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context ) const
757{
759
760 if ( !mExtent3D.isNull() && dataProvider() && dataProvider()->elevationProperties() && dataProvider()->elevationProperties()->containsElevationData() )
761 layerElement.appendChild( QgsXmlUtils::writeBox3D( mExtent3D, document ) );
762 else
763 {
764 // Extent might be null because lazily set
765 const QgsRectangle extent2D { mExtent2D.isNull() ? extent() : mExtent2D };
766 if ( !extent2D.isNull() )
767 {
768 layerElement.appendChild( QgsXmlUtils::writeRectangle( extent2D, document ) );
769 }
770 }
771
772 if ( const QgsRectangle lWgs84Extent = wgs84Extent( true ); !lWgs84Extent.isNull() )
773 {
774 layerElement.appendChild( QgsXmlUtils::writeRectangle( lWgs84Extent, document, QStringLiteral( "wgs84extent" ) ) );
775 }
776
777 layerElement.setAttribute( QStringLiteral( "autoRefreshTime" ), QString::number( mRefreshTimer->interval() ) );
778 layerElement.setAttribute( QStringLiteral( "autoRefreshMode" ), qgsEnumValueToKey( mAutoRefreshMode ) );
779 layerElement.setAttribute( QStringLiteral( "refreshOnNotifyEnabled" ), mIsRefreshOnNofifyEnabled ? 1 : 0 );
780 layerElement.setAttribute( QStringLiteral( "refreshOnNotifyMessage" ), mRefreshOnNofifyMessage );
781
782 // ID
783 QDomElement layerId = document.createElement( QStringLiteral( "id" ) );
784 const QDomText layerIdText = document.createTextNode( id() );
785 layerId.appendChild( layerIdText );
786
787 layerElement.appendChild( layerId );
788
789 if ( mVerticalCrs.isValid() )
790 {
791 QDomElement verticalSrsNode = document.createElement( QStringLiteral( "verticalCrs" ) );
792 mVerticalCrs.writeXml( verticalSrsNode, document );
793 layerElement.appendChild( verticalSrsNode );
794 }
795
796 // data source
797 QDomElement dataSource = document.createElement( QStringLiteral( "datasource" ) );
798 const QString src = encodedSource( source(), context );
799 const QDomText dataSourceText = document.createTextNode( src );
800 dataSource.appendChild( dataSourceText );
801 layerElement.appendChild( dataSource );
802
803 // layer name
804 QDomElement layerName = document.createElement( QStringLiteral( "layername" ) );
805 const QDomText layerNameText = document.createTextNode( name() );
806 layerName.appendChild( layerNameText );
807 layerElement.appendChild( layerName );
808
809 // timestamp if supported
810 if ( timestamp() > QDateTime() )
811 {
812 QDomElement stamp = document.createElement( QStringLiteral( "timestamp" ) );
813 const QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
814 stamp.appendChild( stampText );
815 layerElement.appendChild( stamp );
816 }
817
818 layerElement.appendChild( layerName );
819
820 // zorder
821 // This is no longer stored in the project file. It is superfluous since the layers
822 // are written and read in the proper order.
823
824 // spatial reference system id
825 QDomElement mySrsElement = document.createElement( QStringLiteral( "srs" ) );
826 mCRS.writeXml( mySrsElement, document );
827 layerElement.appendChild( mySrsElement );
828
829 // layer metadata
830 QDomElement myMetadataElem = document.createElement( QStringLiteral( "resourceMetadata" ) );
831 mMetadata.writeMetadataXml( myMetadataElem, document );
832 layerElement.appendChild( myMetadataElem );
833
834 layerElement.setAttribute( QStringLiteral( "legendPlaceholderImage" ), mLegendPlaceholderImage );
835
836 serverProperties()->writeXml( layerElement, document );
837
838 // now append layer node to map layer node
839 return writeXml( layerElement, document, context );
840}
841
842void QgsMapLayer::writeCommonStyle( QDomElement &layerElement, QDomDocument &document,
843 const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
844{
846
847 // save categories
848 const QMetaEnum metaEnum = QMetaEnum::fromType<QgsMapLayer::StyleCategories>();
849 const QString categoriesKeys( metaEnum.valueToKeys( static_cast<int>( categories ) ) );
850 layerElement.setAttribute( QStringLiteral( "styleCategories" ), categoriesKeys );
851
852 // Store layer type
853 layerElement.setAttribute( QStringLiteral( "layerType" ), qgsEnumValueToKey( type() ) );
854
855 if ( categories.testFlag( Rendering ) )
856 {
857 // use scale dependent visibility flag
858 layerElement.setAttribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ), hasScaleBasedVisibility() ? 1 : 0 );
859 layerElement.setAttribute( QStringLiteral( "maxScale" ), QString::number( maximumScale() ) );
860 layerElement.setAttribute( QStringLiteral( "minScale" ), QString::number( minimumScale() ) );
861 layerElement.setAttribute( QStringLiteral( "autoRefreshMode" ), qgsEnumValueToKey( mAutoRefreshMode ) );
862 layerElement.setAttribute( QStringLiteral( "autoRefreshTime" ), QString::number( autoRefreshInterval() ) );
863 }
864
865 if ( categories.testFlag( Symbology3D ) )
866 {
867 if ( m3DRenderer )
868 {
869 QDomElement renderer3DElem = document.createElement( QStringLiteral( "renderer-3d" ) );
870 renderer3DElem.setAttribute( QStringLiteral( "type" ), m3DRenderer->type() );
871 m3DRenderer->writeXml( renderer3DElem, context );
872 layerElement.appendChild( renderer3DElem );
873 }
874 }
875
876 if ( categories.testFlag( LayerConfiguration ) )
877 {
878 // flags
879 // this code is saving automatically all the flags entries
880 QDomElement layerFlagsElem = document.createElement( QStringLiteral( "flags" ) );
881 const auto enumMap = qgsEnumMap<QgsMapLayer::LayerFlag>();
882 for ( auto it = enumMap.constBegin(); it != enumMap.constEnd(); ++it )
883 {
884 const bool flagValue = mFlags.testFlag( it.key() );
885 QDomElement flagElem = document.createElement( it.value() );
886 flagElem.appendChild( document.createTextNode( QString::number( flagValue ) ) );
887 layerFlagsElem.appendChild( flagElem );
888 }
889 layerElement.appendChild( layerFlagsElem );
890 }
891
892 if ( categories.testFlag( Temporal ) )
893 {
895 properties->writeXml( layerElement, document, context );
896 }
897
898 if ( categories.testFlag( Elevation ) )
899 {
901 properties->writeXml( layerElement, document, context );
902 }
903
904 if ( categories.testFlag( Notes ) && QgsLayerNotesUtils::layerHasNotes( this ) )
905 {
906 QDomElement notesElem = document.createElement( QStringLiteral( "userNotes" ) );
907 notesElem.setAttribute( QStringLiteral( "value" ), QgsLayerNotesUtils::layerNotes( this ) );
908 layerElement.appendChild( notesElem );
909 }
910
911 // custom properties
912 if ( categories.testFlag( CustomProperties ) )
913 {
914 writeCustomProperties( layerElement, document );
915 }
916}
917
918
919bool QgsMapLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
920{
922
923 Q_UNUSED( layer_node )
924 Q_UNUSED( document )
925 Q_UNUSED( context )
926 // NOP by default; children will over-ride with behavior specific to them
927
928 return true;
929}
930
931QString QgsMapLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
932{
934
935 Q_UNUSED( context )
936 return source;
937}
938
939QString QgsMapLayer::decodedSource( const QString &source, const QString &dataProvider, const QgsReadWriteContext &context ) const
940{
942
943 Q_UNUSED( context )
944 Q_UNUSED( dataProvider )
945 return source;
946}
947
949{
951
953 if ( m3DRenderer )
954 m3DRenderer->resolveReferences( *project );
955}
956
957
958void QgsMapLayer::readCustomProperties( const QDomNode &layerNode, const QString &keyStartsWith )
959{
961
962 const QgsObjectCustomProperties oldKeys = mCustomProperties;
963
964 mCustomProperties.readXml( layerNode, keyStartsWith );
965
966 for ( const QString &key : mCustomProperties.keys() )
967 {
968 if ( !oldKeys.contains( key ) || mCustomProperties.value( key ) != oldKeys.value( key ) )
969 {
970 emit customPropertyChanged( key );
971 }
972 }
973}
974
975void QgsMapLayer::writeCustomProperties( QDomNode &layerNode, QDomDocument &doc ) const
976{
978
979 mCustomProperties.writeXml( layerNode, doc );
980}
981
982void QgsMapLayer::readStyleManager( const QDomNode &layerNode )
983{
985
986 const QDomElement styleMgrElem = layerNode.firstChildElement( QStringLiteral( "map-layer-style-manager" ) );
987 if ( !styleMgrElem.isNull() )
988 mStyleManager->readXml( styleMgrElem );
989 else
990 mStyleManager->reset();
991}
992
993void QgsMapLayer::writeStyleManager( QDomNode &layerNode, QDomDocument &doc ) const
994{
996
997 if ( mStyleManager )
998 {
999 QDomElement styleMgrElem = doc.createElement( QStringLiteral( "map-layer-style-manager" ) );
1000 mStyleManager->writeXml( styleMgrElem );
1001 layerNode.appendChild( styleMgrElem );
1002 }
1003}
1004
1006{
1008
1009 return mMapTipTemplate;
1010}
1011
1012void QgsMapLayer::setMapTipTemplate( const QString &mapTip )
1013{
1015
1016 if ( mMapTipTemplate == mapTip )
1017 return;
1018
1019 mMapTipTemplate = mapTip;
1020 emit mapTipTemplateChanged();
1021}
1022
1024{
1026
1027 if ( mMapTipsEnabled == enabled )
1028 return;
1029
1030 mMapTipsEnabled = enabled;
1031 emit mapTipsEnabledChanged();
1032}
1033
1035{
1037
1038 return mMapTipsEnabled;
1039}
1040
1042{
1044 if ( layerReadFlags & QgsMapLayer::FlagTrustLayerMetadata )
1045 {
1047 }
1048 if ( layerReadFlags & QgsMapLayer::FlagForceReadOnly )
1049 {
1051 }
1052
1053 if ( layerReadFlags & QgsMapLayer::FlagReadExtentFromXml )
1054 {
1055 const QDomNode extent3DNode = layerNode.namedItem( QStringLiteral( "extent3D" ) );
1056 if ( extent3DNode.isNull() )
1057 {
1058 const QDomNode extentNode = layerNode.namedItem( QStringLiteral( "extent" ) );
1059 if ( !extentNode.isNull() )
1060 {
1062 }
1063 }
1064 else
1065 {
1067 }
1068 }
1069
1070 return flags;
1071}
1072
1074{
1075 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
1077
1078 return mValid;
1079}
1080
1081#if 0
1082void QgsMapLayer::connectNotify( const char *signal )
1083{
1084 Q_UNUSED( signal )
1085 QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
1086} // QgsMapLayer::connectNotify
1087#endif
1088
1089bool QgsMapLayer::isInScaleRange( double scale ) const
1090{
1091 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1093
1094 // mMinScale (denominator!) is inclusive ( >= --> In range )
1095 // mMaxScale (denominator!) is exclusive ( < --> In range )
1096 return !mScaleBasedVisibility
1097 || ( ( mMinScale == 0 || !QgsScaleUtils::lessThanMaximumScale( scale, mMinScale ) )
1098 && ( mMaxScale == 0 || !QgsScaleUtils::equalToOrGreaterThanMinimumScale( scale, mMaxScale ) ) );
1099}
1100
1102{
1103 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1105
1106 return mScaleBasedVisibility;
1107}
1108
1110{
1112
1113 return mAutoRefreshMode != Qgis::AutoRefreshMode::Disabled;;
1114}
1115
1117{
1119
1120 return mAutoRefreshMode;
1121}
1122
1124{
1126
1127 return mRefreshTimer->interval();
1128}
1129
1131{
1133
1134 if ( interval <= 0 )
1135 {
1136 mRefreshTimer->stop();
1137 mRefreshTimer->setInterval( 0 );
1139 }
1140 else
1141 {
1142 mRefreshTimer->setInterval( interval );
1143 }
1144 emit autoRefreshIntervalChanged( mRefreshTimer->isActive() ? mRefreshTimer->interval() : 0 );
1145}
1146
1153
1155{
1157
1158 if ( mode == mAutoRefreshMode )
1159 return;
1160
1161 mAutoRefreshMode = mode;
1162 switch ( mAutoRefreshMode )
1163 {
1165 mRefreshTimer->stop();
1166 break;
1167
1170 if ( mRefreshTimer->interval() > 0 )
1171 mRefreshTimer->start();
1172 break;
1173 }
1174
1175 emit autoRefreshIntervalChanged( mRefreshTimer->isActive() ? mRefreshTimer->interval() : 0 );
1176}
1177
1179{
1181
1182 return mMetadata;
1183}
1184
1186{
1188
1189 mMinScale = scale;
1190}
1191
1193{
1195
1196 return mMinScale;
1197}
1198
1200{
1202
1203 mMaxScale = scale;
1204}
1205
1207{
1209
1210 mScaleBasedVisibility = enabled;
1211}
1212
1214{
1216
1217 return mMaxScale;
1218}
1219
1220QStringList QgsMapLayer::subLayers() const
1221{
1223
1224 return QStringList();
1225}
1226
1227void QgsMapLayer::setLayerOrder( const QStringList &layers )
1228{
1230
1231 Q_UNUSED( layers )
1232}
1233
1234void QgsMapLayer::setSubLayerVisibility( const QString &name, bool vis )
1235{
1237
1238 Q_UNUSED( name )
1239 Q_UNUSED( vis )
1240}
1241
1243{
1245
1246 return false;
1247}
1248
1250{
1251 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1253
1254 return mCRS;
1255}
1256
1258{
1259 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1261
1262 switch ( mCRS.type() )
1263 {
1264 case Qgis::CrsType::Vertical: // would hope this never happens!
1265 QgsDebugError( QStringLiteral( "Layer has a vertical CRS set as the horizontal CRS!" ) );
1266 return mCRS;
1267
1269 return mCRS.verticalCrs();
1270
1282 break;
1283 }
1284 return mVerticalCrs;
1285}
1286
1288{
1290
1291 return mCrs3D.isValid() ? mCrs3D : mCRS;
1292}
1293
1294void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem &srs, bool emitSignal )
1295{
1297 const bool needToValidateCrs = mShouldValidateCrs && isSpatial() && !srs.isValid() && type() != Qgis::LayerType::Annotation;
1298
1299 if ( mCRS == srs && !needToValidateCrs )
1300 return;
1301
1302 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
1303 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
1304 const QgsCoordinateReferenceSystem oldCrs = mCRS;
1305
1306 mCRS = srs;
1307
1308 if ( needToValidateCrs )
1309 {
1310 mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( name() ) );
1311 mCRS.validate();
1312 }
1313
1314 rebuildCrs3D();
1315
1316 if ( emitSignal && mCRS != oldCrs )
1317 emit crsChanged();
1318
1319 // Did vertical crs also change as a result of this? If so, emit signal
1320 if ( oldVerticalCrs != verticalCrs() )
1321 emit verticalCrsChanged();
1322 if ( oldCrs3D != mCrs3D )
1323 emit crs3DChanged();
1324}
1325
1327{
1329 bool res = true;
1330 if ( crs.isValid() )
1331 {
1332 // validate that passed crs is a vertical crs
1333 switch ( crs.type() )
1334 {
1336 break;
1337
1350 if ( errorMessage )
1351 *errorMessage = QObject::tr( "Specified CRS is a %1 CRS, not a Vertical CRS" ).arg( qgsEnumValueToKey( crs.type() ) );
1352 return false;
1353 }
1354 }
1355
1356 if ( crs != mVerticalCrs )
1357 {
1358 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
1359 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
1360
1361 switch ( mCRS.type() )
1362 {
1364 if ( crs != oldVerticalCrs )
1365 {
1366 if ( errorMessage )
1367 *errorMessage = QObject::tr( "Layer CRS is a Compound CRS, specified Vertical CRS will be ignored" );
1368 return false;
1369 }
1370 break;
1371
1373 if ( crs != oldVerticalCrs )
1374 {
1375 if ( errorMessage )
1376 *errorMessage = QObject::tr( "Layer CRS is a Geographic 3D CRS, specified Vertical CRS will be ignored" );
1377 return false;
1378 }
1379 break;
1380
1382 if ( crs != oldVerticalCrs )
1383 {
1384 if ( errorMessage )
1385 *errorMessage = QObject::tr( "Layer CRS is a Geocentric CRS, specified Vertical CRS will be ignored" );
1386 return false;
1387 }
1388 break;
1389
1391 if ( mCRS.hasVerticalAxis() && crs != oldVerticalCrs )
1392 {
1393 if ( errorMessage )
1394 *errorMessage = QObject::tr( "Layer CRS is a Projected 3D CRS, specified Vertical CRS will be ignored" );
1395 return false;
1396 }
1397 break;
1398
1408 break;
1409 }
1410
1411 mVerticalCrs = crs;
1412 res = rebuildCrs3D( errorMessage );
1413
1414 // only emit signal if vertical crs was actually changed, so eg if mCrs is compound
1415 // then we haven't actually changed the vertical crs by this call!
1416 if ( verticalCrs() != oldVerticalCrs )
1417 emit verticalCrsChanged();
1418 if ( mCrs3D != oldCrs3D )
1419 emit crs3DChanged();
1420 }
1421 return res;
1422}
1423
1425{
1427
1428 const QgsDataProvider *lDataProvider = dataProvider();
1429 return lDataProvider ? lDataProvider->transformContext() : QgsCoordinateTransformContext();
1430}
1431
1432QString QgsMapLayer::formatLayerName( const QString &name )
1433{
1434 QString layerName( name );
1435 layerName.replace( '_', ' ' );
1437 return layerName;
1438}
1439
1440QString QgsMapLayer::baseURI( PropertyType type ) const
1441{
1443
1444 QString myURI = publicSource();
1445
1446 // first get base path for delimited text, spatialite and OGR layers,
1447 // as in these cases URI may contain layer name and/or additional
1448 // information. This also strips prefix in case if VSIFILE mechanism
1449 // is used
1450 if ( providerType() == QLatin1String( "ogr" ) || providerType() == QLatin1String( "delimitedtext" )
1451 || providerType() == QLatin1String( "gdal" ) || providerType() == QLatin1String( "spatialite" ) )
1452 {
1453 QVariantMap components = QgsProviderRegistry::instance()->decodeUri( providerType(), myURI );
1454 myURI = components["path"].toString();
1455 }
1456
1457 QFileInfo myFileInfo( myURI );
1458 QString key;
1459
1460 if ( myFileInfo.exists() )
1461 {
1462 // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name
1463 if ( myURI.endsWith( QLatin1String( ".gz" ), Qt::CaseInsensitive ) )
1464 myURI.chop( 3 );
1465 else if ( myURI.endsWith( QLatin1String( ".zip" ), Qt::CaseInsensitive ) )
1466 myURI.chop( 4 );
1467 else if ( myURI.endsWith( QLatin1String( ".tar" ), Qt::CaseInsensitive ) )
1468 myURI.chop( 4 );
1469 else if ( myURI.endsWith( QLatin1String( ".tar.gz" ), Qt::CaseInsensitive ) )
1470 myURI.chop( 7 );
1471 else if ( myURI.endsWith( QLatin1String( ".tgz" ), Qt::CaseInsensitive ) )
1472 myURI.chop( 4 );
1473 myFileInfo.setFile( myURI );
1474 // get the file name for our .qml style file
1475 key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
1476 }
1477 else
1478 {
1479 key = publicSource();
1480 }
1481
1482 return key;
1483}
1484
1486{
1488
1489 return baseURI( PropertyType::Metadata );
1490}
1491
1492QString QgsMapLayer::saveDefaultMetadata( bool &resultFlag )
1493{
1495
1497 {
1498 if ( metadata->providerCapabilities() & QgsProviderMetadata::SaveLayerMetadata )
1499 {
1500 try
1501 {
1502 QString errorMessage;
1503 resultFlag = QgsProviderRegistry::instance()->saveLayerMetadata( providerType(), mDataSource, mMetadata, errorMessage );
1504 if ( resultFlag )
1505 return tr( "Successfully saved default layer metadata" );
1506 else
1507 return errorMessage;
1508 }
1509 catch ( QgsNotSupportedException &e )
1510 {
1511 resultFlag = false;
1512 return e.what();
1513 }
1514 }
1515 }
1516
1517 // fallback default metadata saving method, for providers which don't support (or implement) saveLayerMetadata
1518 return saveNamedMetadata( metadataUri(), resultFlag );
1519}
1520
1521QString QgsMapLayer::loadDefaultMetadata( bool &resultFlag )
1522{
1524
1525 return loadNamedMetadata( metadataUri(), resultFlag );
1526}
1527
1529{
1531
1532 return baseURI( PropertyType::Style );
1533}
1534
1541
1542bool QgsMapLayer::loadNamedMetadataFromDatabase( const QString &db, const QString &uri, QString &qmd )
1543{
1545
1546 return loadNamedPropertyFromDatabase( db, uri, qmd, PropertyType::Metadata );
1547}
1548
1549bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &uri, QString &qml )
1550{
1552
1553 return loadNamedPropertyFromDatabase( db, uri, qml, PropertyType::Style );
1554}
1555
1556bool QgsMapLayer::loadNamedPropertyFromDatabase( const QString &db, const QString &uri, QString &xml, QgsMapLayer::PropertyType type )
1557{
1559
1560 QgsDebugMsgLevel( QStringLiteral( "db = %1 uri = %2" ).arg( db, uri ), 4 );
1561
1562 bool resultFlag = false;
1563
1564 // read from database
1567
1568 int myResult;
1569
1570 QgsDebugMsgLevel( QStringLiteral( "Trying to load style or metadata for \"%1\" from \"%2\"" ).arg( uri, db ), 4 );
1571
1572 if ( db.isEmpty() || !QFile( db ).exists() )
1573 return false;
1574
1575 myResult = database.open_v2( db, SQLITE_OPEN_READONLY, nullptr );
1576 if ( myResult != SQLITE_OK )
1577 {
1578 return false;
1579 }
1580
1581 QString mySql;
1582 switch ( type )
1583 {
1584 case Metadata:
1585 mySql = QStringLiteral( "select qmd from tbl_metadata where metadata=?" );
1586 break;
1587
1588 case Style:
1589 mySql = QStringLiteral( "select qml from tbl_styles where style=?" );
1590 break;
1591 }
1592
1593 statement = database.prepare( mySql, myResult );
1594 if ( myResult == SQLITE_OK )
1595 {
1596 QByteArray param = uri.toUtf8();
1597
1598 if ( sqlite3_bind_text( statement.get(), 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK &&
1599 sqlite3_step( statement.get() ) == SQLITE_ROW )
1600 {
1601 xml = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( statement.get(), 0 ) ) );
1602 resultFlag = true;
1603 }
1604 }
1605 return resultFlag;
1606}
1607
1608
1609QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories, Qgis::LoadStyleFlags flags )
1610{
1612
1613 return loadNamedStyle( uri, resultFlag, false, categories, flags );
1614}
1615
1616QString QgsMapLayer::loadNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &namedPropertyExists, bool &propertySuccessfullyLoaded, StyleCategories categories, Qgis::LoadStyleFlags flags )
1617{
1619
1620 QgsDebugMsgLevel( QStringLiteral( "uri = %1 myURI = %2" ).arg( uri, publicSource() ), 4 );
1621
1622 namedPropertyExists = false;
1623 propertySuccessfullyLoaded = false;
1624 if ( uri.isEmpty() )
1625 return QString();
1626
1627 QDomDocument myDocument( QStringLiteral( "qgis" ) );
1628
1629 // location of problem associated with errorMsg
1630 int line, column;
1631 QString myErrorMessage;
1632
1633 QFile myFile( uri );
1634 if ( myFile.open( QFile::ReadOnly ) )
1635 {
1636 QgsDebugMsgLevel( QStringLiteral( "file found %1" ).arg( uri ), 2 );
1637 namedPropertyExists = true;
1638
1639 // read file
1640 propertySuccessfullyLoaded = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
1641 if ( !propertySuccessfullyLoaded )
1642 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1643 myFile.close();
1644 }
1645 else
1646 {
1647 const QFileInfo project( QgsProject::instance()->fileName() ); // skip-keyword-check
1648 QgsDebugMsgLevel( QStringLiteral( "project fileName: %1" ).arg( project.absoluteFilePath() ), 4 );
1649
1650 QString xml;
1651 switch ( type )
1652 {
1653 case QgsMapLayer::Style:
1654 {
1655 if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, xml ) ||
1656 ( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) ) ||
1657 loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, xml ) )
1658 {
1659 namedPropertyExists = true;
1660 propertySuccessfullyLoaded = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1661 if ( !propertySuccessfullyLoaded )
1662 {
1663 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1664 }
1665 }
1666 else
1667 {
1669 {
1670 myErrorMessage = tr( "Style not found in database" );
1671 }
1672 }
1673 break;
1674 }
1676 {
1677 if ( loadNamedMetadataFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ), uri, xml ) ||
1678 ( project.exists() && loadNamedMetadataFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) ) ||
1679 loadNamedMetadataFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( QStringLiteral( "resources/qgis.qmldb" ) ), uri, xml ) )
1680 {
1681 namedPropertyExists = true;
1682 propertySuccessfullyLoaded = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1683 if ( !propertySuccessfullyLoaded )
1684 {
1685 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1686 }
1687 }
1688 else
1689 {
1690 myErrorMessage = tr( "Metadata not found in database" );
1691 }
1692 break;
1693 }
1694 }
1695 }
1696
1697 if ( !propertySuccessfullyLoaded )
1698 {
1699 return myErrorMessage;
1700 }
1701
1702 switch ( type )
1703 {
1704 case QgsMapLayer::Style:
1705 propertySuccessfullyLoaded = importNamedStyle( myDocument, myErrorMessage, categories );
1706 if ( !propertySuccessfullyLoaded )
1707 myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1708 break;
1710 propertySuccessfullyLoaded = importNamedMetadata( myDocument, myErrorMessage );
1711 if ( !propertySuccessfullyLoaded )
1712 myErrorMessage = tr( "Loading metadata file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1713 break;
1714 }
1715 return myErrorMessage;
1716}
1717
1718bool QgsMapLayer::importNamedMetadata( QDomDocument &document, QString &errorMessage )
1719{
1721
1722 const QDomElement myRoot = document.firstChildElement( QStringLiteral( "qgis" ) );
1723 if ( myRoot.isNull() )
1724 {
1725 errorMessage = tr( "Root <qgis> element could not be found" );
1726 return false;
1727 }
1728
1729 return mMetadata.readMetadataXml( myRoot );
1730}
1731
1732bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMessage, QgsMapLayer::StyleCategories categories )
1733{
1735
1736 const QDomElement myRoot = myDocument.firstChildElement( QStringLiteral( "qgis" ) );
1737 if ( myRoot.isNull() )
1738 {
1739 myErrorMessage = tr( "Root <qgis> element could not be found" );
1740 return false;
1741 }
1742
1743 // get style file version string, if any
1744 const QgsProjectVersion fileVersion( myRoot.attribute( QStringLiteral( "version" ) ) );
1745 const QgsProjectVersion thisVersion( Qgis::version() );
1746
1747 if ( thisVersion > fileVersion )
1748 {
1749 QgsProjectFileTransform styleFile( myDocument, fileVersion );
1750 styleFile.updateRevision( thisVersion );
1751 }
1752
1753 // Get source categories
1754 const QgsMapLayer::StyleCategories sourceCategories = QgsXmlUtils::readFlagAttribute( myRoot, QStringLiteral( "styleCategories" ), QgsMapLayer::AllStyleCategories );
1755
1756 //Test for matching geometry type on vector layers when applying, if geometry type is given in the style
1757 if ( ( sourceCategories.testFlag( QgsMapLayer::Symbology ) || sourceCategories.testFlag( QgsMapLayer::Symbology3D ) ) &&
1758 ( categories.testFlag( QgsMapLayer::Symbology ) || categories.testFlag( QgsMapLayer::Symbology3D ) ) )
1759 {
1760 if ( type() == Qgis::LayerType::Vector && !myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).isNull() )
1761 {
1762 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( this );
1763 const Qgis::GeometryType importLayerGeometryType = static_cast<Qgis::GeometryType>( myRoot.firstChildElement( QStringLiteral( "layerGeometryType" ) ).text().toInt() );
1764 if ( importLayerGeometryType != Qgis::GeometryType::Unknown && vl->geometryType() != importLayerGeometryType )
1765 {
1766 myErrorMessage = tr( "Cannot apply style with symbology to layer with a different geometry type" );
1767 return false;
1768 }
1769 }
1770 }
1771
1772 // Pass the intersection between the desired categories and those that are really in the document
1774 return readSymbology( myRoot, myErrorMessage, context, categories & sourceCategories ); // TODO: support relative paths in QML?
1775}
1776
1777void QgsMapLayer::exportNamedMetadata( QDomDocument &doc, QString &errorMsg ) const
1778{
1780
1781 QDomImplementation DomImplementation;
1782 const QDomDocumentType documentType = DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1783 QDomDocument myDocument( documentType );
1784
1785 QDomElement myRootNode = myDocument.createElement( QStringLiteral( "qgis" ) );
1786 myRootNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
1787 myDocument.appendChild( myRootNode );
1788
1789 if ( !mMetadata.writeMetadataXml( myRootNode, myDocument ) )
1790 {
1791 errorMsg = QObject::tr( "Could not save metadata" );
1792 return;
1793 }
1794
1795 doc = myDocument;
1796}
1797
1798void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1799{
1801
1802 QDomImplementation DomImplementation;
1803 const QDomDocumentType documentType = DomImplementation.createDocumentType( QStringLiteral( "qgis" ), QStringLiteral( "http://mrcc.com/qgis.dtd" ), QStringLiteral( "SYSTEM" ) );
1804 QDomDocument myDocument( documentType );
1805
1806 QDomElement myRootNode = myDocument.createElement( QStringLiteral( "qgis" ) );
1807 myRootNode.setAttribute( QStringLiteral( "version" ), Qgis::version() );
1808 myDocument.appendChild( myRootNode );
1809
1810 if ( !writeSymbology( myRootNode, myDocument, errorMsg, context, categories ) ) // TODO: support relative paths in QML?
1811 {
1812 errorMsg = QObject::tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1813 return;
1814 }
1815
1816 /*
1817 * Check to see if the layer is vector - in which case we should also export its geometryType
1818 * to avoid eventually pasting to a layer with a different geometry
1819 */
1820 if ( type() == Qgis::LayerType::Vector )
1821 {
1822 //Getting the selectionLayer geometry
1823 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( this );
1824 const QString geoType = QString::number( static_cast<int>( vl->geometryType() ) );
1825
1826 //Adding geometryinformation
1827 QDomElement layerGeometryType = myDocument.createElement( QStringLiteral( "layerGeometryType" ) );
1828 const QDomText type = myDocument.createTextNode( geoType );
1829
1830 layerGeometryType.appendChild( type );
1831 myRootNode.appendChild( layerGeometryType );
1832 }
1833
1834 doc = myDocument;
1835}
1836
1837QString QgsMapLayer::saveDefaultStyle( bool &resultFlag )
1838{
1840
1841 return saveDefaultStyle( resultFlag, AllStyleCategories );
1842}
1843
1844QString QgsMapLayer::saveDefaultStyle( bool &resultFlag, StyleCategories categories )
1845{
1847
1848 return saveNamedStyle( styleURI(), resultFlag, categories );
1849}
1850
1851QString QgsMapLayer::saveNamedMetadata( const QString &uri, bool &resultFlag )
1852{
1854
1855 return saveNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
1856}
1857
1858QString QgsMapLayer::loadNamedMetadata( const QString &uri, bool &resultFlag )
1859{
1861
1862 bool metadataExists = false;
1863 bool metadataSuccessfullyLoaded = false;
1864 const QString message = loadNamedProperty( uri, QgsMapLayer::Metadata, metadataExists, metadataSuccessfullyLoaded );
1865
1866 // TODO QGIS 4.0 -- fix API for loadNamedMetadata so we can return metadataExists too
1867 ( void )metadataExists;
1868 resultFlag = metadataSuccessfullyLoaded;
1869 return message;
1870}
1871
1872QString QgsMapLayer::saveNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag, StyleCategories categories )
1873{
1875
1876 // check if the uri is a file or ends with .qml/.qmd,
1877 // which indicates that it should become one
1878 // everything else goes to the database
1879 QString filename;
1880
1881 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1882 if ( vlayer && vlayer->providerType() == QLatin1String( "ogr" ) )
1883 {
1884 QStringList theURIParts = uri.split( '|' );
1885 filename = theURIParts[0];
1886 }
1887 else if ( vlayer && vlayer->providerType() == QLatin1String( "gpx" ) )
1888 {
1889 QStringList theURIParts = uri.split( '?' );
1890 filename = theURIParts[0];
1891 }
1892 else if ( vlayer && vlayer->providerType() == QLatin1String( "delimitedtext" ) )
1893 {
1894 filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
1895 // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
1896 if ( filename.isEmpty() )
1897 filename = uri;
1898 }
1899 else
1900 {
1901 filename = uri;
1902 }
1903
1904 QString myErrorMessage;
1905 QDomDocument myDocument;
1906 switch ( type )
1907 {
1908 case Metadata:
1909 exportNamedMetadata( myDocument, myErrorMessage );
1910 break;
1911
1912 case Style:
1913 const QgsReadWriteContext context;
1914 exportNamedStyle( myDocument, myErrorMessage, context, categories );
1915 break;
1916 }
1917
1918 const QFileInfo myFileInfo( filename );
1919 if ( myFileInfo.exists() || filename.endsWith( QgsMapLayer::extensionPropertyType( type ), Qt::CaseInsensitive ) )
1920 {
1921 const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1922 if ( !myDirInfo.isWritable() )
1923 {
1924 resultFlag = false;
1925 return tr( "The directory containing your dataset needs to be writable!" );
1926 }
1927
1928 // now construct the file name for our .qml or .qmd file
1929 const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
1930
1931 QFile myFile( myFileName );
1932 if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1933 {
1934 QTextStream myFileStream( &myFile );
1935 // save as utf-8 with 2 spaces for indents
1936 myDocument.save( myFileStream, 2 );
1937 myFile.close();
1938 resultFlag = true;
1939 switch ( type )
1940 {
1941 case Metadata:
1942 return tr( "Created default metadata file as %1" ).arg( myFileName );
1943
1944 case Style:
1945 return tr( "Created default style file as %1" ).arg( myFileName );
1946 }
1947
1948 }
1949 else
1950 {
1951 resultFlag = false;
1952 switch ( type )
1953 {
1954 case Metadata:
1955 return tr( "ERROR: Failed to created default metadata file as %1. Check file permissions and retry." ).arg( myFileName );
1956
1957 case Style:
1958 return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
1959 }
1960 }
1961 }
1962 else
1963 {
1964 const QString qml = myDocument.toString();
1965
1966 // read from database
1967 sqlite3_database_unique_ptr database;
1968 sqlite3_statement_unique_ptr statement;
1969
1970 int myResult = database.open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( QStringLiteral( "qgis.qmldb" ) ) );
1971 if ( myResult != SQLITE_OK )
1972 {
1973 return tr( "User database could not be opened." );
1974 }
1975
1976 QByteArray param0 = uri.toUtf8();
1977 QByteArray param1 = qml.toUtf8();
1978
1979 QString mySql;
1980 switch ( type )
1981 {
1982 case Metadata:
1983 mySql = QStringLiteral( "create table if not exists tbl_metadata(metadata varchar primary key,qmd varchar)" );
1984 break;
1985
1986 case Style:
1987 mySql = QStringLiteral( "create table if not exists tbl_styles(style varchar primary key,qml varchar)" );
1988 break;
1989 }
1990
1991 statement = database.prepare( mySql, myResult );
1992 if ( myResult == SQLITE_OK )
1993 {
1994 if ( sqlite3_step( statement.get() ) != SQLITE_DONE )
1995 {
1996 resultFlag = false;
1997 switch ( type )
1998 {
1999 case Metadata:
2000 return tr( "The metadata table could not be created." );
2001
2002 case Style:
2003 return tr( "The style table could not be created." );
2004 }
2005 }
2006 }
2007
2008 switch ( type )
2009 {
2010 case Metadata:
2011 mySql = QStringLiteral( "insert into tbl_metadata(metadata,qmd) values (?,?)" );
2012 break;
2013
2014 case Style:
2015 mySql = QStringLiteral( "insert into tbl_styles(style,qml) values (?,?)" );
2016 break;
2017 }
2018 statement = database.prepare( mySql, myResult );
2019 if ( myResult == SQLITE_OK )
2020 {
2021 if ( sqlite3_bind_text( statement.get(), 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
2022 sqlite3_bind_text( statement.get(), 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
2023 sqlite3_step( statement.get() ) == SQLITE_DONE )
2024 {
2025 resultFlag = true;
2026 switch ( type )
2027 {
2028 case Metadata:
2029 myErrorMessage = tr( "The metadata %1 was saved to database" ).arg( uri );
2030 break;
2031
2032 case Style:
2033 myErrorMessage = tr( "The style %1 was saved to database" ).arg( uri );
2034 break;
2035 }
2036 }
2037 }
2038
2039 if ( !resultFlag )
2040 {
2041 QString mySql;
2042 switch ( type )
2043 {
2044 case Metadata:
2045 mySql = QStringLiteral( "update tbl_metadata set qmd=? where metadata=?" );
2046 break;
2047
2048 case Style:
2049 mySql = QStringLiteral( "update tbl_styles set qml=? where style=?" );
2050 break;
2051 }
2052 statement = database.prepare( mySql, myResult );
2053 if ( myResult == SQLITE_OK )
2054 {
2055 if ( sqlite3_bind_text( statement.get(), 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK &&
2056 sqlite3_bind_text( statement.get(), 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK &&
2057 sqlite3_step( statement.get() ) == SQLITE_DONE )
2058 {
2059 resultFlag = true;
2060 switch ( type )
2061 {
2062 case Metadata:
2063 myErrorMessage = tr( "The metadata %1 was updated in the database." ).arg( uri );
2064 break;
2065
2066 case Style:
2067 myErrorMessage = tr( "The style %1 was updated in the database." ).arg( uri );
2068 break;
2069 }
2070 }
2071 else
2072 {
2073 resultFlag = false;
2074 switch ( type )
2075 {
2076 case Metadata:
2077 myErrorMessage = tr( "The metadata %1 could not be updated in the database." ).arg( uri );
2078 break;
2079
2080 case Style:
2081 myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( uri );
2082 break;
2083 }
2084 }
2085 }
2086 else
2087 {
2088 resultFlag = false;
2089 switch ( type )
2090 {
2091 case Metadata:
2092 myErrorMessage = tr( "The metadata %1 could not be inserted into database." ).arg( uri );
2093 break;
2094
2095 case Style:
2096 myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( uri );
2097 break;
2098 }
2099 }
2100 }
2101 }
2102
2103 return myErrorMessage;
2104}
2105
2106QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag, StyleCategories categories )
2107{
2109
2110 return saveNamedProperty( uri, QgsMapLayer::Style, resultFlag, categories );
2111}
2112
2113void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
2114{
2115 QgsSldExportContext exportContext;
2116 doc = exportSldStyleV3( exportContext );
2117 if ( !exportContext.errors().empty() )
2118 errorMsg = exportContext.errors().join( "\n" );
2119}
2120
2121void QgsMapLayer::exportSldStyleV2( QDomDocument &doc, QString &errorMsg, QgsSldExportContext &exportContext ) const
2122{
2124 doc = exportSldStyleV3( exportContext );
2125 if ( !exportContext.errors().empty() )
2126 errorMsg = exportContext.errors().join( "\n" );
2127}
2128
2129QDomDocument QgsMapLayer::exportSldStyleV3( QgsSldExportContext &exportContext ) const
2130{
2132
2133 QDomDocument myDocument = QDomDocument();
2134
2135 const QDomNode header = myDocument.createProcessingInstruction( QStringLiteral( "xml" ), QStringLiteral( "version=\"1.0\" encoding=\"UTF-8\"" ) );
2136 myDocument.appendChild( header );
2137
2138 const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
2139 const QgsRasterLayer *rlayer = qobject_cast<const QgsRasterLayer *>( this );
2140 if ( !vlayer && !rlayer )
2141 {
2142 exportContext.pushError( tr( "Could not save symbology because:\n%1" )
2143 .arg( tr( "Only vector and raster layers are supported" ) ) );
2144 return myDocument;
2145 }
2146
2147 // Create the root element
2148 QDomElement root = myDocument.createElementNS( QStringLiteral( "http://www.opengis.net/sld" ), QStringLiteral( "StyledLayerDescriptor" ) );
2149 QDomElement layerNode;
2150 if ( vlayer )
2151 {
2152 root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.1.0" ) );
2153 root.setAttribute( QStringLiteral( "xsi:schemaLocation" ), QStringLiteral( "http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" ) );
2154 root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
2155 root.setAttribute( QStringLiteral( "xmlns:se" ), QStringLiteral( "http://www.opengis.net/se" ) );
2156 root.setAttribute( QStringLiteral( "xmlns:xlink" ), QStringLiteral( "http://www.w3.org/1999/xlink" ) );
2157 root.setAttribute( QStringLiteral( "xmlns:xsi" ), QStringLiteral( "http://www.w3.org/2001/XMLSchema-instance" ) );
2158 myDocument.appendChild( root );
2159
2160 // Create the NamedLayer element
2161 layerNode = myDocument.createElement( QStringLiteral( "NamedLayer" ) );
2162 root.appendChild( layerNode );
2163 }
2164
2165 // note: Only SLD 1.0 version is generated because seems none is using SE1.1.0 at least for rasters
2166 if ( rlayer )
2167 {
2168 // Create the root element
2169 root.setAttribute( QStringLiteral( "version" ), QStringLiteral( "1.0.0" ) );
2170 root.setAttribute( QStringLiteral( "xmlns:gml" ), QStringLiteral( "http://www.opengis.net/gml" ) );
2171 root.setAttribute( QStringLiteral( "xmlns:ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) );
2172 root.setAttribute( QStringLiteral( "xmlns:sld" ), QStringLiteral( "http://www.opengis.net/sld" ) );
2173 myDocument.appendChild( root );
2174
2175 // Create the NamedLayer element
2176 layerNode = myDocument.createElement( QStringLiteral( "UserLayer" ) );
2177 root.appendChild( layerNode );
2178 }
2179
2180 QVariantMap props = exportContext.extraProperties();
2181
2182 QVariant context;
2183 context.setValue( exportContext );
2184
2185 // TODO -- move this to proper members of QgsSldExportContext
2186 props[ QStringLiteral( "SldExportContext" ) ] = context;
2187
2189 {
2190 props[ QStringLiteral( "scaleMinDenom" ) ] = QString::number( mMinScale );
2191 props[ QStringLiteral( "scaleMaxDenom" ) ] = QString::number( mMaxScale );
2192 }
2193 exportContext.setExtraProperties( props );
2194
2195 if ( vlayer )
2196 {
2197 if ( !vlayer->writeSld( layerNode, myDocument, exportContext ) )
2198 {
2199 return myDocument;
2200 }
2201 }
2202 else if ( rlayer )
2203 {
2204 if ( !rlayer->writeSld( layerNode, myDocument, exportContext ) )
2205 {
2206 return myDocument;
2207 }
2208 }
2209
2210 return myDocument;
2211}
2212
2213QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const
2214{
2215 QgsSldExportContext context;
2216 context.setExportFilePath( uri );
2217 return saveSldStyleV2( resultFlag, context );
2218}
2219
2220QString QgsMapLayer::saveSldStyleV2( bool &resultFlag, QgsSldExportContext &exportContext ) const
2221{
2223
2224 const QgsMapLayer *mlayer = qobject_cast<const QgsMapLayer *>( this );
2225
2226 const QString uri { exportContext.exportFilePath() };
2227
2228 // check if the uri is a file or ends with .sld,
2229 // which indicates that it should become one
2230 QString filename;
2231 if ( mlayer->providerType() == QLatin1String( "ogr" ) )
2232 {
2233 QStringList theURIParts = uri.split( '|' );
2234 filename = theURIParts[0];
2235 }
2236 else if ( mlayer->providerType() == QLatin1String( "gpx" ) )
2237 {
2238 QStringList theURIParts = uri.split( '?' );
2239 filename = theURIParts[0];
2240 }
2241 else if ( mlayer->providerType() == QLatin1String( "delimitedtext" ) )
2242 {
2243 filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
2244 // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
2245 if ( filename.isEmpty() )
2246 filename = uri;
2247 }
2248 else
2249 {
2250 filename = uri;
2251 }
2252
2253 const QFileInfo myFileInfo( filename );
2254 if ( myFileInfo.exists() || filename.endsWith( QLatin1String( ".sld" ), Qt::CaseInsensitive ) )
2255 {
2256 const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
2257 if ( !myDirInfo.isWritable() )
2258 {
2259 resultFlag = false;
2260 return tr( "The directory containing your dataset needs to be writable!" );
2261 }
2262
2263 // now construct the file name for our .sld style file
2264 const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";
2265
2266 QgsSldExportContext context { exportContext };
2267 context.setExportFilePath( myFileName );
2268
2269 QDomDocument myDocument = mlayer->exportSldStyleV3( context );
2270
2271 if ( !context.errors().empty() )
2272 {
2273 resultFlag = false;
2274 return context.errors().join( '\n' );
2275 }
2276
2277 QFile myFile( myFileName );
2278 if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
2279 {
2280 QTextStream myFileStream( &myFile );
2281 // save as utf-8 with 2 spaces for indents
2282 myDocument.save( myFileStream, 2 );
2283 myFile.close();
2284 resultFlag = true;
2285 return tr( "Created default style file as %1" ).arg( myFileName );
2286 }
2287 }
2288
2289 resultFlag = false;
2290 return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
2291
2292}
2293
2294QString QgsMapLayer::loadSldStyle( const QString &uri, bool &resultFlag )
2295{
2297
2298 resultFlag = false;
2299
2300 QDomDocument myDocument;
2301
2302 // location of problem associated with errorMsg
2303 int line = 0, column = 0;
2304 QString myErrorMessage;
2305
2306 QFile myFile( uri );
2307 if ( myFile.open( QFile::ReadOnly ) )
2308 {
2309 // read file
2310#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
2311 QXmlStreamReader xmlReader( &myFile );
2312 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( QStringLiteral( "sld" ), QStringLiteral( "http://www.opengis.net/sld" ) ) );
2313 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( QStringLiteral( "fes" ), QStringLiteral( "http://www.opengis.net/fes/2.0" ) ) );
2314 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( QStringLiteral( "ogc" ), QStringLiteral( "http://www.opengis.net/ogc" ) ) );
2315 const QDomDocument::ParseResult result = myDocument.setContent( &xmlReader, QDomDocument::ParseOption::UseNamespaceProcessing );
2316 if ( result )
2317 {
2318 resultFlag = true;
2319 }
2320 else
2321 {
2322 myErrorMessage = result.errorMessage;
2323 line = result.errorLine;
2324 column = result.errorColumn;
2325 }
2326#else
2327 resultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
2328#endif
2329 if ( !resultFlag )
2330 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
2331 myFile.close();
2332 }
2333 else
2334 {
2335 myErrorMessage = tr( "Unable to open file %1" ).arg( uri );
2336 }
2337
2338 if ( !resultFlag )
2339 {
2340 return myErrorMessage;
2341 }
2342
2343 // check for root SLD element
2344 const QDomElement myRoot = myDocument.firstChildElement( QStringLiteral( "StyledLayerDescriptor" ) );
2345 if ( myRoot.isNull() )
2346 {
2347 myErrorMessage = QStringLiteral( "Error: StyledLayerDescriptor element not found in %1" ).arg( uri );
2348 resultFlag = false;
2349 return myErrorMessage;
2350 }
2351
2352 // now get the style node out and pass it over to the layer
2353 // to deserialise...
2354 const QDomElement namedLayerElem = myRoot.firstChildElement( QStringLiteral( "NamedLayer" ) );
2355 if ( namedLayerElem.isNull() )
2356 {
2357 myErrorMessage = QStringLiteral( "Info: NamedLayer element not found." );
2358 resultFlag = false;
2359 return myErrorMessage;
2360 }
2361
2362 QString errorMsg;
2363 resultFlag = readSld( namedLayerElem, errorMsg );
2364 if ( !resultFlag )
2365 {
2366 myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, errorMsg );
2367 return myErrorMessage;
2368 }
2369
2370 return QString();
2371}
2372
2373bool QgsMapLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
2374{
2376
2377 Q_UNUSED( node )
2378 Q_UNUSED( errorMessage )
2379 Q_UNUSED( context )
2380 Q_UNUSED( categories )
2381 return false;
2382}
2383
2384bool QgsMapLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage,
2385 const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2386{
2388
2389 Q_UNUSED( node )
2390 Q_UNUSED( doc )
2391 Q_UNUSED( errorMessage )
2392 Q_UNUSED( context )
2393 Q_UNUSED( categories )
2394 return false;
2395}
2396
2397
2398void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider,
2399 bool loadDefaultStyleFlag )
2400{
2402
2404
2406 if ( loadDefaultStyleFlag )
2407 {
2409 }
2410
2412 {
2414 }
2415 setDataSource( dataSource,
2416 baseName.isEmpty() ? mLayerName : baseName,
2417 provider.isEmpty() ? mProviderKey : provider,
2418 options, flags );
2419}
2420
2421void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider,
2422 const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag )
2423{
2425
2427 if ( loadDefaultStyleFlag )
2428 {
2430 }
2431
2433 {
2435 }
2436 setDataSource( dataSource, baseName, provider, options, flags );
2437}
2438
2439void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider,
2441{
2443
2446 {
2448 }
2449 setDataSourcePrivate( dataSource, baseName, provider, options, flags );
2450 emit dataSourceChanged();
2451 emit dataChanged();
2453}
2454
2455
2456void QgsMapLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider,
2458{
2460
2461 Q_UNUSED( dataSource )
2462 Q_UNUSED( baseName )
2463 Q_UNUSED( provider )
2464 Q_UNUSED( options )
2465 Q_UNUSED( flags )
2466}
2467
2468
2470{
2472
2473 return mProviderKey;
2474}
2475
2476void QgsMapLayer::readCommonStyle( const QDomElement &layerElement, const QgsReadWriteContext &context,
2477 QgsMapLayer::StyleCategories categories )
2478{
2480
2481 if ( categories.testFlag( Symbology3D ) )
2482 {
2483 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "3D Symbology" ) );
2484
2485 QgsAbstract3DRenderer *r3D = nullptr;
2486 QDomElement renderer3DElem = layerElement.firstChildElement( QStringLiteral( "renderer-3d" ) );
2487 if ( !renderer3DElem.isNull() )
2488 {
2489 const QString type3D = renderer3DElem.attribute( QStringLiteral( "type" ) );
2491 if ( meta3D )
2492 {
2493 r3D = meta3D->createRenderer( renderer3DElem, context );
2494 }
2495 }
2496 setRenderer3D( r3D );
2497 }
2498
2499 if ( categories.testFlag( CustomProperties ) )
2500 {
2501 // read custom properties before passing reading further to a subclass, so that
2502 // the subclass can also read custom properties
2503 readCustomProperties( layerElement );
2504 }
2505
2506 // use scale dependent visibility flag
2507 if ( categories.testFlag( Rendering ) )
2508 {
2509 setScaleBasedVisibility( layerElement.attribute( QStringLiteral( "hasScaleBasedVisibilityFlag" ) ).toInt() == 1 );
2510 if ( layerElement.hasAttribute( QStringLiteral( "minimumScale" ) ) )
2511 {
2512 // older element, when scales were reversed
2513 setMaximumScale( layerElement.attribute( QStringLiteral( "minimumScale" ) ).toDouble() );
2514 setMinimumScale( layerElement.attribute( QStringLiteral( "maximumScale" ) ).toDouble() );
2515 }
2516 else
2517 {
2518 setMaximumScale( layerElement.attribute( QStringLiteral( "maxScale" ) ).toDouble() );
2519 setMinimumScale( layerElement.attribute( QStringLiteral( "minScale" ) ).toDouble() );
2520 }
2521 if ( layerElement.hasAttribute( QStringLiteral( "autoRefreshMode" ) ) )
2522 {
2523 setAutoRefreshInterval( layerElement.attribute( QStringLiteral( "autoRefreshTime" ) ).toInt() );
2524 setAutoRefreshMode( qgsEnumKeyToValue( layerElement.attribute( QStringLiteral( "autoRefreshMode" ) ), Qgis::AutoRefreshMode::Disabled ) );
2525 }
2526 }
2527
2528 if ( categories.testFlag( LayerConfiguration ) )
2529 {
2530 // flags
2531 const QDomElement flagsElem = layerElement.firstChildElement( QStringLiteral( "flags" ) );
2532 LayerFlags flags = mFlags;
2533 const auto enumMap = qgsEnumMap<QgsMapLayer::LayerFlag>();
2534 for ( auto it = enumMap.constBegin(); it != enumMap.constEnd(); ++it )
2535 {
2536 const QDomNode flagNode = flagsElem.namedItem( it.value() );
2537 if ( flagNode.isNull() )
2538 continue;
2539 const bool flagValue = flagNode.toElement().text() == "1" ? true : false;
2540 if ( flags.testFlag( it.key() ) && !flagValue )
2541 flags &= ~it.key();
2542 else if ( !flags.testFlag( it.key() ) && flagValue )
2543 flags |= it.key();
2544 }
2545 setFlags( flags );
2546 }
2547
2548 if ( categories.testFlag( Temporal ) )
2549 {
2550 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Temporal" ) );
2551
2553 properties->readXml( layerElement.toElement(), context );
2554 }
2555
2556 if ( categories.testFlag( Elevation ) )
2557 {
2558 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Elevation" ) );
2559
2561 properties->readXml( layerElement.toElement(), context );
2562 }
2563
2564 if ( categories.testFlag( Notes ) )
2565 {
2566 const QDomElement notesElem = layerElement.firstChildElement( QStringLiteral( "userNotes" ) );
2567 if ( !notesElem.isNull() )
2568 {
2569 const QString notes = notesElem.attribute( QStringLiteral( "value" ) );
2570 QgsLayerNotesUtils::setLayerNotes( this, notes );
2571 }
2572 }
2573}
2574
2576{
2578
2579 return mUndoStack;
2580}
2581
2583{
2585
2586 return mUndoStackStyles;
2587}
2588
2590{
2592
2593 return mCustomProperties.keys();
2594}
2595
2596void QgsMapLayer::setCustomProperty( const QString &key, const QVariant &value )
2597{
2599
2600 if ( !mCustomProperties.contains( key ) || mCustomProperties.value( key ) != value )
2601 {
2602 mCustomProperties.setValue( key, value );
2603 emit customPropertyChanged( key );
2604 }
2605}
2606
2608{
2610
2611 mCustomProperties = properties;
2612 for ( const QString &key : mCustomProperties.keys() )
2613 {
2614 emit customPropertyChanged( key );
2615 }
2616}
2617
2619{
2621
2622 return mCustomProperties;
2623}
2624
2625QVariant QgsMapLayer::customProperty( const QString &value, const QVariant &defaultValue ) const
2626{
2627 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
2629
2630 return mCustomProperties.value( value, defaultValue );
2631}
2632
2633void QgsMapLayer::removeCustomProperty( const QString &key )
2634{
2636
2637 if ( mCustomProperties.contains( key ) )
2638 {
2639 mCustomProperties.remove( key );
2640 emit customPropertyChanged( key );
2641 }
2642}
2643
2644int QgsMapLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError )
2645{
2647
2648 return QgsProviderRegistry::instance()->listStyles( mProviderKey, mDataSource, ids, names, descriptions, msgError );
2649}
2650
2651QString QgsMapLayer::getStyleFromDatabase( const QString &styleId, QString &msgError )
2652{
2654
2655 return QgsProviderRegistry::instance()->getStyleById( mProviderKey, mDataSource, styleId, msgError );
2656}
2657
2658bool QgsMapLayer::deleteStyleFromDatabase( const QString &styleId, QString &msgError )
2659{
2661
2663}
2664
2665void QgsMapLayer::saveStyleToDatabase( const QString &name, const QString &description,
2666 bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories )
2667{
2668 saveStyleToDatabaseV2( name, description, useAsDefault, uiFileContent, msgError, categories );
2669}
2670
2671QgsMapLayer::SaveStyleResults QgsMapLayer::saveStyleToDatabaseV2( const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories )
2672{
2674
2676
2677 QString sldStyle, qmlStyle;
2678 QDomDocument qmlDocument;
2679 QgsReadWriteContext context;
2680 exportNamedStyle( qmlDocument, msgError, context, categories );
2681 if ( !msgError.isEmpty() )
2682 {
2684 }
2685 else
2686 {
2687 qmlStyle = qmlDocument.toString();
2688 }
2689
2690 QgsSldExportContext sldContext;
2691 QDomDocument sldDocument = this->exportSldStyleV3( sldContext );
2692 if ( !sldContext.errors().empty() )
2693 {
2695 }
2696 else
2697 {
2698 sldStyle = sldDocument.toString();
2699 }
2700
2701 if ( !QgsProviderRegistry::instance()->saveStyle( mProviderKey,
2702 mDataSource, qmlStyle, sldStyle, name, description, uiFileContent, useAsDefault, msgError ) )
2703 {
2705 }
2706 return results;
2707}
2708
2709QString QgsMapLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB, QgsMapLayer::StyleCategories categories, Qgis::LoadStyleFlags flags )
2710{
2712
2713 QString returnMessage;
2714 QString qml, errorMsg;
2715 QString styleName;
2716 if ( !loadFromLocalDB && dataProvider() && dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
2717 {
2719 }
2720
2721 // Style was successfully loaded from provider storage
2722 if ( !qml.isEmpty() )
2723 {
2724 QDomDocument myDocument( QStringLiteral( "qgis" ) );
2725 myDocument.setContent( qml );
2726 resultFlag = importNamedStyle( myDocument, errorMsg );
2727 returnMessage = QObject::tr( "Loaded from Provider" );
2728 }
2729 else
2730 {
2732
2733 bool styleExists = false;
2734 bool styleSuccessfullyLoaded = false;
2735
2736 returnMessage = loadNamedProperty( theURI, PropertyType::Style, styleExists, styleSuccessfullyLoaded, categories, flags );
2737
2738 // TODO QGIS 4.0 -- fix API for loadNamedStyle so we can return styleExists too
2739 ( void )styleExists;
2740 resultFlag = styleSuccessfullyLoaded;
2741 }
2742
2743 if ( ! styleName.isEmpty() )
2744 {
2745 styleManager()->renameStyle( styleManager()->currentStyle(), styleName );
2746 }
2747
2748 if ( resultFlag )
2749 emit styleLoaded( categories );
2750
2751 return returnMessage;
2752}
2753
2760
2762{
2764
2765 return false;
2766}
2767
2769{
2771
2772 return false;
2773}
2774
2776{
2778
2779 return true;
2780}
2781
2783{
2785
2786 // invalid layers are temporary? -- who knows?!
2787 if ( !isValid() )
2788 return false;
2789
2790 if ( mProviderKey == QLatin1String( "memory" ) )
2791 return true;
2792
2793 const QVariantMap sourceParts = QgsProviderRegistry::instance()->decodeUri( mProviderKey, mDataSource );
2794 const QString path = sourceParts.value( QStringLiteral( "path" ) ).toString();
2795 if ( path.isEmpty() )
2796 return false;
2797
2798 // check if layer path is inside one of the standard temporary file locations for this platform
2799 const QStringList tempPaths = QStandardPaths::standardLocations( QStandardPaths::TempLocation );
2800 for ( const QString &tempPath : tempPaths )
2801 {
2802 if ( path.startsWith( tempPath ) )
2803 return true;
2804 }
2805
2806 return false;
2807}
2808
2809void QgsMapLayer::setValid( bool valid )
2810{
2812
2813 if ( mValid == valid )
2814 return;
2815
2816 mValid = valid;
2817 emit isValidChanged();
2818}
2819
2821{
2823
2824 if ( legend == mLegend.get() )
2825 return;
2826
2827 mLegend.reset( legend );
2828
2829
2830 if ( mLegend )
2831 {
2832 mLegend->setParent( this );
2833 connect( mLegend.get(), &QgsMapLayerLegend::itemsChanged, this, &QgsMapLayer::legendChanged, Qt::UniqueConnection );
2834 }
2835
2836 emit legendChanged();
2837}
2838
2840{
2842
2843 return mLegend.get();
2844}
2845
2847{
2849
2850 return mStyleManager.get();
2851}
2852
2854{
2856
2857 if ( renderer == m3DRenderer.get() )
2858 return;
2859
2860 m3DRenderer.reset( renderer );
2861
2862 emit renderer3DChanged();
2863 emit repaintRequested();
2865}
2866
2868{
2870
2871 return m3DRenderer.get();
2872}
2873
2874void QgsMapLayer::triggerRepaint( bool deferredUpdate )
2875{
2877
2878 if ( mRepaintRequestedFired )
2879 return;
2880 mRepaintRequestedFired = true;
2881 emit repaintRequested( deferredUpdate );
2882 mRepaintRequestedFired = false;
2883}
2884
2891
2893{
2895
2896 mMetadata = metadata;
2897// mMetadata.saveToLayer( this );
2898 emit metadataChanged();
2899}
2900
2902{
2904
2905 return QString();
2906}
2907
2908QDateTime QgsMapLayer::timestamp() const
2909{
2911
2912 return QDateTime();
2913}
2914
2922
2924{
2925 updateExtent( extent );
2926}
2927
2929{
2931
2932 updateExtent( extent );
2933}
2934
2935bool QgsMapLayer::isReadOnly() const
2936{
2938
2939 return true;
2940}
2941
2943{
2945
2946 return mOriginalXmlProperties;
2947}
2948
2950{
2952
2953 mOriginalXmlProperties = originalXmlProperties;
2954}
2955
2956QString QgsMapLayer::generateId( const QString &layerName )
2957{
2958 // Generate the unique ID of this layer
2959 const QString uuid = QUuid::createUuid().toString();
2960 // trim { } from uuid
2961 QString id = layerName + '_' + uuid.mid( 1, uuid.length() - 2 );
2962 // Tidy the ID up to avoid characters that may cause problems
2963 // elsewhere (e.g in some parts of XML). Replaces every non-word
2964 // character (word characters are the alphabet, numbers and
2965 // underscore) with an underscore.
2966 // Note that the first backslash in the regular expression is
2967 // there for the compiler, so the pattern is actually \W
2968 const thread_local QRegularExpression idRx( QStringLiteral( "[\\W]" ) );
2969 id.replace( idRx, QStringLiteral( "_" ) );
2970 return id;
2971}
2972
2974{
2976
2977 return true;
2978}
2979
2986
2988{
2990
2991 return mapTipsEnabled() && !mMapTipTemplate.isEmpty();
2992}
2993
3000
3001QSet<QgsMapLayerDependency> QgsMapLayer::dependencies() const
3002{
3004
3005 return mDependencies;
3006}
3007
3008bool QgsMapLayer::setDependencies( const QSet<QgsMapLayerDependency> &oDeps )
3009{
3011
3012 QSet<QgsMapLayerDependency> deps;
3013 const auto constODeps = oDeps;
3014 for ( const QgsMapLayerDependency &dep : constODeps )
3015 {
3016 if ( dep.origin() == QgsMapLayerDependency::FromUser )
3017 deps << dep;
3018 }
3019
3020 mDependencies = deps;
3021 emit dependenciesChanged();
3022 return true;
3023}
3024
3026{
3028
3029 QgsDataProvider *lDataProvider = dataProvider();
3030
3031 if ( !lDataProvider )
3032 return;
3033
3034 if ( enabled && !isRefreshOnNotifyEnabled() )
3035 {
3036 lDataProvider->setListening( enabled );
3037 connect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
3038 }
3039 else if ( !enabled && isRefreshOnNotifyEnabled() )
3040 {
3041 // we don't want to disable provider listening because someone else could need it (e.g. actions)
3042 disconnect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
3043 }
3044 mIsRefreshOnNofifyEnabled = enabled;
3045}
3046
3048{
3050
3051 if ( QgsMapLayerStore *store = qobject_cast<QgsMapLayerStore *>( parent() ) )
3052 {
3053 return qobject_cast<QgsProject *>( store->parent() );
3054 }
3055 return nullptr;
3056}
3057
3058void QgsMapLayer::onNotified( const QString &message )
3059{
3061
3062 if ( refreshOnNotifyMessage().isEmpty() || refreshOnNotifyMessage() == message )
3063 {
3065 emit dataChanged();
3066 }
3067}
3068
3069QgsRectangle QgsMapLayer::wgs84Extent( bool forceRecalculate ) const
3070{
3072
3074
3075 if ( ! forceRecalculate && ! mWgs84Extent.isNull() )
3076 {
3077 wgs84Extent = mWgs84Extent;
3078 }
3079 else if ( ! mExtent2D.isNull() || ! mExtent3D.isNull() )
3080 {
3081 QgsCoordinateTransform transformer { crs(), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ), transformContext() };
3082 transformer.setBallparkTransformsAreAppropriate( true );
3083 try
3084 {
3085 if ( mExtent2D.isNull() )
3086 wgs84Extent = transformer.transformBoundingBox( mExtent3D.toRectangle() );
3087 else
3088 wgs84Extent = transformer.transformBoundingBox( mExtent2D );
3089 }
3090 catch ( const QgsCsException &cse )
3091 {
3092 QgsMessageLog::logMessage( tr( "Error transforming extent: %1" ).arg( cse.what() ) );
3094 }
3095 }
3096 return wgs84Extent;
3097}
3098
3099void QgsMapLayer::updateExtent( const QgsRectangle &extent ) const
3100{
3102
3103 if ( extent == mExtent2D )
3104 return;
3105
3106 mExtent2D = extent;
3107
3108 // do not update the wgs84 extent if we trust layer metadata
3110 return;
3111
3112 mWgs84Extent = wgs84Extent( true );
3113}
3114
3115void QgsMapLayer::updateExtent( const QgsBox3D &extent ) const
3116{
3118
3119 if ( extent == mExtent3D )
3120 return;
3121
3122 if ( extent.isNull() )
3123 {
3124 if ( !extent.toRectangle().isNull() )
3125 {
3126 // bad 3D extent param but valid in 2d --> update 2D extent
3127 updateExtent( extent.toRectangle() );
3128 }
3129 else
3130 {
3131 QgsDebugMsgLevel( QStringLiteral( "Unable to update extent with empty parameter" ), 1 );
3132 }
3133 }
3134 else
3135 {
3136 mExtent3D = extent;
3137
3138 // do not update the wgs84 extent if we trust layer metadata
3140 return;
3141
3142 mWgs84Extent = wgs84Extent( true );
3143 }
3144}
3145
3146bool QgsMapLayer::rebuildCrs3D( QString *error )
3147{
3148 bool res = true;
3149 if ( !mCRS.isValid() )
3150 {
3151 mCrs3D = QgsCoordinateReferenceSystem();
3152 }
3153 else if ( !mVerticalCrs.isValid() )
3154 {
3155 mCrs3D = mCRS;
3156 }
3157 else
3158 {
3159 switch ( mCRS.type() )
3160 {
3164 mCrs3D = mCRS;
3165 break;
3166
3168 {
3169 QString tempError;
3170 mCrs3D = mCRS.hasVerticalAxis() ? mCRS : QgsCoordinateReferenceSystem::createCompoundCrs( mCRS, mVerticalCrs, error ? *error : tempError );
3171 res = mCrs3D.isValid();
3172 break;
3173 }
3174
3176 // nonsense situation
3177 mCrs3D = QgsCoordinateReferenceSystem();
3178 res = false;
3179 break;
3180
3189 {
3190 QString tempError;
3191 mCrs3D = QgsCoordinateReferenceSystem::createCompoundCrs( mCRS, mVerticalCrs, error ? *error : tempError );
3192 res = mCrs3D.isValid();
3193 break;
3194 }
3195 }
3196 }
3197 return res;
3198}
3199
3201{
3203
3204 // do not update the wgs84 extent if we trust layer metadata
3206 return;
3207
3208 mWgs84Extent = QgsRectangle();
3209}
3210
3212{
3214
3215 QString metadata = QStringLiteral( "<h1>" ) + tr( "General" ) + QStringLiteral( "</h1>\n<hr>\n" ) + QStringLiteral( "<table class=\"list-view\">\n" );
3216
3217 // name
3218 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + name() + QStringLiteral( "</td></tr>\n" );
3219
3220 const QString lPublicSource = publicSource();
3221
3222 QString path;
3223 bool isLocalPath = false;
3224 if ( dataProvider() )
3225 {
3226 // local path
3227 QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( dataProvider()->name(), lPublicSource );
3228 if ( uriComponents.contains( QStringLiteral( "path" ) ) )
3229 {
3230 path = uriComponents[QStringLiteral( "path" )].toString();
3231 QFileInfo fi( path );
3232 if ( fi.exists() )
3233 {
3234 isLocalPath = true;
3235 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Path" ) + QStringLiteral( "</td><td>%1" ).arg( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( path ).toString(), QDir::toNativeSeparators( path ) ) ) + QStringLiteral( "</td></tr>\n" );
3236
3237 QDateTime lastModified = fi.lastModified();
3238 QString lastModifiedFileName;
3239 QSet<QString> sidecarFiles = QgsFileUtils::sidecarFilesForPath( path );
3240 if ( fi.isFile() )
3241 {
3242 qint64 fileSize = fi.size();
3243 if ( !sidecarFiles.isEmpty() )
3244 {
3245 lastModifiedFileName = fi.fileName();
3246 QStringList sidecarFileNames;
3247 for ( const QString &sidecarFile : sidecarFiles )
3248 {
3249 QFileInfo sidecarFi( sidecarFile );
3250 fileSize += sidecarFi.size();
3251 if ( sidecarFi.lastModified() > lastModified )
3252 {
3253 lastModified = sidecarFi.lastModified();
3254 lastModifiedFileName = sidecarFi.fileName();
3255 }
3256 sidecarFileNames << sidecarFi.fileName();
3257 }
3258 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + ( sidecarFiles.size() > 1 ? tr( "Sidecar files" ) : tr( "Sidecar file" ) ) + QStringLiteral( "</td><td>%1" ).arg( sidecarFileNames.join( QLatin1String( ", " ) ) ) + QStringLiteral( "</td></tr>\n" );
3259 }
3260 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + ( !sidecarFiles.isEmpty() ? tr( "Total size" ) : tr( "Size" ) ) + QStringLiteral( "</td><td>%1" ).arg( QgsFileUtils::representFileSize( fileSize ) ) + QStringLiteral( "</td></tr>\n" );
3261 }
3262 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Last modified" ) + QStringLiteral( "</td><td>%1" ).arg( QLocale().toString( fi.lastModified() ) ) + ( !lastModifiedFileName.isEmpty() ? QStringLiteral( " (%1)" ).arg( lastModifiedFileName ) : QString() ) + QStringLiteral( "</td></tr>\n" );
3263 }
3264 }
3265 if ( uriComponents.contains( QStringLiteral( "url" ) ) )
3266 {
3267 QUrl decodedUri = QUrl::fromPercentEncoding( uriComponents[QStringLiteral( "url" )].toString().toLocal8Bit() );
3268 const QString url = decodedUri.toString();
3269 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "URL" ) + QStringLiteral( "</td><td>%1" ).arg( QStringLiteral( "<a href=\"%1\">%2</a>" ).arg( url, url ) ) + QStringLiteral( "</td></tr>\n" );
3270 }
3271 }
3272
3273 // data source
3274 if ( lPublicSource != path || !isLocalPath )
3275 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Source" ) + QStringLiteral( "</td><td>%1" ).arg( lPublicSource != path ? lPublicSource : path ) + QStringLiteral( "</td></tr>\n" );
3276
3277 // provider
3278 if ( dataProvider() )
3279 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Provider" ) + QStringLiteral( "</td><td>%1" ).arg( dataProvider()->name() ) + QStringLiteral( "</td></tr>\n" );
3280
3281 // Layer ID
3282 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Layer ID" ) + QStringLiteral( "</td><td>%1" ).arg( id() ) + QStringLiteral( "</td></tr>\n" );
3283
3284 metadata += QLatin1String( "</table>\n<br><br>" );
3285
3286 return metadata;
3287}
3288
3290{
3291 QString metadata;
3292 // custom properties
3293 if ( const auto keys = customPropertyKeys(); !keys.isEmpty() )
3294 {
3295 metadata += QStringLiteral( "<h1>" ) + tr( "Custom properties" ) + QStringLiteral( "</h1>\n<hr>\n" );
3296 metadata += QLatin1String( "<table class=\"list-view\">\n<tbody>" );
3297 for ( const QString &key : keys )
3298 {
3299 // keys prefaced with _ are considered private/internal details
3300 if ( key.startsWith( '_' ) )
3301 continue;
3302
3303 const QVariant propValue = customProperty( key );
3304 QString stringValue;
3305 if ( propValue.type() == QVariant::List || propValue.type() == QVariant::StringList )
3306 {
3307 for ( const QString &s : propValue.toStringList() )
3308 {
3309 stringValue += "<p style=\"margin: 0;\">" + s.toHtmlEscaped() + "</p>";
3310 }
3311 }
3312 else
3313 {
3314 stringValue = propValue.toString().toHtmlEscaped();
3315
3316 //if the result string is empty but propValue is not, the conversion has failed
3317 if ( stringValue.isEmpty() && !QgsVariantUtils::isNull( propValue ) )
3318 stringValue = tr( "<i>value cannot be displayed</i>" );
3319 }
3320
3321 metadata += QStringLiteral( "<tr><td class=\"highlight\">%1</td><td>%2</td></tr>" ).arg( key.toHtmlEscaped(), stringValue );
3322 }
3323 metadata += QLatin1String( "</tbody></table>\n" );
3324 metadata += QLatin1String( "<br><br>\n" );
3325 }
3326 return metadata;
3327}
3328
3330{
3332 QString metadata;
3333
3334 auto addCrsInfo = [&metadata]( const QgsCoordinateReferenceSystem & c, bool includeType, bool includeOperation, bool includeCelestialBody )
3335 {
3336 if ( !c.isValid() )
3337 metadata += QStringLiteral( "<tr><td colspan=\"2\" class=\"highlight\">" ) + tr( "Unknown" ) + QStringLiteral( "</td></tr>\n" );
3338 else
3339 {
3340 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Name" ) + QStringLiteral( "</td><td>" ) + c.userFriendlyIdentifier( Qgis::CrsIdentifierType::FullString ) + QStringLiteral( "</td></tr>\n" );
3341
3342 // map units
3343 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Units" ) + QStringLiteral( "</td><td>" )
3344 + ( c.isGeographic() ? tr( "Geographic (uses latitude and longitude for coordinates)" ) : QgsUnitTypes::toString( c.mapUnits() ) )
3345 + QStringLiteral( "</td></tr>\n" );
3346
3347 if ( includeType )
3348 {
3349 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Type" ) + QStringLiteral( "</td><td>" ) + QgsCoordinateReferenceSystemUtils::crsTypeToString( c.type() ) + QStringLiteral( "</td></tr>\n" );
3350 }
3351
3352 if ( includeOperation )
3353 {
3354 // operation
3355 const QgsProjOperation operation = c.operation();
3356 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Method" ) + QStringLiteral( "</td><td>" ) + operation.description() + QStringLiteral( "</td></tr>\n" );
3357 }
3358
3359 if ( includeCelestialBody )
3360 {
3361 // celestial body
3362 try
3363 {
3364 const QString celestialBody = c.celestialBodyName();
3365 if ( !celestialBody.isEmpty() )
3366 {
3367 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Celestial Body" ) + QStringLiteral( "</td><td>" ) + celestialBody + QStringLiteral( "</td></tr>\n" );
3368 }
3369 }
3370 catch ( QgsNotSupportedException & )
3371 {
3372
3373 }
3374 }
3375
3376 QString accuracyString;
3377 // dynamic crs with no epoch?
3378 if ( c.isDynamic() && std::isnan( c.coordinateEpoch() ) )
3379 {
3380 accuracyString = tr( "Based on a dynamic CRS, but no coordinate epoch is set. Coordinates are ambiguous and of limited accuracy." );
3381 }
3382
3383 // based on datum ensemble?
3384 try
3385 {
3386 const QgsDatumEnsemble ensemble = c.datumEnsemble();
3387 if ( ensemble.isValid() )
3388 {
3389 QString id;
3390 if ( !ensemble.code().isEmpty() )
3391 id = QStringLiteral( "<i>%1</i> (%2:%3)" ).arg( ensemble.name(), ensemble.authority(), ensemble.code() );
3392 else
3393 id = QStringLiteral( "<i>%1</i>”" ).arg( ensemble.name() );
3394
3395 if ( ensemble.accuracy() > 0 )
3396 {
3397 accuracyString = tr( "Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg( id ).arg( ensemble.accuracy() );
3398 }
3399 else
3400 {
3401 accuracyString = tr( "Based on %1, which has a limited accuracy." ).arg( id );
3402 }
3403 }
3404 }
3405 catch ( QgsNotSupportedException & )
3406 {
3407
3408 }
3409
3410 if ( !accuracyString.isEmpty() )
3411 {
3412 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Accuracy" ) + QStringLiteral( "</td><td>" ) + accuracyString + QStringLiteral( "</td></tr>\n" );
3413 }
3414
3415 // static/dynamic
3416 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Reference" ) + QStringLiteral( "</td><td>%1</td></tr>\n" ).arg( c.isDynamic() ? tr( "Dynamic (relies on a datum which is not plate-fixed)" ) : tr( "Static (relies on a datum which is plate-fixed)" ) );
3417
3418 // coordinate epoch
3419 if ( !std::isnan( c.coordinateEpoch() ) )
3420 {
3421 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) + tr( "Coordinate Epoch" ) + QStringLiteral( "</td><td>%1</td></tr>\n" ).arg( qgsDoubleToString( c.coordinateEpoch(), 3 ) );
3422 }
3423 }
3424 };
3425
3426 metadata += QStringLiteral( "<h1>" ) + tr( "Coordinate Reference System (CRS)" ) + QStringLiteral( "</h1>\n<hr>\n" );
3427 metadata += QLatin1String( "<table class=\"list-view\">\n" );
3428 addCrsInfo( crs().horizontalCrs(), true, true, true );
3429 metadata += QLatin1String( "</table>\n<br><br>\n" );
3430
3431 if ( verticalCrs().isValid() )
3432 {
3433 metadata += QStringLiteral( "<h1>" ) + tr( "Vertical Coordinate Reference System (CRS)" ) + QStringLiteral( "</h1>\n<hr>\n" );
3434 metadata += QLatin1String( "<table class=\"list-view\">\n" );
3435 addCrsInfo( verticalCrs(), false, false, false );
3436 metadata += QLatin1String( "</table>\n<br><br>\n" );
3437 }
3438
3439 return metadata;
3440}
static QString version()
Version string.
Definition qgis.cpp:677
@ FullString
Full definition – possibly a very lengthy string, e.g. with no truncation of custom WKT definitions.
Definition qgis.h:2420
@ Vertical
Vertical CRS.
Definition qgis.h:2333
@ Temporal
Temporal CRS.
Definition qgis.h:2336
@ Compound
Compound (horizontal + vertical) CRS.
Definition qgis.h:2335
@ Projected
Projected CRS.
Definition qgis.h:2334
@ Other
Other type.
Definition qgis.h:2339
@ Bound
Bound CRS.
Definition qgis.h:2338
@ DerivedProjected
Derived projected CRS.
Definition qgis.h:2340
@ Unknown
Unknown type.
Definition qgis.h:2328
@ Engineering
Engineering CRS.
Definition qgis.h:2337
@ Geographic3d
3D geopraphic CRS
Definition qgis.h:2332
@ Geodetic
Geodetic CRS.
Definition qgis.h:2329
@ Geographic2d
2D geographic CRS
Definition qgis.h:2331
@ Geocentric
Geocentric CRS.
Definition qgis.h:2330
@ RemoveCredentials
Completely remove credentials (eg passwords) from the URI. This flag is not compatible with the Redac...
Definition qgis.h:1372
@ RedactCredentials
Replace the value of credentials (eg passwords) with 'xxxxxxxx'. This flag is not compatible with the...
Definition qgis.h:1373
@ ForceFirstLetterToCapital
Convert just the first letter of each word to uppercase, leave the rest untouched.
Definition qgis.h:3393
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:358
@ Unknown
Unknown types.
Definition qgis.h:362
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:486
LayerType
Types of layers that can be added to a map.
Definition qgis.h:190
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:196
@ Vector
Vector layer.
Definition qgis.h:191
QFlags< MapLayerProperty > MapLayerProperties
Map layer properties.
Definition qgis.h:2285
QFlags< LoadStyleFlag > LoadStyleFlags
Flags for loading layer styles.
Definition qgis.h:246
@ LoadDefaultStyle
Reset the layer's style to the default for the datasource.
Definition qgis.h:470
@ ForceReadOnly
Open layer in a read-only mode.
Definition qgis.h:473
@ SkipGetExtent
Skip the extent from provider.
Definition qgis.h:471
@ TrustDataSource
Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load ti...
Definition qgis.h:468
@ IgnoreMissingStyleErrors
If the style is missing, then don't flag it as an error. This flag can be used when the caller is not...
Definition qgis.h:237
AutoRefreshMode
Map layer automatic refresh modes.
Definition qgis.h:2295
@ RedrawOnly
Redraw current data only.
Definition qgis.h:2298
@ ReloadData
Reload data (and draw the new data).
Definition qgis.h:2297
@ Disabled
Automatic refreshing is disabled.
Definition qgis.h:2296
Base metadata class for 3D renderers.
virtual QgsAbstract3DRenderer * createRenderer(QDomElement &elem, const QgsReadWriteContext &context)=0
Returns new instance of the renderer given the DOM element.
Qgs3DRendererAbstractMetadata * rendererMetadata(const QString &type) const
Returns metadata for a 3D renderer type (may be used to create a new instance of the type).
Base class for all renderers that participate in 3D views.
static QString pkgDataPath()
Returns the common root path of all application data directories.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user's home dir.
static QgsAuthManager * authManager()
Returns the application's authentication manager instance.
static Qgs3DRendererRegistry * renderer3DRegistry()
Returns registry of available 3D renderers.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:42
static QString crsTypeToString(Qgis::CrsType type)
Returns a translated string representing a CRS type.
Represents a coordinate reference system (CRS).
bool isValid() const
Returns whether this CRS is correctly initialized and usable.
static CUSTOM_CRS_VALIDATION customCrsValidation()
Gets custom function.
static QgsCoordinateReferenceSystem createCompoundCrs(const QgsCoordinateReferenceSystem &horizontalCrs, const QgsCoordinateReferenceSystem &verticalCrs, QString &error)
Given a horizontal and vertical CRS, attempts to create a compound CRS from them.
static void setCustomCrsValidation(CUSTOM_CRS_VALIDATION f)
Sets custom function to force valid CRS.
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
void setBallparkTransformsAreAppropriate(bool appropriate)
Sets whether approximate "ballpark" results are appropriate for this coordinate transform.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
Custom exception class for Coordinate Reference System related exceptions.
virtual bool containsElevationData() const
Returns true if the data provider definitely contains elevation related data.
Abstract base class for spatial data provider implementations.
void notify(const QString &msg)
Emitted when the datasource issues a notification.
virtual QgsDataProviderElevationProperties * elevationProperties()
Returns the provider's elevation properties.
static QString removePassword(const QString &aUri, bool hide=false)
Removes the password element from a URI.
Contains information about a datum ensemble.
Definition qgsdatums.h:95
QString code() const
Identification code, e.g.
Definition qgsdatums.h:122
QString authority() const
Authority name, e.g.
Definition qgsdatums.h:117
bool isValid() const
Returns true if the datum ensemble is a valid object, or false if it is a null/invalid object.
Definition qgsdatums.h:102
QString name() const
Display name of datum ensemble.
Definition qgsdatums.h:107
double accuracy() const
Positional accuracy (in meters).
Definition qgsdatums.h:112
A container for error messages.
Definition qgserror.h:81
QString what() const
static QSet< QString > sidecarFilesForPath(const QString &path)
Returns a list of the sidecar files which exist for the dataset a the specified path.
static QString representFileSize(qint64 bytes)
Returns the human size from bytes.
A structured metadata store for a map layer.
static void setLayerNotes(QgsMapLayer *layer, const QString &notes)
Sets the notes for the specified layer, where notes is a HTML formatted string.
static bool layerHasNotes(const QgsMapLayer *layer)
Returns true if the specified layer has notes available.
static QString layerNotes(const QgsMapLayer *layer)
Returns the notes for the specified layer.
Models dependencies with or between map layers.
Base class for storage of map layer elevation properties.
An abstract interface for implementations of legends for one map layer.
void itemsChanged()
Emitted when existing items/nodes got invalid and should be replaced by new ones.
Manages QGIS Server properties for a map layer.
void readXml(const QDomNode &layer_node)
Reads server properties from project file.
void copyTo(QgsMapLayerServerProperties *properties) const
Copy properties to another instance.
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves server properties to xml under the layer node.
A storage object for map layers, in which the layers are owned by the store and have their lifetime b...
Management of styles for use with one map layer.
bool addStyle(const QString &name, const QgsMapLayerStyle &style)
Add a style with given name and data.
QStringList styles() const
Returns list of all defined style names.
bool renameStyle(const QString &name, const QString &newName)
Rename a stored style to a different name.
Base class for storage of map layer temporal properties.
void crs3DChanged()
Emitted when the crs3D() of the layer has changed.
Q_DECL_DEPRECATED void setShortName(const QString &shortName)
Sets the short name of the layer used by QGIS Server to identify the layer.
virtual bool deleteStyleFromDatabase(const QString &styleId, QString &msgError)
Deletes a style from the database.
bool importNamedMetadata(QDomDocument &document, QString &errorMessage)
Import the metadata of this layer from a QDomDocument.
QString name
Definition qgsmaplayer.h:84
void readStyleManager(const QDomNode &layerNode)
Read style manager's configuration (if any). To be called by subclasses.
virtual bool writeSymbology(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const =0
Write the style for the layer into the document provided.
Q_DECL_DEPRECATED QString legendUrlFormat() const
Returns the format for a URL based layer legend.
QgsRectangle wgs84Extent(bool forceRecalculate=false) const
Returns the WGS84 extent (EPSG:4326) of the layer according to ReadFlag::FlagTrustLayerMetadata.
void setRefreshOnNotifyEnabled(bool enabled)
Set whether provider notification is connected to triggerRepaint.
virtual bool isSpatial() const
Returns true if the layer is considered a spatial layer, ie it has some form of geometry associated w...
QgsAbstract3DRenderer * renderer3D() const
Returns 3D renderer associated with the layer.
virtual bool isTemporary() const
Returns true if the layer is considered a temporary layer.
virtual Q_DECL_DEPRECATED void exportSldStyleV2(QDomDocument &doc, QString &errorMsg, QgsSldExportContext &exportContext) const
Export the properties of this layer as SLD style in a QDomDocument.
virtual void exportNamedStyle(QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context=QgsReadWriteContext(), QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories) const
Export the properties of this layer as named style in a QDomDocument.
bool setId(const QString &id)
Sets the layer's id.
virtual bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all symbols associated with the layer.
void dependenciesChanged()
Emitted when dependencies are changed.
virtual bool hasMapTips() const
Returns true if the layer contains map tips.
bool isInScaleRange(double scale) const
Tests whether the layer should be visible at the specified scale.
void legendChanged()
Signal emitted when legend of the layer has changed.
void writeStyleManager(QDomNode &layerNode, QDomDocument &doc) const
Write style manager's configuration (if exists). To be called by subclasses.
QgsMapLayerLegend * legend() const
Can be nullptr.
QFlags< ReadFlag > ReadFlags
QFlags< LayerFlag > LayerFlags
virtual bool importNamedStyle(QDomDocument &doc, QString &errorMsg, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Import the properties of this layer from a QDomDocument.
Q_DECL_DEPRECATED void setAbstract(const QString &abstract)
Sets the abstract of the layer used by QGIS Server in GetCapabilities request.
void metadataChanged()
Emitted when the layer's metadata is changed.
virtual QgsRectangle extent() const
Returns the extent of the layer.
virtual QString saveSldStyle(const QString &uri, bool &resultFlag) const
Saves the properties of this layer to an SLD format file.
QString source() const
Returns the source for the layer.
Q_DECL_DEPRECATED void setLegendUrl(const QString &legendUrl)
Sets the URL for the layer's legend.
virtual bool setDependencies(const QSet< QgsMapLayerDependency > &layers)
Sets the list of dependencies.
void request3DUpdate()
Signal emitted when a layer requires an update in any 3D maps.
Q_INVOKABLE QVariant customProperty(const QString &value, const QVariant &defaultValue=QVariant()) const
Read a custom property from layer.
QgsError mError
Error.
int mBlockStyleChangedSignal
If non-zero, the styleChanged signal should not be emitted.
@ SldGenerationFailed
Generation of the SLD failed, and was not written to the database.
@ DatabaseWriteFailed
An error occurred when attempting to write to the database.
@ QmlGenerationFailed
Generation of the QML failed, and was not written to the database.
QString providerType() const
Returns the provider type (provider key) for this layer.
virtual void setExtent3D(const QgsBox3D &box)
Sets the extent.
void removeCustomProperty(const QString &key)
Remove a custom property from layer.
Qgis::AutoRefreshMode autoRefreshMode() const
Returns the layer's automatic refresh mode.
void setBlendMode(QPainter::CompositionMode blendMode)
Set the blending mode used for rendering a layer.
void configChanged()
Emitted whenever the configuration is changed.
void trigger3DUpdate()
Will advise any 3D maps that this layer requires to be updated in the scene.
void autoRefreshIntervalChanged(int interval)
Emitted when the auto refresh interval changes.
void setMinimumScale(double scale)
Sets the minimum map scale (i.e.
virtual QSet< QgsMapLayerDependency > dependencies() const
Gets the list of dependencies.
void setCustomProperties(const QgsObjectCustomProperties &properties)
Set custom properties for layer.
static Qgis::DataProviderReadFlags providerReadFlags(const QDomNode &layerNode, QgsMapLayer::ReadFlags layerReadFlags)
Returns provider read flag deduced from layer read flags layerReadFlags and a dom node layerNode that...
virtual QString loadNamedStyle(const QString &theURI, bool &resultFlag, bool loadFromLocalDb, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories, Qgis::LoadStyleFlags flags=Qgis::LoadStyleFlags())
Loads a named style from file/local db/datasource db.
virtual QString encodedSource(const QString &source, const QgsReadWriteContext &context) const
Called by writeLayerXML(), used by derived classes to encode provider's specific data source to proje...
QgsCoordinateReferenceSystem crs3D
Definition qgsmaplayer.h:89
virtual void setSubLayerVisibility(const QString &name, bool visible)
Set the visibility of the given sublayer name.
void isValidChanged()
Emitted when the validity of this layer changed.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
bool loadNamedMetadataFromDatabase(const QString &db, const QString &uri, QString &qmd)
Retrieve a named metadata for this layer from a sqlite database.
friend class QgsVectorLayer
virtual bool readXml(const QDomNode &layer_node, QgsReadWriteContext &context)
Called by readLayerXML(), used by children to read state specific to them from project files.
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
Q_DECL_DEPRECATED QString attribution() const
Returns the attribution of the layer used by QGIS Server in GetCapabilities request.
void setOriginalXmlProperties(const QString &originalXmlProperties)
Sets the original XML properties for the layer to originalXmlProperties.
void writeCustomProperties(QDomNode &layerNode, QDomDocument &doc) const
Write custom properties to project file.
QString mRefreshOnNofifyMessage
virtual int listStylesInDatabase(QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError)
Lists all the style in db split into related to the layer and not related to.
virtual QString loadDefaultStyle(bool &resultFlag)
Retrieve the default style for this layer if one exists (either as a .qml file on disk or as a record...
void setDataSource(const QString &dataSource, const QString &baseName=QString(), const QString &provider=QString(), bool loadDefaultStyleFlag=false)
Updates the data source of the layer.
QString mLayerName
Name of the layer - used for display.
virtual QString loadNamedMetadata(const QString &uri, bool &resultFlag)
Retrieve a named metadata for this layer if one exists (either as a .qmd file on disk or as a record ...
virtual bool writeXml(QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context) const
Called by writeLayerXML(), used by children to write state specific to them to project files.
Q_DECL_DEPRECATED bool hasAutoRefreshEnabled() const
Returns true if auto refresh is enabled for the layer.
QString id
Definition qgsmaplayer.h:83
void mapTipTemplateChanged()
Emitted when the map tip template changes.
void triggerRepaint(bool deferredUpdate=false)
Will advise the map canvas (and any other interested party) that this layer requires to be repainted.
QString crsHtmlMetadata() const
Returns a HTML fragment containing the layer's CRS metadata, for use in the htmlMetadata() method.
Q_DECL_DEPRECATED void setAttributionUrl(const QString &attribUrl)
Sets the attribution URL of the layer used by QGIS Server in GetCapabilities request.
QgsMapLayer::SaveStyleResults saveStyleToDatabaseV2(const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Saves QML and SLD representations of the layer's style to a table in the database.
Q_DECL_DEPRECATED void setAutoRefreshEnabled(bool enabled)
Sets whether auto refresh is enabled for the layer.
void setMaximumScale(double scale)
Sets the maximum map scale (i.e.
QgsLayerMetadata metadata
Definition qgsmaplayer.h:86
static QString formatLayerName(const QString &name)
A convenience function to capitalize and format a layer name.
void renderer3DChanged()
Signal emitted when 3D renderer associated with the layer has changed.
Q_DECL_DEPRECATED QString abstract() const
Returns the abstract of the layer used by QGIS Server in GetCapabilities request.
QgsMapLayer(Qgis::LayerType type=Qgis::LayerType::Vector, const QString &name=QString(), const QString &source=QString())
Constructor for QgsMapLayer.
QString originalXmlProperties() const
Returns the XML properties of the original layer as they were when the layer was first read from the ...
Qgis::LayerType type
Definition qgsmaplayer.h:90
Q_DECL_DEPRECATED QString dataUrlFormat() const
Returns the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
QPainter::CompositionMode blendMode() const
Returns the current blending mode for a layer.
Q_DECL_DEPRECATED void setDataUrl(const QString &dataUrl)
Sets the DataUrl of the layer used by QGIS Server in GetCapabilities request.
virtual void setOpacity(double opacity)
Sets the opacity for the layer, where opacity is a value between 0 (totally transparent) and 1....
Q_DECL_DEPRECATED void setKeywordList(const QString &keywords)
Sets the keyword list of the layerused by QGIS Server in GetCapabilities request.
Q_DECL_DEPRECATED void setAttribution(const QString &attrib)
Sets the attribution of the layerused by QGIS Server in GetCapabilities request.
void setFlags(QgsMapLayer::LayerFlags flags)
Returns the flags for this layer.
bool isRefreshOnNotifyEnabled() const
Returns true if the refresh on provider nofification is enabled.
QString publicSource(bool hidePassword=false) const
Gets a version of the internal layer definition that has sensitive bits removed (for example,...
Q_DECL_DEPRECATED QString shortName() const
Returns the short name of the layer used by QGIS Server to identify the layer.
QSet< QgsMapLayerDependency > mDependencies
List of layers that may modify this layer on modification.
void readCustomProperties(const QDomNode &layerNode, const QString &keyStartsWith=QString())
Read custom properties from project file.
virtual Qgis::MapLayerProperties properties() const
Returns the map layer properties of this layer.
virtual QString loadSldStyle(const QString &uri, bool &resultFlag)
Attempts to style the layer using the formatting from an SLD type file.
virtual void setMetadata(const QgsLayerMetadata &metadata)
Sets the layer's metadata store.
virtual bool readStyle(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read the style for the current layer from the DOM node supplied.
virtual QString saveDefaultMetadata(bool &resultFlag)
Save the current metadata of this layer as the default metadata (either as a .qmd file on disk or as ...
virtual bool supportsEditing() const
Returns whether the layer supports editing or not.
Q_DECL_DEPRECATED void setDataUrlFormat(const QString &dataUrlFormat)
Sets the DataUrl format of the layer used by QGIS Server in GetCapabilities request.
QFlags< StyleCategory > StyleCategories
virtual Q_DECL_DEPRECATED void saveStyleToDatabase(const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories=QgsMapLayer::AllStyleCategories)
Saves QML and SLD representations of the layer's style to a table in the database.
Q_INVOKABLE void setCustomProperty(const QString &key, const QVariant &value)
Set a custom property for layer.
QString mProviderKey
Data provider key (name of the data provider).
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
void styleChanged()
Signal emitted whenever a change affects the layer's style.
virtual bool isEditable() const
Returns true if the layer can be edited.
QUndoStack * undoStack()
Returns pointer to layer's undo stack.
std::unique_ptr< QgsDataProvider > mPreloadedProvider
Optionally used when loading a project, it is released when the layer is effectively created.
Q_DECL_DEPRECATED QString title() const
Returns the title of the layer used by QGIS Server in GetCapabilities request.
void crsChanged()
Emitted when the crs() of the layer has changed.
virtual QgsError error() const
Gets current status error.
bool writeLayerXml(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context) const
Stores state in DOM node.
virtual QString styleURI() const
Retrieve the style URI for this layer (either as a .qml file on disk or as a record in the users styl...
void setScaleBasedVisibility(bool enabled)
Sets whether scale based visibility is enabled for the layer.
void dataSourceChanged()
Emitted whenever the layer's data source has been changed.
void idChanged(const QString &id)
Emitted when the layer's ID has been changed.
Q_DECL_DEPRECATED QString dataUrl() const
Returns the DataUrl of the layer used by QGIS Server in GetCapabilities request.
QgsMapLayer::LayerFlags flags
Definition qgsmaplayer.h:96
bool hasScaleBasedVisibility() const
Returns whether scale based visibility is enabled for the layer.
Q_DECL_DEPRECATED QString metadataUrlFormat() const
Returns the metadata format of the layer used by QGIS Server in GetCapabilities request.
void setRefreshOnNofifyMessage(const QString &message)
Set the notification message that triggers repaint If refresh on notification is enabled,...
static QString generateId(const QString &layerName)
Generates an unique identifier for this layer, the generate ID is prefixed by layerName.
QgsProviderMetadata * providerMetadata() const
Returns the layer data provider's metadata, it may be nullptr.
void opacityChanged(double opacity)
Emitted when the layer's opacity is changed, where opacity is a value between 0 (transparent) and 1 (...
virtual bool isModified() const
Returns true if the layer has been modified since last commit/save.
void styleLoaded(QgsMapLayer::StyleCategories categories)
Emitted when a style has been loaded.
virtual QString getStyleFromDatabase(const QString &styleId, QString &msgError)
Returns the named style corresponding to style id provided.
void emitStyleChanged()
Triggers an emission of the styleChanged() signal.
virtual QgsMapLayerTemporalProperties * temporalProperties()
Returns the layer's temporal properties.
QUndoStack * undoStackStyles()
Returns pointer to layer's style undo stack.
void dataChanged()
Data of layer changed.
virtual QStringList subLayers() const
Returns the sublayers of this layer.
virtual QString htmlMetadata() const
Obtain a formatted HTML string containing assorted metadata for this layer.
Q_DECL_DEPRECATED void setMetadataUrlFormat(const QString &metaUrlFormat)
Sets the metadata format of the layer used by QGIS Server in GetCapabilities request.
virtual bool loadNamedStyleFromDatabase(const QString &db, const QString &uri, QString &qml)
Retrieve a named style for this layer from a sqlite database.
void verticalCrsChanged()
Emitted when the verticalCrs() of the layer has changed.
virtual QgsBox3D extent3D() const
Returns the 3D extent of the layer.
static QString extensionPropertyType(PropertyType type)
Returns the extension of a Property.
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
void blendModeChanged(QPainter::CompositionMode blendMode)
Signal emitted when the blend mode is changed, through QgsMapLayer::setBlendMode().
void setName(const QString &name)
Set the display name of the layer.
void setAutoRefreshInterval(int interval)
Sets the auto refresh interval (in milliseconds) for the layer.
virtual bool readSymbology(const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)=0
Read the symbology for the current layer from the DOM node supplied.
Q_DECL_DEPRECATED QString metadataUrl() const
Returns the metadata URL of the layer used by QGIS Server in GetCapabilities request.
virtual void setExtent(const QgsRectangle &rect)
Sets the extent.
virtual void resolveReferences(QgsProject *project)
Resolve references to other layers (kept as layer IDs after reading XML) into layer objects.
QString saveNamedMetadata(const QString &uri, bool &resultFlag)
Save the current metadata of this layer as a named metadata (either as a .qmd file on disk or as a re...
QString mDataSource
Data source description string, varies by layer type.
void setAutoRefreshMode(Qgis::AutoRefreshMode mode)
Sets the automatic refresh mode for the layer.
QString refreshOnNotifyMessage() const
Returns the message that should be notified by the provider to triggerRepaint.
virtual bool readSld(const QDomNode &node, QString &errorMessage)
void setMapTipsEnabled(bool enabled)
Enable or disable map tips for this layer.
virtual QString loadDefaultMetadata(bool &resultFlag)
Retrieve the default metadata for this layer if one exists (either as a .qmd file on disk or as a rec...
virtual QString saveSldStyleV2(bool &resultFlag, QgsSldExportContext &exportContext) const
Saves the properties of this layer to an SLD format file.
@ FlagReadExtentFromXml
Read extent from xml and skip get extent from provider.
@ FlagTrustLayerMetadata
Trust layer metadata. Improves layer load time by skipping expensive checks like primary key unicity,...
@ FlagForceReadOnly
Force open as read only.
void setValid(bool valid)
Sets whether layer is valid or not.
Q_DECL_DEPRECATED QString attributionUrl() const
Returns the attribution URL of the layer used by QGIS Server in GetCapabilities request.
void readCommonStyle(const QDomElement &layerElement, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories)
Read style data common to all layer types.
void customPropertyChanged(const QString &key)
Emitted when a custom property of the layer has been changed or removed.
virtual QDomDocument exportSldStyleV3(QgsSldExportContext &exportContext) const
Export the properties of this layer as SLD style in a QDomDocument.
QgsMapLayer::ReadFlags mReadFlags
Read flags. It's up to the subclass to respect these when restoring state from XML.
double minimumScale() const
Returns the minimum map scale (i.e.
QgsMapLayerStyleManager * styleManager() const
Gets access to the layer's style manager.
Q_DECL_DEPRECATED QString legendUrl() const
Returns the URL for the layer's legend.
void flagsChanged()
Emitted when layer's flags have been modified.
void repaintRequested(bool deferredUpdate=false)
By emitting this signal the layer tells that either appearance or content have been changed and any v...
Q_DECL_DEPRECATED void setLegendUrlFormat(const QString &legendUrlFormat)
Sets the format for a URL based layer legend.
void exportNamedMetadata(QDomDocument &doc, QString &errorMsg) const
Export the current metadata of this layer as named metadata in a QDomDocument.
virtual QString saveNamedStyle(const QString &uri, bool &resultFlag, StyleCategories categories=AllStyleCategories)
Save the properties of this layer as a named style (either as a .qml file on disk or as a record in t...
virtual Q_DECL_DEPRECATED void exportSldStyle(QDomDocument &doc, QString &errorMsg) const
Export the properties of this layer as SLD style in a QDomDocument.
void beforeResolveReferences(QgsProject *project)
Emitted when all layers are loaded and references can be resolved, just before the references of this...
void setMapTipTemplate(const QString &mapTipTemplate)
The mapTip is a pretty, html representation for feature information.
Q_DECL_DEPRECATED void setMetadataUrl(const QString &metaUrl)
Sets the metadata URL of the layer used by QGIS Server in GetCapabilities request.
virtual QgsMapLayerElevationProperties * elevationProperties()
Returns the layer's elevation properties.
bool setVerticalCrs(const QgsCoordinateReferenceSystem &crs, QString *errorMessage=nullptr)
Sets the layer's vertical coordinate reference system.
Q_INVOKABLE QStringList customPropertyKeys() const
Returns list of all keys within custom properties.
QgsProject * project() const
Returns the parent project if this map layer is added to a project.
Q_DECL_DEPRECATED void setMetadataUrlType(const QString &metaUrlType)
Set the metadata type of the layer used by QGIS Server in GetCapabilities request MetadataUrlType ind...
bool mapTipsEnabled
Definition qgsmaplayer.h:94
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags(), QgsDataProvider *preloadedProvider=nullptr)
Sets state from DOM document.
void setLegend(QgsMapLayerLegend *legend)
Assign a legend controller to the map layer.
double opacity
Definition qgsmaplayer.h:92
virtual QString decodedSource(const QString &source, const QString &dataProvider, const QgsReadWriteContext &context) const
Called by readLayerXML(), used by derived classes to decode provider's specific data source from proj...
void nameChanged()
Emitted when the name has been changed.
virtual QString metadataUri() const
Retrieve the metadata URI for this layer (either as a .qmd file on disk or as a record in the users s...
int autoRefreshInterval
Definition qgsmaplayer.h:85
QgsCoordinateReferenceSystem verticalCrs
Definition qgsmaplayer.h:88
virtual bool writeStyle(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write just the symbology information for the layer into the document.
bool mIsRefreshOnNofifyEnabled
virtual Q_INVOKABLE QgsDataProvider * dataProvider()
Returns the layer's data provider, it may be nullptr.
double mLayerOpacity
Layer opacity.
bool mValid
Indicates if the layer is valid and can be drawn.
@ LayerConfiguration
General configuration: identifiable, removable, searchable, display expression, read-only.
@ Symbology
Symbology.
@ Notes
Layer user notes.
@ Temporal
Temporal properties.
@ Rendering
Rendering: scale visibility, simplify method, opacity.
@ Elevation
Elevation settings.
@ Symbology3D
3D symbology
@ CustomProperties
Custom properties (by plugins for instance).
virtual Q_INVOKABLE void reload()
Synchronises with changes in the datasource.
virtual QDateTime timestamp() const
Time stamp of data source in the moment when data/metadata were loaded by provider.
void setProviderType(const QString &providerType)
Sets the providerType (provider key).
void mapTipsEnabledChanged()
Emitted when map tips are enabled or disabled for the layer.
virtual QString saveDefaultStyle(bool &resultFlag, StyleCategories categories)
Save the properties of this layer as the default style (either as a .qml file on disk or as a record ...
QFlags< SaveStyleResult > SaveStyleResults
Results of saving styles to database.
void setRenderer3D(QgsAbstract3DRenderer *renderer)
Sets 3D renderer for the layer.
~QgsMapLayer() override
QString customPropertyHtmlMetadata() const
Returns an HTML fragment containing custom property information, for use in the htmlMetadata() method...
const QgsObjectCustomProperties & customProperties() const
Read all custom properties from layer.
QString generalHtmlMetadata() const
Returns an HTML fragment containing general metadata information, for use in the htmlMetadata() metho...
Q_DECL_DEPRECATED QString metadataUrlType() const
Returns the metadata type of the layer used by QGIS Server in GetCapabilities request.
void writeCommonStyle(QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context, StyleCategories categories=AllStyleCategories) const
Write style data common to all layer types.
double maximumScale() const
Returns the maximum map scale (i.e.
Q_DECL_DEPRECATED QString keywordList() const
Returns the keyword list of the layer used by QGIS Server in GetCapabilities request.
virtual void setLayerOrder(const QStringList &layers)
Reorders the previously selected sublayers of this layer from bottom to top.
void invalidateWgs84Extent()
Invalidates the WGS84 extent.
QString mapTipTemplate
Definition qgsmaplayer.h:93
Q_DECL_DEPRECATED void setTitle(const QString &title)
Sets the title of the layer used by QGIS Server in GetCapabilities request.
PropertyType
Maplayer has a style and a metadata property.
bool mShouldValidateCrs
true if the layer's CRS should be validated and invalid CRSes are not permitted.
void setCrs(const QgsCoordinateReferenceSystem &srs, bool emitSignal=true)
Sets layer's spatial reference system.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE())
Adds a message to the log instance (and creates it if necessary).
Custom exception class which is raised when an operation is not supported.
Simple key-value store (keys = strings, values = variants) that supports loading/saving to/from XML i...
void readXml(const QDomNode &parentNode, const QString &keyStartsWith=QString())
Read store contents from an XML node.
An interface for classes which can visit various object entity (e.g.
A QgsObjectEntityVisitorInterface context object.
Contains information about a PROJ operation.
QString description() const
Description.
Convert from older project file versions to newer.
bool updateRevision(const QgsProjectVersion &version)
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
Describes the version of a project.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:109
bool removeAttachedFile(const QString &path)
Removes the attached file.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Holds data provider key, description, and associated shared library file or function pointer informat...
@ SaveLayerMetadata
Indicates that the provider supports saving native layer metadata.
QString getStyleById(const QString &providerKey, const QString &uri, const QString &styleId, QString &errCause)
Gets a layer style defined by styleId.
QVariantMap decodeUri(const QString &providerKey, const QString &uri)
Breaks a provider data source URI into its component paths (e.g.
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
bool saveLayerMetadata(const QString &providerKey, const QString &uri, const QgsLayerMetadata &metadata, QString &errorMessage)
Saves metadata to the layer corresponding to the specified uri.
bool deleteStyleById(const QString &providerKey, const QString &uri, const QString &styleId, QString &errCause)
Deletes a layer style defined by styleId.
QString loadStoredStyle(const QString &providerKey, const QString &uri, QString &styleName, QString &errCause)
Loads a layer style from the provider storage, reporting its name.
QgsProviderMetadata * providerMetadata(const QString &providerKey) const
Returns metadata of the provider or nullptr if not found.
int listStyles(const QString &providerKey, const QString &uri, QStringList &ids, QStringList &names, QStringList &descriptions, QString &errCause)
Lists stored layer styles in the provider defined by providerKey and uri.
Represents a raster layer.
Q_DECL_DEPRECATED bool writeSld(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props=QVariantMap()) const
Writes the symbology of the layer into the document provided in SLD 1.0.0 format.
Allows entering a context category and takes care of leaving this category on deletion of the class.
A container for the context for various read/write operations on objects.
QgsReadWriteContextCategoryPopper enterCategory(const QString &category, const QString &details=QString()) const
Push a category to the stack.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
A rectangle specified with double values.
static bool equalToOrGreaterThanMinimumScale(const double scale, const double minScale)
Returns whether the scale is equal to or greater than the minScale, taking non-round numbers into acc...
static bool lessThanMaximumScale(const double scale, const double maxScale)
Returns whether the scale is less than the maxScale, taking non-round numbers into account.
void setMetadataUrls(const QList< QgsServerMetadataUrlProperties::MetadataUrl > &metaUrls)
Sets a the list of metadata URL for the layer.
QList< QgsServerMetadataUrlProperties::MetadataUrl > metadataUrls() const
Returns a list of metadataUrl resources associated for the layer.
Holds SLD export options and other information related to SLD export of a QGIS layer style.
QString exportFilePath() const
Returns the export file path for the SLD.
QStringList errors() const
Returns a list of errors which occurred during the conversion.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
void setExportFilePath(const QString &exportFilePath)
Sets the export file path for the SLD to exportFilePath.
QVariantMap extraProperties() const
Returns the open ended set of properties that can drive/inform the SLD encoding.
void pushError(const QString &error)
Pushes a error message generated during the conversion.
static QString capitalize(const QString &string, Qgis::Capitalization capitalization)
Converts a string by applying capitalization rules to the string.
An interface for classes which can visit style entity (e.g.
static Q_INVOKABLE QString toString(Qgis::DistanceUnit unit)
Returns a translated string representing a distance unit.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
Q_DECL_DEPRECATED bool writeSld(QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props=QVariantMap()) const
Writes the symbology of the layer into the document provided in SLD 1.1 format.
Q_INVOKABLE Qgis::GeometryType geometryType() const
Returns point, line or polygon.
static T readFlagAttribute(const QDomElement &element, const QString &attributeName, T defaultValue)
Read a flag value from an attribute of the element.
static QDomElement writeBox3D(const QgsBox3D &box, QDomDocument &doc, const QString &elementName=QStringLiteral("extent3D"))
Encodes a 3D box to a DOM element.
static QgsBox3D readBox3D(const QDomElement &element)
Decodes a DOM element to a 3D box.
static QDomElement writeRectangle(const QgsRectangle &rect, QDomDocument &doc, const QString &elementName=QStringLiteral("extent"))
Encodes a rectangle to a DOM element.
static QgsRectangle readRectangle(const QDomElement &element)
Unique pointer for sqlite3 databases, which automatically closes the database when the pointer goes o...
sqlite3_statement_unique_ptr prepare(const QString &sql, int &resultCode) const
Prepares a sql statement, returning the result.
int open(const QString &path)
Opens the database at the specified file path.
int open_v2(const QString &path, int flags, const char *zVfs)
Opens the database at the specified file path.
Unique pointer for sqlite3 prepared statements, which automatically finalizes the statement when the ...
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
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:6817
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:6524
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6798
const QMap< T, QString > qgsEnumMap()
Returns a map of all enum entries.
Definition qgis.h:6781
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS_NON_FATAL
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.
QString format
Format specification of online resource.