QGIS API Documentation 4.3.0-Master (6402a64e93b)
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 <QString>
69#include <QTextStream>
70#include <QTimer>
71#include <QUrl>
72#include <QXmlStreamReader>
73
74#include "moc_qgsmaplayer.cpp"
75
76using namespace Qt::StringLiterals;
77
79{
80 switch ( type )
81 {
82 case Metadata:
83 return u".qmd"_s;
84
85 case Style:
86 return u".qml"_s;
87 }
88 return QString();
89}
90
91QgsMapLayer::QgsMapLayer( Qgis::LayerType type, const QString &lyrname, 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 switch ( mAutoRefreshMode )
106 {
108 break;
110 triggerRepaint( true );
111 break;
113 reload();
114 break;
115 }
116 } );
117}
118
120{
121 if ( project() && project()->pathResolver().writePath( mDataSource ).startsWith( "attachment:" ) )
122 {
124 }
125}
126
127void QgsMapLayer::clone( QgsMapLayer *layer ) const
128{
130
131 QgsDebugMsgLevel( u"Cloning layer '%1'"_s.arg( name() ), 3 );
132 layer->setBlendMode( blendMode() );
133
134 const auto constStyles = styleManager()->styles();
135 for ( const QString &s : constStyles )
136 {
137 layer->styleManager()->addStyle( s, styleManager()->style( s ) );
138 }
139
140 layer->setName( name() );
141
142 if ( layer->dataProvider() && layer->dataProvider()->elevationProperties() )
143 {
145 layer->mExtent3D = mExtent3D;
146 else
147 layer->mExtent2D = mExtent2D;
148 }
149
150 layer->setMaximumScale( maximumScale() );
151 layer->setMinimumScale( minimumScale() );
153 layer->setDependencies( dependencies() );
155 layer->setCrs( crs() );
156 layer->setCustomProperties( mCustomProperties );
157 layer->setOpacity( mLayerOpacity );
158 layer->setMetadata( mMetadata );
159 mServerProperties->copyTo( layer->serverProperties() );
160}
161
163{
164 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
166
167 return mLayerType;
168}
169
176
178{
180
181 if ( flags == mFlags )
182 return;
183
184 mFlags = flags;
185 emit flagsChanged();
186}
187
194
195QString QgsMapLayer::id() const
196{
197 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
199
200 return mID;
201}
202
203bool QgsMapLayer::setId( const QString &id )
204{
206 if ( qobject_cast< QgsMapLayerStore * >( parent() ) )
207 {
208 // layer is already registered, cannot change id
209 return false;
210 }
211
212 if ( id == mID )
213 return false;
214
215 mID = id;
216 emit idChanged( id );
217 return true;
218}
219
220void QgsMapLayer::setName( const QString &name )
221{
223
224 if ( name == mLayerName )
225 return;
226
228
229 emit nameChanged();
230}
231
232QString QgsMapLayer::name() const
233{
234 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
236
237 QgsDebugMsgLevel( "returning name '" + mLayerName + '\'', 4 );
238 return mLayerName;
239}
240
247
249{
251
252 return nullptr;
253}
254
259
261{
263
264 mServerProperties->setShortName( shortName );
265}
266
268{
270
271 return mServerProperties->shortName();
272}
273
274void QgsMapLayer::setTitle( const QString &title )
275{
277
278 mServerProperties->setTitle( title );
279}
280
281QString QgsMapLayer::title() const
282{
284
285 return mServerProperties->title();
286}
287
288void QgsMapLayer::setAbstract( const QString &abstract )
289{
291
292 mServerProperties->setAbstract( abstract );
293}
294
296{
298
299 return mServerProperties->abstract();
300}
301
302void QgsMapLayer::setKeywordList( const QString &keywords )
303{
305
306 mServerProperties->setKeywordList( keywords );
307}
308
310{
312
313 return mServerProperties->keywordList();
314}
315
316void QgsMapLayer::setDataUrl( const QString &dataUrl )
317{
319
320 mServerProperties->setDataUrl( dataUrl );
321}
322
323QString QgsMapLayer::dataUrl() const
324{
326
327 return mServerProperties->dataUrl();
328}
329
331{
333
334 mServerProperties->setDataUrlFormat( dataUrlFormat );
335}
336
338{
340
341 return mServerProperties->dataUrlFormat();
342}
343
344void QgsMapLayer::setAttribution( const QString &attrib )
345{
347
348 mServerProperties->setAttribution( attrib );
349}
350
352{
354
355 return mServerProperties->attribution();
356}
357
358void QgsMapLayer::setAttributionUrl( const QString &attribUrl )
359{
361
362 mServerProperties->setAttributionUrl( attribUrl );
363}
364
366{
368
369 return mServerProperties->attributionUrl();
370}
371
373{
375
376 mServerProperties->setLegendUrl( legendUrl );
377}
378
380{
382
383 return mServerProperties->legendUrl();
384}
385
387{
389
390 mServerProperties->setLegendUrlFormat( legendUrlFormat );
391}
392
394{
396
397 return mServerProperties->legendUrlFormat();
398}
399
400void QgsMapLayer::setMetadataUrl( const QString &metaUrl )
401{
403
404 QList<QgsMapLayerServerProperties::MetadataUrl> urls = serverProperties()->metadataUrls();
405 if ( urls.isEmpty() )
406 {
407 const QgsMapLayerServerProperties::MetadataUrl newItem = QgsMapLayerServerProperties::MetadataUrl( metaUrl, QLatin1String(), QLatin1String() );
408 urls.prepend( newItem );
409 }
410 else
411 {
412 const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst();
413 const QgsMapLayerServerProperties::MetadataUrl newItem( metaUrl, old.type, old.format );
414 urls.prepend( newItem );
415 }
417}
418
420{
422
423 if ( mServerProperties->metadataUrls().isEmpty() )
424 {
425 return QLatin1String();
426 }
427 else
428 {
429 return mServerProperties->metadataUrls().first().url;
430 }
431}
432
433void QgsMapLayer::setMetadataUrlType( const QString &metaUrlType )
434{
436
437 QList<QgsMapLayerServerProperties::MetadataUrl> urls = mServerProperties->metadataUrls();
438 if ( urls.isEmpty() )
439 {
440 const QgsMapLayerServerProperties::MetadataUrl newItem( QLatin1String(), metaUrlType, QLatin1String() );
441 urls.prepend( newItem );
442 }
443 else
444 {
445 const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst();
446 const QgsMapLayerServerProperties::MetadataUrl newItem( old.url, metaUrlType, old.format );
447 urls.prepend( newItem );
448 }
449 mServerProperties->setMetadataUrls( urls );
450}
451
453{
455
456 if ( mServerProperties->metadataUrls().isEmpty() )
457 {
458 return QLatin1String();
459 }
460 else
461 {
462 return mServerProperties->metadataUrls().first().type;
463 }
464}
465
466void QgsMapLayer::setMetadataUrlFormat( const QString &metaUrlFormat )
467{
469
470 QList<QgsMapLayerServerProperties::MetadataUrl> urls = mServerProperties->metadataUrls();
471 if ( urls.isEmpty() )
472 {
473 const QgsMapLayerServerProperties::MetadataUrl newItem( QLatin1String(), QLatin1String(), metaUrlFormat );
474 urls.prepend( newItem );
475 }
476 else
477 {
478 const QgsMapLayerServerProperties::MetadataUrl old = urls.takeFirst();
479 const QgsMapLayerServerProperties::MetadataUrl newItem( old.url, old.type, metaUrlFormat );
480 urls.prepend( newItem );
481 }
482 mServerProperties->setMetadataUrls( urls );
483}
484
486{
488
489 if ( mServerProperties->metadataUrls().isEmpty() )
490 {
491 return QString();
492 }
493 else
494 {
495 return mServerProperties->metadataUrls().first().format;
496 }
497}
498
499QString QgsMapLayer::publicSource( bool redactCredentials ) const
500{
502
503 // Redo this every time we're asked for it, as we don't know if
504 // dataSource has changed.
506 {
508 }
509 else
510 {
511 return QgsDataSourceUri::removePassword( mDataSource, redactCredentials );
512 }
513}
514
515QString QgsMapLayer::source() const
516{
518
519 return mDataSource;
520}
521
523{
525
526 return mExtent2D.isNull() ? mExtent3D.toRectangle() : mExtent2D;
527}
528
530{
532
533 return mExtent3D;
534}
535
536void QgsMapLayer::setBlendMode( const QPainter::CompositionMode blendMode )
537{
539
540 if ( mBlendMode == blendMode )
541 return;
542
543 mBlendMode = blendMode;
546}
547
548QPainter::CompositionMode QgsMapLayer::blendMode() const
549{
550 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
552
553 return mBlendMode;
554}
555
566
568{
569 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
571
572 return mLayerOpacity;
573}
574
575bool QgsMapLayer::readLayerXml( const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags, QgsDataProvider *preloadedProvider )
576{
578
579 mPreloadedProvider.reset( preloadedProvider );
580 if ( mPreloadedProvider )
581 {
583 }
584
585 bool layerError;
587
588 QDomNode mnl;
589 QDomElement mne;
590
591 // read provider
592 QString provider;
593 mnl = layerElement.namedItem( u"provider"_s );
594 mne = mnl.toElement();
595 provider = mne.text();
596
597 // set data source
598 mnl = layerElement.namedItem( u"datasource"_s );
599 mne = mnl.toElement();
600 const QString dataSourceRaw = mne.text();
601
602 // if the layer needs authentication, ensure the master password is set
603 const thread_local QRegularExpression rx( "authcfg=([a-z]|[A-Z]|[0-9]){7}" );
604 if ( rx.match( dataSourceRaw ).hasMatch() && !QgsApplication::authManager()->setMasterPassword( true ) )
605 {
606 return false;
607 }
608
609 mDataSource = decodedSource( dataSourceRaw, provider, context );
610
611 // Set the CRS from project file, asking the user if necessary.
612 // Make it the saved CRS to have WMS layer projected correctly.
613 // We will still overwrite whatever GDAL etc picks up anyway
614 // further down this function.
615 mnl = layerElement.namedItem( u"layername"_s );
616 mne = mnl.toElement();
617
619 CUSTOM_CRS_VALIDATION savedValidation;
620
621 const QDomNode srsNode = layerElement.namedItem( u"srs"_s );
622 mCRS.readXml( srsNode );
623 mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( mne.text() ) );
625 mCRS.validate();
626 savedCRS = mCRS;
627
628 // Do not validate any projections in children, they will be overwritten anyway.
629 // No need to ask the user for a projections when it is overwritten, is there?
632
633 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Layer" ), mne.text() );
634
635 // the internal name is just the data source basename
636 //QFileInfo dataSourceFileInfo( mDataSource );
637 //internalName = dataSourceFileInfo.baseName();
638
639 // set ID
640 mnl = layerElement.namedItem( u"id"_s );
641 if ( !mnl.isNull() )
642 {
643 mne = mnl.toElement();
644 if ( !mne.isNull() && mne.text().length() > 10 ) // should be at least 17 (yyyyMMddhhmmsszzz)
645 {
646 const QString newId = mne.text();
647 if ( newId != mID )
648 {
649 mID = mne.text();
650 emit idChanged( mID );
651 }
652 }
653 }
654
655 // set name
656 mnl = layerElement.namedItem( u"layername"_s );
657 mne = mnl.toElement();
658
659 //name can be translated
660 setName( context.projectTranslator()->translate( u"project:layers:%1"_s.arg( layerElement.namedItem( u"id"_s ).toElement().text() ), mne.text() ) );
661
662 // now let the children grab what they need from the Dom node.
663 layerError = !readXml( layerElement, context );
664
665 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
666 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
667
668 // overwrite CRS with what we read from project file before the raster/vector
669 // file reading functions changed it. They will if projections is specified in the file.
670 // FIXME: is this necessary? Yes, it is (autumn 2019)
672 mCRS = savedCRS;
673
674 //vertical CRS
675 {
677 const QDomNode verticalCrsNode = layerElement.firstChildElement( u"verticalCrs"_s );
678 if ( !verticalCrsNode.isNull() )
679 {
680 verticalCrs.readXml( verticalCrsNode );
681 }
682 mVerticalCrs = verticalCrs;
683 }
684 rebuildCrs3D();
685
686 serverProperties()->readXml( layerElement );
687
688 // mMetadata.readFromLayer( this );
689 const QDomElement metadataElem = layerElement.firstChildElement( u"resourceMetadata"_s );
690 mMetadata.readMetadataXml( metadataElem, context );
691
692 setAutoRefreshInterval( layerElement.attribute( u"autoRefreshTime"_s, u"0"_s ).toInt() );
693 if ( layerElement.hasAttribute( u"autoRefreshMode"_s ) )
694 {
695 setAutoRefreshMode( qgsEnumKeyToValue( layerElement.attribute( u"autoRefreshMode"_s ), Qgis::AutoRefreshMode::Disabled ) );
696 }
697 else
698 {
699 setAutoRefreshMode( layerElement.attribute( u"autoRefreshEnabled"_s, u"0"_s ).toInt() ? Qgis::AutoRefreshMode::RedrawOnly : Qgis::AutoRefreshMode::Disabled );
700 }
701 setRefreshOnNofifyMessage( layerElement.attribute( u"refreshOnNotifyMessage"_s, QString() ) );
702 setRefreshOnNotifyEnabled( layerElement.attribute( u"refreshOnNotifyEnabled"_s, u"0"_s ).toInt() );
703
704 // geographic extent is read only if necessary
706 {
707 const QDomNode wgs84ExtentNode = layerElement.namedItem( u"wgs84extent"_s );
708 if ( !wgs84ExtentNode.isNull() )
709 mWgs84Extent = QgsXmlUtils::readRectangle( wgs84ExtentNode.toElement() );
710 }
711
712 mLegendPlaceholderImage = layerElement.attribute( u"legendPlaceholderImage"_s );
713
714 if ( verticalCrs() != oldVerticalCrs )
715 emit verticalCrsChanged();
716 if ( mCrs3D != oldCrs3D )
717 emit crs3DChanged();
718
719 return !layerError;
720} // bool QgsMapLayer::readLayerXML
721
722
723bool QgsMapLayer::readXml( const QDomNode &layer_node, QgsReadWriteContext &context )
724{
726
727 Q_UNUSED( layer_node )
728 Q_UNUSED( context )
729 // NOP by default; children will over-ride with behavior specific to them
730
731 // read Extent
733 {
734 const QDomNode extent3DNode = layer_node.namedItem( u"extent3D"_s );
735 if ( extent3DNode.isNull() )
736 {
737 const QDomNode extentNode = layer_node.namedItem( u"extent"_s );
738 if ( !extentNode.isNull() )
739 {
740 mExtent2D = QgsXmlUtils::readRectangle( extentNode.toElement() );
741 }
742 }
743 else
744 {
745 mExtent3D = QgsXmlUtils::readBox3D( extent3DNode.toElement() );
746 }
747 }
748
749 return true;
750} // void QgsMapLayer::readXml
751
752
753bool QgsMapLayer::writeLayerXml( QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context ) const
754{
756
757 if ( !mExtent3D.isNull() && dataProvider() && dataProvider()->elevationProperties() && dataProvider()->elevationProperties()->containsElevationData() )
758 layerElement.appendChild( QgsXmlUtils::writeBox3D( mExtent3D, document ) );
759 else
760 {
761 // Extent might be null because lazily set
762 const QgsRectangle extent2D { mExtent2D.isNull() ? extent() : mExtent2D };
763 if ( !extent2D.isNull() )
764 {
765 layerElement.appendChild( QgsXmlUtils::writeRectangle( extent2D, document ) );
766 }
767 }
768
769 if ( const QgsRectangle lWgs84Extent = wgs84Extent( true ); !lWgs84Extent.isNull() )
770 {
771 layerElement.appendChild( QgsXmlUtils::writeRectangle( lWgs84Extent, document, u"wgs84extent"_s ) );
772 }
773
774 layerElement.setAttribute( u"autoRefreshTime"_s, QString::number( mRefreshTimer->interval() ) );
775 layerElement.setAttribute( u"autoRefreshMode"_s, qgsEnumValueToKey( mAutoRefreshMode ) );
776 layerElement.setAttribute( u"refreshOnNotifyEnabled"_s, mIsRefreshOnNofifyEnabled ? 1 : 0 );
777 layerElement.setAttribute( u"refreshOnNotifyMessage"_s, mRefreshOnNofifyMessage );
778
779 // ID
780 QDomElement layerId = document.createElement( u"id"_s );
781 const QDomText layerIdText = document.createTextNode( id() );
782 layerId.appendChild( layerIdText );
783
784 layerElement.appendChild( layerId );
785
786 if ( mVerticalCrs.isValid() )
787 {
788 QDomElement verticalSrsNode = document.createElement( u"verticalCrs"_s );
789 mVerticalCrs.writeXml( verticalSrsNode, document );
790 layerElement.appendChild( verticalSrsNode );
791 }
792
793 // data source
794 QDomElement dataSource = document.createElement( u"datasource"_s );
795 const QString src = encodedSource( source(), context );
796 const QDomText dataSourceText = document.createTextNode( src );
797 dataSource.appendChild( dataSourceText );
798 layerElement.appendChild( dataSource );
799
800 // layer name
801 QDomElement layerName = document.createElement( u"layername"_s );
802 const QDomText layerNameText = document.createTextNode( name() );
803 layerName.appendChild( layerNameText );
804 layerElement.appendChild( layerName );
805
806 // timestamp if supported
807 if ( timestamp() > QDateTime() )
808 {
809 QDomElement stamp = document.createElement( u"timestamp"_s );
810 const QDomText stampText = document.createTextNode( timestamp().toString( Qt::ISODate ) );
811 stamp.appendChild( stampText );
812 layerElement.appendChild( stamp );
813 }
814
815 layerElement.appendChild( layerName );
816
817 // zorder
818 // This is no longer stored in the project file. It is superfluous since the layers
819 // are written and read in the proper order.
820
821 // spatial reference system id
822 QDomElement mySrsElement = document.createElement( u"srs"_s );
823 mCRS.writeXml( mySrsElement, document );
824 layerElement.appendChild( mySrsElement );
825
826 // layer metadata
827 QDomElement myMetadataElem = document.createElement( u"resourceMetadata"_s );
828 mMetadata.writeMetadataXml( myMetadataElem, document );
829 layerElement.appendChild( myMetadataElem );
830
831 layerElement.setAttribute( u"legendPlaceholderImage"_s, mLegendPlaceholderImage );
832
833 serverProperties()->writeXml( layerElement, document );
834
835 // now append layer node to map layer node
836 return writeXml( layerElement, document, context );
837}
838
839void QgsMapLayer::writeCommonStyle( QDomElement &layerElement, QDomDocument &document, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
840{
842
843 // save categories
844 const QMetaEnum metaEnum = QMetaEnum::fromType<QgsMapLayer::StyleCategories>();
845 const QString categoriesKeys( metaEnum.valueToKeys( static_cast<int>( categories ) ) );
846 layerElement.setAttribute( u"styleCategories"_s, categoriesKeys );
847
848 // Store layer type
849 layerElement.setAttribute( u"layerType"_s, qgsEnumValueToKey( type() ) );
850
851 if ( categories.testFlag( Rendering ) )
852 {
853 // use scale dependent visibility flag
854 layerElement.setAttribute( u"hasScaleBasedVisibilityFlag"_s, hasScaleBasedVisibility() ? 1 : 0 );
855 layerElement.setAttribute( u"maxScale"_s, QString::number( maximumScale() ) );
856 layerElement.setAttribute( u"minScale"_s, QString::number( minimumScale() ) );
857 layerElement.setAttribute( u"autoRefreshMode"_s, qgsEnumValueToKey( mAutoRefreshMode ) );
858 layerElement.setAttribute( u"autoRefreshTime"_s, QString::number( autoRefreshInterval() ) );
859 }
860
861 if ( categories.testFlag( Symbology3D ) )
862 {
863 if ( m3DRenderer )
864 {
865 QDomElement renderer3DElem = document.createElement( u"renderer-3d"_s );
866 renderer3DElem.setAttribute( u"type"_s, m3DRenderer->type() );
867 m3DRenderer->writeXml( renderer3DElem, context );
868 layerElement.appendChild( renderer3DElem );
869 }
870 }
871
872 if ( categories.testFlag( LayerConfiguration ) )
873 {
874 // flags
875 // this code is saving automatically all the flags entries
876 QDomElement layerFlagsElem = document.createElement( u"flags"_s );
877 const auto enumMap = qgsEnumMap<QgsMapLayer::LayerFlag>();
878 for ( auto it = enumMap.constBegin(); it != enumMap.constEnd(); ++it )
879 {
880 const bool flagValue = mFlags.testFlag( it.key() );
881 QDomElement flagElem = document.createElement( it.value() );
882 flagElem.appendChild( document.createTextNode( QString::number( flagValue ) ) );
883 layerFlagsElem.appendChild( flagElem );
884 }
885 layerElement.appendChild( layerFlagsElem );
886 }
887
888 if ( categories.testFlag( Temporal ) )
889 {
890 if ( QgsMapLayerTemporalProperties *properties = const_cast< QgsMapLayer * >( this )->temporalProperties() )
891 properties->writeXml( layerElement, document, context );
892 }
893
894 if ( categories.testFlag( Elevation ) )
895 {
896 if ( QgsMapLayerElevationProperties *properties = const_cast< QgsMapLayer * >( this )->elevationProperties() )
897 properties->writeXml( layerElement, document, context );
898 }
899
900 if ( categories.testFlag( Notes ) && QgsLayerNotesUtils::layerHasNotes( this ) )
901 {
902 QDomElement notesElem = document.createElement( u"userNotes"_s );
903 notesElem.setAttribute( u"value"_s, QgsLayerNotesUtils::layerNotes( this ) );
904 layerElement.appendChild( notesElem );
905 }
906
907 // custom properties
908 if ( categories.testFlag( CustomProperties ) )
909 {
910 writeCustomProperties( layerElement, document );
911 }
912}
913
914
915bool QgsMapLayer::writeXml( QDomNode &layer_node, QDomDocument &document, const QgsReadWriteContext &context ) const
916{
918
919 Q_UNUSED( layer_node )
920 Q_UNUSED( document )
921 Q_UNUSED( context )
922 // NOP by default; children will over-ride with behavior specific to them
923
924 return true;
925}
926
927QString QgsMapLayer::encodedSource( const QString &source, const QgsReadWriteContext &context ) const
928{
930
931 Q_UNUSED( context )
932 return source;
933}
934
935QString QgsMapLayer::decodedSource( const QString &source, const QString &dataProvider, const QgsReadWriteContext &context ) const
936{
938
939 Q_UNUSED( context )
940 Q_UNUSED( dataProvider )
941 return source;
942}
943
945{
947
949 if ( m3DRenderer )
950 m3DRenderer->resolveReferences( *project );
951}
952
953
954void QgsMapLayer::readCustomProperties( const QDomNode &layerNode, const QString &keyStartsWith )
955{
957
958 const QgsObjectCustomProperties oldKeys = mCustomProperties;
959
960 mCustomProperties.readXml( layerNode, keyStartsWith );
961
962 for ( const QString &key : mCustomProperties.keys() )
963 {
964 if ( !oldKeys.contains( key ) || mCustomProperties.value( key ) != oldKeys.value( key ) )
965 {
966 emit customPropertyChanged( key );
967 }
968 }
969}
970
971void QgsMapLayer::writeCustomProperties( QDomNode &layerNode, QDomDocument &doc ) const
972{
974
975 mCustomProperties.writeXml( layerNode, doc );
976}
977
978void QgsMapLayer::readStyleManager( const QDomNode &layerNode )
979{
981
982 const QDomElement styleMgrElem = layerNode.firstChildElement( u"map-layer-style-manager"_s );
983 if ( !styleMgrElem.isNull() )
984 mStyleManager->readXml( styleMgrElem );
985 else
986 mStyleManager->reset();
987}
988
989void QgsMapLayer::writeStyleManager( QDomNode &layerNode, QDomDocument &doc ) const
990{
992
993 if ( mStyleManager )
994 {
995 QDomElement styleMgrElem = doc.createElement( u"map-layer-style-manager"_s );
996 mStyleManager->writeXml( styleMgrElem );
997 layerNode.appendChild( styleMgrElem );
998 }
999}
1000
1002{
1004
1005 return mMapTipTemplate;
1006}
1007
1008void QgsMapLayer::setMapTipTemplate( const QString &mapTip )
1009{
1011
1012 if ( mMapTipTemplate == mapTip )
1013 return;
1014
1015 mMapTipTemplate = mapTip;
1016 emit mapTipTemplateChanged();
1017}
1018
1020{
1022
1023 if ( mMapTipsEnabled == enabled )
1024 return;
1025
1026 mMapTipsEnabled = enabled;
1027 emit mapTipsEnabledChanged();
1028}
1029
1031{
1033
1034 return mMapTipsEnabled;
1035}
1036
1038{
1040 if ( layerReadFlags & QgsMapLayer::FlagTrustLayerMetadata )
1041 {
1043 }
1044 if ( layerReadFlags & QgsMapLayer::FlagForceReadOnly )
1045 {
1047 }
1048
1049 if ( layerReadFlags & QgsMapLayer::FlagReadExtentFromXml )
1050 {
1051 const QDomNode extent3DNode = layerNode.namedItem( u"extent3D"_s );
1052 if ( extent3DNode.isNull() )
1053 {
1054 const QDomNode extentNode = layerNode.namedItem( u"extent"_s );
1055 if ( !extentNode.isNull() )
1056 {
1058 }
1059 }
1060 else
1061 {
1063 }
1064 }
1065
1066 return flags;
1067}
1068
1070{
1071 // because QgsVirtualLayerProvider is not anywhere NEAR thread safe:
1073
1074 return mValid;
1075}
1076
1077#if 0
1078void QgsMapLayer::connectNotify( const char *signal )
1079{
1080 Q_UNUSED( signal )
1081 QgsDebugMsgLevel( "QgsMapLayer connected to " + QString( signal ), 3 );
1082} // QgsMapLayer::connectNotify
1083#endif
1084
1085bool QgsMapLayer::isInScaleRange( double scale ) const
1086{
1087 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1089
1090 // mMinScale (denominator!) is inclusive ( >= --> In range )
1091 // mMaxScale (denominator!) is exclusive ( < --> In range )
1092 return !mScaleBasedVisibility
1093 || ( ( mMinScale == 0 || !QgsScaleUtils::lessThanMaximumScale( scale, mMinScale ) ) && ( mMaxScale == 0 || !QgsScaleUtils::equalToOrGreaterThanMinimumScale( scale, mMaxScale ) ) );
1094}
1095
1097{
1098 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1100
1101 return mScaleBasedVisibility;
1102}
1103
1105{
1107
1108 return mAutoRefreshMode != Qgis::AutoRefreshMode::Disabled;
1109 ;
1110}
1111
1113{
1115
1116 return mAutoRefreshMode;
1117}
1118
1120{
1122
1123 return mRefreshTimer->interval();
1124}
1125
1127{
1129
1130 if ( interval <= 0 )
1131 {
1132 mRefreshTimer->stop();
1133 mRefreshTimer->setInterval( 0 );
1135 }
1136 else
1137 {
1138 mRefreshTimer->setInterval( interval );
1139 }
1140 emit autoRefreshIntervalChanged( mRefreshTimer->isActive() ? mRefreshTimer->interval() : 0 );
1141}
1142
1149
1151{
1153
1154 if ( mode == mAutoRefreshMode )
1155 return;
1156
1157 mAutoRefreshMode = mode;
1158 switch ( mAutoRefreshMode )
1159 {
1161 mRefreshTimer->stop();
1162 break;
1163
1166 if ( mRefreshTimer->interval() > 0 )
1167 mRefreshTimer->start();
1168 break;
1169 }
1170
1171 emit autoRefreshIntervalChanged( mRefreshTimer->isActive() ? mRefreshTimer->interval() : 0 );
1172}
1173
1175{
1177
1178 return mMetadata;
1179}
1180
1182{
1184
1185 mMinScale = scale;
1186}
1187
1189{
1191
1192 return mMinScale;
1193}
1194
1196{
1198
1199 mMaxScale = scale;
1200}
1201
1203{
1205
1206 mScaleBasedVisibility = enabled;
1207}
1208
1210{
1212
1213 return mMaxScale;
1214}
1215
1216QStringList QgsMapLayer::subLayers() const
1217{
1219
1220 return QStringList();
1221}
1222
1223void QgsMapLayer::setLayerOrder( const QStringList &layers )
1224{
1226
1227 Q_UNUSED( layers )
1228}
1229
1230void QgsMapLayer::setSubLayerVisibility( const QString &name, bool vis )
1231{
1233
1234 Q_UNUSED( name )
1235 Q_UNUSED( vis )
1236}
1237
1239{
1241
1242 return false;
1243}
1244
1246{
1247 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1249
1250 return mCRS;
1251}
1252
1254{
1255 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
1257
1258 switch ( mCRS.type() )
1259 {
1260 case Qgis::CrsType::Vertical: // would hope this never happens!
1261 QgsDebugError( u"Layer has a vertical CRS set as the horizontal CRS!"_s );
1262 return mCRS;
1263
1265 return mCRS.verticalCrs();
1266
1278 break;
1279 }
1280 return mVerticalCrs;
1281}
1282
1284{
1286
1287 return mCrs3D.isValid() ? mCrs3D : mCRS;
1288}
1289
1290void QgsMapLayer::setCrs( const QgsCoordinateReferenceSystem &srs, bool emitSignal )
1291{
1293 const bool needToValidateCrs = mShouldValidateCrs && isSpatial() && !srs.isValid() && type() != Qgis::LayerType::Annotation;
1294
1295 if ( mCRS == srs && !needToValidateCrs )
1296 return;
1297
1298 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
1299 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
1300 const QgsCoordinateReferenceSystem oldCrs = mCRS;
1301
1302 mCRS = srs;
1303
1304 if ( needToValidateCrs )
1305 {
1306 mCRS.setValidationHint( tr( "Specify CRS for layer %1" ).arg( name() ) );
1307 mCRS.validate();
1308 }
1309
1310 rebuildCrs3D();
1311
1312 if ( emitSignal && mCRS != oldCrs )
1313 emit crsChanged();
1314
1315 // Did vertical crs also change as a result of this? If so, emit signal
1316 if ( oldVerticalCrs != verticalCrs() )
1317 emit verticalCrsChanged();
1318 if ( oldCrs3D != mCrs3D )
1319 emit crs3DChanged();
1320}
1321
1323{
1325 bool res = true;
1326 if ( crs.isValid() )
1327 {
1328 // validate that passed crs is a vertical crs
1329 switch ( crs.type() )
1330 {
1332 break;
1333
1346 if ( errorMessage )
1347 *errorMessage = QObject::tr( "Specified CRS is a %1 CRS, not a Vertical CRS" ).arg( qgsEnumValueToKey( crs.type() ) );
1348 return false;
1349 }
1350 }
1351
1352 if ( crs != mVerticalCrs )
1353 {
1354 const QgsCoordinateReferenceSystem oldVerticalCrs = verticalCrs();
1355 const QgsCoordinateReferenceSystem oldCrs3D = mCrs3D;
1356
1357 switch ( mCRS.type() )
1358 {
1360 if ( crs != oldVerticalCrs )
1361 {
1362 if ( errorMessage )
1363 *errorMessage = QObject::tr( "Layer CRS is a Compound CRS, specified Vertical CRS will be ignored" );
1364 return false;
1365 }
1366 break;
1367
1369 if ( crs != oldVerticalCrs )
1370 {
1371 if ( errorMessage )
1372 *errorMessage = QObject::tr( "Layer CRS is a Geographic 3D CRS, specified Vertical CRS will be ignored" );
1373 return false;
1374 }
1375 break;
1376
1378 if ( crs != oldVerticalCrs )
1379 {
1380 if ( errorMessage )
1381 *errorMessage = QObject::tr( "Layer CRS is a Geocentric CRS, specified Vertical CRS will be ignored" );
1382 return false;
1383 }
1384 break;
1385
1387 if ( mCRS.hasVerticalAxis() && crs != oldVerticalCrs )
1388 {
1389 if ( errorMessage )
1390 *errorMessage = QObject::tr( "Layer CRS is a Projected 3D CRS, specified Vertical CRS will be ignored" );
1391 return false;
1392 }
1393 break;
1394
1404 break;
1405 }
1406
1407 mVerticalCrs = crs;
1408 res = rebuildCrs3D( errorMessage );
1409
1410 // only emit signal if vertical crs was actually changed, so eg if mCrs is compound
1411 // then we haven't actually changed the vertical crs by this call!
1412 if ( verticalCrs() != oldVerticalCrs )
1413 emit verticalCrsChanged();
1414 if ( mCrs3D != oldCrs3D )
1415 emit crs3DChanged();
1416 }
1417 return res;
1418}
1419
1421{
1423
1424 const QgsDataProvider *lDataProvider = dataProvider();
1425 return lDataProvider ? lDataProvider->transformContext() : QgsCoordinateTransformContext();
1426}
1427
1428QString QgsMapLayer::formatLayerName( const QString &name )
1429{
1430 QString layerName( name );
1431 layerName.replace( '_', ' ' );
1433 return layerName;
1434}
1435
1436QString QgsMapLayer::baseURI( PropertyType type ) const
1437{
1439
1440 QString myURI = publicSource();
1441
1442 // first get base path for delimited text, spatialite and OGR layers,
1443 // as in these cases URI may contain layer name and/or additional
1444 // information. This also strips prefix in case if VSIFILE mechanism
1445 // is used
1446 if ( providerType() == "ogr"_L1 || providerType() == "delimitedtext"_L1 || providerType() == "gdal"_L1 || providerType() == "spatialite"_L1 )
1447 {
1448 QVariantMap components = QgsProviderRegistry::instance()->decodeUri( providerType(), myURI );
1449 myURI = components["path"].toString();
1450 }
1451
1452 QFileInfo myFileInfo( myURI );
1453 QString key;
1454
1455 if ( myFileInfo.exists() )
1456 {
1457 // if file is using the /vsizip/ or /vsigzip/ mechanism, cleanup the name
1458 if ( myURI.endsWith( ".gz"_L1, Qt::CaseInsensitive ) )
1459 myURI.chop( 3 );
1460 else if ( myURI.endsWith( ".zip"_L1, Qt::CaseInsensitive ) )
1461 myURI.chop( 4 );
1462 else if ( myURI.endsWith( ".tar"_L1, Qt::CaseInsensitive ) )
1463 myURI.chop( 4 );
1464 else if ( myURI.endsWith( ".tar.gz"_L1, Qt::CaseInsensitive ) )
1465 myURI.chop( 7 );
1466 else if ( myURI.endsWith( ".tgz"_L1, Qt::CaseInsensitive ) )
1467 myURI.chop( 4 );
1468 myFileInfo.setFile( myURI );
1469 // get the file name for our .qml style file
1470 key = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
1471 }
1472 else
1473 {
1474 key = publicSource();
1475 }
1476
1477 return key;
1478}
1479
1481{
1483
1484 return baseURI( PropertyType::Metadata );
1485}
1486
1487QString QgsMapLayer::saveDefaultMetadata( bool &resultFlag )
1488{
1490
1492 {
1493 if ( metadata->providerCapabilities() & QgsProviderMetadata::SaveLayerMetadata )
1494 {
1495 try
1496 {
1497 QString errorMessage;
1498 resultFlag = QgsProviderRegistry::instance()->saveLayerMetadata( providerType(), mDataSource, mMetadata, errorMessage );
1499 if ( resultFlag )
1500 return tr( "Successfully saved default layer metadata" );
1501 else
1502 return errorMessage;
1503 }
1504 catch ( QgsNotSupportedException &e )
1505 {
1506 resultFlag = false;
1507 return e.what();
1508 }
1509 }
1510 }
1511
1512 // fallback default metadata saving method, for providers which don't support (or implement) saveLayerMetadata
1513 return saveNamedMetadata( metadataUri(), resultFlag );
1514}
1515
1516QString QgsMapLayer::loadDefaultMetadata( bool &resultFlag )
1517{
1519
1520 return loadNamedMetadata( metadataUri(), resultFlag );
1521}
1522
1524{
1526
1527 return baseURI( PropertyType::Style );
1528}
1529
1536
1537bool QgsMapLayer::loadNamedMetadataFromDatabase( const QString &db, const QString &uri, QString &qmd )
1538{
1540
1541 return loadNamedPropertyFromDatabase( db, uri, qmd, PropertyType::Metadata );
1542}
1543
1544bool QgsMapLayer::loadNamedStyleFromDatabase( const QString &db, const QString &uri, QString &qml )
1545{
1547
1548 return loadNamedPropertyFromDatabase( db, uri, qml, PropertyType::Style );
1549}
1550
1551bool QgsMapLayer::loadNamedPropertyFromDatabase( const QString &db, const QString &uri, QString &xml, QgsMapLayer::PropertyType type )
1552{
1554
1555 QgsDebugMsgLevel( u"db = %1 uri = %2"_s.arg( db, uri ), 4 );
1556
1557 bool resultFlag = false;
1558
1559 // read from database
1562
1563 int myResult;
1564
1565 QgsDebugMsgLevel( u"Trying to load style or metadata for \"%1\" from \"%2\""_s.arg( uri, db ), 4 );
1566
1567 if ( db.isEmpty() || !QFile( db ).exists() )
1568 return false;
1569
1570 myResult = database.open_v2( db, SQLITE_OPEN_READONLY, nullptr );
1571 if ( myResult != SQLITE_OK )
1572 {
1573 return false;
1574 }
1575
1576 QString mySql;
1577 switch ( type )
1578 {
1579 case Metadata:
1580 mySql = u"select qmd from tbl_metadata where metadata=?"_s;
1581 break;
1582
1583 case Style:
1584 mySql = u"select qml from tbl_styles where style=?"_s;
1585 break;
1586 }
1587
1588 statement = database.prepare( mySql, myResult );
1589 if ( myResult == SQLITE_OK )
1590 {
1591 QByteArray param = uri.toUtf8();
1592
1593 if ( sqlite3_bind_text( statement.get(), 1, param.data(), param.length(), SQLITE_STATIC ) == SQLITE_OK && sqlite3_step( statement.get() ) == SQLITE_ROW )
1594 {
1595 xml = QString::fromUtf8( reinterpret_cast< const char * >( sqlite3_column_text( statement.get(), 0 ) ) );
1596 resultFlag = true;
1597 }
1598 }
1599 return resultFlag;
1600}
1601
1602
1603QString QgsMapLayer::loadNamedStyle( const QString &uri, bool &resultFlag, QgsMapLayer::StyleCategories categories, Qgis::LoadStyleFlags flags )
1604{
1606
1607 return loadNamedStyle( uri, resultFlag, false, categories, flags );
1608}
1609
1610QString QgsMapLayer::loadNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &namedPropertyExists, bool &propertySuccessfullyLoaded, StyleCategories categories, Qgis::LoadStyleFlags flags )
1611{
1613
1614 QgsDebugMsgLevel( u"uri = %1 myURI = %2"_s.arg( uri, publicSource() ), 4 );
1615
1616 namedPropertyExists = false;
1617 propertySuccessfullyLoaded = false;
1618 if ( uri.isEmpty() )
1619 return QString();
1620
1621 QDomDocument myDocument( u"qgis"_s );
1622
1623 // location of problem associated with errorMsg
1624 int line, column;
1625 QString myErrorMessage;
1626
1627 QFile myFile( uri );
1628 if ( myFile.open( QFile::ReadOnly ) )
1629 {
1630 QgsDebugMsgLevel( u"file found %1"_s.arg( uri ), 2 );
1631 namedPropertyExists = true;
1632
1633 // read file
1634 propertySuccessfullyLoaded = myDocument.setContent( &myFile, &myErrorMessage, &line, &column );
1635 if ( !propertySuccessfullyLoaded )
1636 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1637 myFile.close();
1638 }
1639 else
1640 {
1641 const QFileInfo project( QgsProject::instance()->fileName() ); // skip-keyword-check
1642 QgsDebugMsgLevel( u"project fileName: %1"_s.arg( project.absoluteFilePath() ), 4 );
1643
1644 QString xml;
1645 switch ( type )
1646 {
1647 case QgsMapLayer::Style:
1648 {
1649 if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( u"qgis.qmldb"_s ), uri, xml )
1650 || ( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) )
1651 || loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( u"resources/qgis.qmldb"_s ), uri, xml ) )
1652 {
1653 namedPropertyExists = true;
1654 propertySuccessfullyLoaded = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1655 if ( !propertySuccessfullyLoaded )
1656 {
1657 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1658 }
1659 }
1660 else
1661 {
1663 {
1664 myErrorMessage = tr( "Style not found in database" );
1665 }
1666 }
1667 break;
1668 }
1670 {
1671 if ( loadNamedMetadataFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( u"qgis.qmldb"_s ), uri, xml )
1672 || ( project.exists() && loadNamedMetadataFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) )
1673 || loadNamedMetadataFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( u"resources/qgis.qmldb"_s ), uri, xml ) )
1674 {
1675 namedPropertyExists = true;
1676 propertySuccessfullyLoaded = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1677 if ( !propertySuccessfullyLoaded )
1678 {
1679 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1680 }
1681 }
1682 else
1683 {
1684 myErrorMessage = tr( "Metadata not found in database" );
1685 }
1686 break;
1687 }
1688 }
1689 }
1690
1691 if ( !propertySuccessfullyLoaded )
1692 {
1693 return myErrorMessage;
1694 }
1695
1696 switch ( type )
1697 {
1698 case QgsMapLayer::Style:
1699 propertySuccessfullyLoaded = importNamedStyle( myDocument, myErrorMessage, categories );
1700 if ( !propertySuccessfullyLoaded )
1701 myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1702 break;
1704 propertySuccessfullyLoaded = importNamedMetadata( myDocument, myErrorMessage );
1705 if ( !propertySuccessfullyLoaded )
1706 myErrorMessage = tr( "Loading metadata file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1707 break;
1708 }
1709 return myErrorMessage;
1710}
1711
1712bool QgsMapLayer::importNamedMetadata( QDomDocument &document, QString &errorMessage )
1713{
1715
1716 const QDomElement myRoot = document.firstChildElement( u"qgis"_s );
1717 if ( myRoot.isNull() )
1718 {
1719 errorMessage = tr( "Root <qgis> element could not be found" );
1720 return false;
1721 }
1722
1723 return mMetadata.readMetadataXml( myRoot );
1724}
1725
1726bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMessage, QgsMapLayer::StyleCategories categories )
1727{
1729
1730 const QDomElement myRoot = myDocument.firstChildElement( u"qgis"_s );
1731 if ( myRoot.isNull() )
1732 {
1733 myErrorMessage = tr( "Root <qgis> element could not be found" );
1734 return false;
1735 }
1736
1737 // get style file version string, if any
1738 const QgsProjectVersion fileVersion( myRoot.attribute( u"version"_s ) );
1739 const QgsProjectVersion thisVersion( Qgis::version() );
1740
1741 if ( thisVersion > fileVersion )
1742 {
1743 QgsProjectFileTransform styleFile( myDocument, fileVersion );
1744 styleFile.updateRevision( thisVersion );
1745 }
1746
1747 // Get source categories
1748 const QgsMapLayer::StyleCategories sourceCategories = QgsXmlUtils::readFlagAttribute( myRoot, u"styleCategories"_s, QgsMapLayer::AllStyleCategories );
1749
1750 //Test for matching geometry type on vector layers when applying, if geometry type is given in the style
1751 if ( ( sourceCategories.testFlag( QgsMapLayer::Symbology ) || sourceCategories.testFlag( QgsMapLayer::Symbology3D ) )
1752 && ( categories.testFlag( QgsMapLayer::Symbology ) || categories.testFlag( QgsMapLayer::Symbology3D ) ) )
1753 {
1754 if ( type() == Qgis::LayerType::Vector && !myRoot.firstChildElement( u"layerGeometryType"_s ).isNull() )
1755 {
1756 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( this );
1757 const Qgis::GeometryType importLayerGeometryType = static_cast<Qgis::GeometryType>( myRoot.firstChildElement( u"layerGeometryType"_s ).text().toInt() );
1758 if ( importLayerGeometryType != Qgis::GeometryType::Unknown && vl->geometryType() != importLayerGeometryType )
1759 {
1760 myErrorMessage = tr( "Cannot apply style with symbology to layer with a different geometry type" );
1761 return false;
1762 }
1763 }
1764 }
1765
1766 // Pass the intersection between the desired categories and those that are really in the document
1768 return readSymbology( myRoot, myErrorMessage, context, categories & sourceCategories ); // TODO: support relative paths in QML?
1769}
1770
1771void QgsMapLayer::exportNamedMetadata( QDomDocument &doc, QString &errorMsg ) const
1772{
1774
1775 QDomImplementation DomImplementation;
1776 const QDomDocumentType documentType = DomImplementation.createDocumentType( u"qgis"_s, u"http://mrcc.com/qgis.dtd"_s, u"SYSTEM"_s );
1777 QDomDocument myDocument( documentType );
1778
1779 QDomElement myRootNode = myDocument.createElement( u"qgis"_s );
1780 myRootNode.setAttribute( u"version"_s, Qgis::version() );
1781 myDocument.appendChild( myRootNode );
1782
1783 if ( !mMetadata.writeMetadataXml( myRootNode, myDocument ) )
1784 {
1785 errorMsg = QObject::tr( "Could not save metadata" );
1786 return;
1787 }
1788
1789 doc = myDocument;
1790}
1791
1792void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1793{
1795
1796 QDomImplementation DomImplementation;
1797 const QDomDocumentType documentType = DomImplementation.createDocumentType( u"qgis"_s, u"http://mrcc.com/qgis.dtd"_s, u"SYSTEM"_s );
1798 QDomDocument myDocument( documentType );
1800 QDomElement myRootNode = myDocument.createElement( u"qgis"_s );
1801 myRootNode.setAttribute( u"version"_s, Qgis::version() );
1802 myDocument.appendChild( myRootNode );
1803
1804 if ( !writeSymbology( myRootNode, myDocument, errorMsg, context, categories ) ) // TODO: support relative paths in QML?
1805 {
1806 errorMsg = QObject::tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1807 return;
1808 }
1809
1810 /*
1811 * Check to see if the layer is vector - in which case we should also export its geometryType
1812 * to avoid eventually pasting to a layer with a different geometry
1813 */
1814 if ( type() == Qgis::LayerType::Vector )
1815 {
1816 //Getting the selectionLayer geometry
1817 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( this );
1818 const QString geoType = QString::number( static_cast<int>( vl->geometryType() ) );
1819
1820 //Adding geometryinformation
1821 QDomElement layerGeometryType = myDocument.createElement( u"layerGeometryType"_s );
1822 const QDomText type = myDocument.createTextNode( geoType );
1823
1824 layerGeometryType.appendChild( type );
1825 myRootNode.appendChild( layerGeometryType );
1826 }
1827
1828 doc = myDocument;
1829}
1830
1831QString QgsMapLayer::saveDefaultStyle( bool &resultFlag )
1832{
1834
1835 return saveDefaultStyle( resultFlag, AllStyleCategories );
1836}
1837
1838QString QgsMapLayer::saveDefaultStyle( bool &resultFlag, StyleCategories categories )
1839{
1841
1842 return saveNamedStyle( styleURI(), resultFlag, categories );
1843}
1844
1845QString QgsMapLayer::saveNamedMetadata( const QString &uri, bool &resultFlag )
1846{
1848
1849 return saveNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
1850}
1851
1852QString QgsMapLayer::loadNamedMetadata( const QString &uri, bool &resultFlag )
1853{
1855
1856 bool metadataExists = false;
1857 bool metadataSuccessfullyLoaded = false;
1858 const QString message = loadNamedProperty( uri, QgsMapLayer::Metadata, metadataExists, metadataSuccessfullyLoaded );
1859
1860 // TODO QGIS 5.0 -- fix API for loadNamedMetadata so we can return metadataExists too
1861 ( void ) metadataExists;
1862 resultFlag = metadataSuccessfullyLoaded;
1863 return message;
1864}
1865
1866QString QgsMapLayer::saveNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag, StyleCategories categories )
1867{
1869
1870 // check if the uri is a file or ends with .qml/.qmd,
1871 // which indicates that it should become one
1872 // everything else goes to the database
1873 QString filename;
1874
1875 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1876 if ( vlayer && vlayer->providerType() == "ogr"_L1 )
1877 {
1878 QStringList theURIParts = uri.split( '|' );
1879 filename = theURIParts[0];
1880 }
1881 else if ( vlayer && vlayer->providerType() == "gpx"_L1 )
1882 {
1883 QStringList theURIParts = uri.split( '?' );
1884 filename = theURIParts[0];
1885 }
1886 else if ( vlayer && vlayer->providerType() == "delimitedtext"_L1 )
1887 {
1888 filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
1889 // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
1890 if ( filename.isEmpty() )
1891 filename = uri;
1892 }
1893 else
1894 {
1895 filename = uri;
1896 }
1897
1898 QString myErrorMessage;
1899 QDomDocument myDocument;
1900 switch ( type )
1901 {
1902 case Metadata:
1903 exportNamedMetadata( myDocument, myErrorMessage );
1904 break;
1905
1906 case Style:
1907 const QgsReadWriteContext context;
1908 exportNamedStyle( myDocument, myErrorMessage, context, categories );
1909 break;
1910 }
1911
1912 const QFileInfo myFileInfo( filename );
1913 if ( myFileInfo.exists() || filename.endsWith( QgsMapLayer::extensionPropertyType( type ), Qt::CaseInsensitive ) )
1914 {
1915 const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1916 if ( !myDirInfo.isWritable() )
1917 {
1918 resultFlag = false;
1919 return tr( "The directory containing your dataset needs to be writable!" );
1920 }
1921
1922 // now construct the file name for our .qml or .qmd file
1923 const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
1924
1925 QFile myFile( myFileName );
1926 if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1927 {
1928 QTextStream myFileStream( &myFile );
1929 // save as utf-8 with 2 spaces for indents
1930 myDocument.save( myFileStream, 2 );
1931 myFile.close();
1932 resultFlag = true;
1933 switch ( type )
1934 {
1935 case Metadata:
1936 return tr( "Created default metadata file as %1" ).arg( myFileName );
1937
1938 case Style:
1939 return tr( "Created default style file as %1" ).arg( myFileName );
1940 }
1941 }
1942 else
1943 {
1944 resultFlag = false;
1945 switch ( type )
1946 {
1947 case Metadata:
1948 return tr( "ERROR: Failed to created default metadata file as %1. Check file permissions and retry." ).arg( myFileName );
1949
1950 case Style:
1951 return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
1952 }
1953 }
1954 }
1955 else
1956 {
1957 const QString qml = myDocument.toString();
1958
1959 // read from database
1960 sqlite3_database_unique_ptr database;
1961 sqlite3_statement_unique_ptr statement;
1962
1963 int myResult = database.open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( u"qgis.qmldb"_s ) );
1964 if ( myResult != SQLITE_OK )
1965 {
1966 return tr( "User database could not be opened." );
1967 }
1968
1969 QByteArray param0 = uri.toUtf8();
1970 QByteArray param1 = qml.toUtf8();
1971
1972 QString mySql;
1973 switch ( type )
1974 {
1975 case Metadata:
1976 mySql = u"create table if not exists tbl_metadata(metadata varchar primary key,qmd varchar)"_s;
1977 break;
1978
1979 case Style:
1980 mySql = u"create table if not exists tbl_styles(style varchar primary key,qml varchar)"_s;
1981 break;
1982 }
1983
1984 statement = database.prepare( mySql, myResult );
1985 if ( myResult == SQLITE_OK )
1986 {
1987 if ( sqlite3_step( statement.get() ) != SQLITE_DONE )
1988 {
1989 resultFlag = false;
1990 switch ( type )
1991 {
1992 case Metadata:
1993 return tr( "The metadata table could not be created." );
1994
1995 case Style:
1996 return tr( "The style table could not be created." );
1997 }
1998 }
1999 }
2000
2001 switch ( type )
2002 {
2003 case Metadata:
2004 mySql = u"insert into tbl_metadata(metadata,qmd) values (?,?)"_s;
2005 break;
2006
2007 case Style:
2008 mySql = u"insert into tbl_styles(style,qml) values (?,?)"_s;
2009 break;
2010 }
2011 statement = database.prepare( mySql, myResult );
2012 if ( myResult == SQLITE_OK )
2013 {
2014 if ( sqlite3_bind_text( statement.get(), 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK
2015 && sqlite3_bind_text( statement.get(), 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK
2016 && sqlite3_step( statement.get() ) == SQLITE_DONE )
2017 {
2018 resultFlag = true;
2019 switch ( type )
2020 {
2021 case Metadata:
2022 myErrorMessage = tr( "The metadata %1 was saved to database" ).arg( uri );
2023 break;
2024
2025 case Style:
2026 myErrorMessage = tr( "The style %1 was saved to database" ).arg( uri );
2027 break;
2028 }
2029 }
2030 }
2031
2032 if ( !resultFlag )
2033 {
2034 QString mySql;
2035 switch ( type )
2036 {
2037 case Metadata:
2038 mySql = u"update tbl_metadata set qmd=? where metadata=?"_s;
2039 break;
2040
2041 case Style:
2042 mySql = u"update tbl_styles set qml=? where style=?"_s;
2043 break;
2044 }
2045 statement = database.prepare( mySql, myResult );
2046 if ( myResult == SQLITE_OK )
2047 {
2048 if ( sqlite3_bind_text( statement.get(), 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK
2049 && sqlite3_bind_text( statement.get(), 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK
2050 && sqlite3_step( statement.get() ) == SQLITE_DONE )
2051 {
2052 resultFlag = true;
2053 switch ( type )
2054 {
2055 case Metadata:
2056 myErrorMessage = tr( "The metadata %1 was updated in the database." ).arg( uri );
2057 break;
2058
2059 case Style:
2060 myErrorMessage = tr( "The style %1 was updated in the database." ).arg( uri );
2061 break;
2062 }
2063 }
2064 else
2065 {
2066 resultFlag = false;
2067 switch ( type )
2068 {
2069 case Metadata:
2070 myErrorMessage = tr( "The metadata %1 could not be updated in the database." ).arg( uri );
2071 break;
2072
2073 case Style:
2074 myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( uri );
2075 break;
2076 }
2077 }
2078 }
2079 else
2080 {
2081 resultFlag = false;
2082 switch ( type )
2083 {
2084 case Metadata:
2085 myErrorMessage = tr( "The metadata %1 could not be inserted into database." ).arg( uri );
2086 break;
2087
2088 case Style:
2089 myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( uri );
2090 break;
2091 }
2092 }
2093 }
2094 }
2095
2096 return myErrorMessage;
2097}
2098
2099QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag, StyleCategories categories )
2100{
2102
2103 return saveNamedProperty( uri, QgsMapLayer::Style, resultFlag, categories );
2104}
2105
2106void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
2107{
2108 QgsSldExportContext exportContext;
2109 doc = exportSldStyleV3( exportContext );
2110 if ( !exportContext.errors().empty() )
2111 errorMsg = exportContext.errors().join( "\n" );
2112}
2113
2114void QgsMapLayer::exportSldStyleV2( QDomDocument &doc, QString &errorMsg, QgsSldExportContext &exportContext ) const
2115{
2117 doc = exportSldStyleV3( exportContext );
2118 if ( !exportContext.errors().empty() )
2119 errorMsg = exportContext.errors().join( "\n" );
2120}
2121
2122QDomDocument QgsMapLayer::exportSldStyleV3( QgsSldExportContext &exportContext ) const
2123{
2125
2126 QDomDocument myDocument = QDomDocument();
2127
2128 const QDomNode header = myDocument.createProcessingInstruction( u"xml"_s, u"version=\"1.0\" encoding=\"UTF-8\""_s );
2129 myDocument.appendChild( header );
2130
2131 const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
2132 const QgsRasterLayer *rlayer = qobject_cast<const QgsRasterLayer *>( this );
2133 if ( !vlayer && !rlayer )
2134 {
2135 exportContext.pushError( tr( "Could not save symbology because:\n%1" ).arg( tr( "Only vector and raster layers are supported" ) ) );
2136 return myDocument;
2137 }
2138
2139 // Create the root element
2140 QDomElement root = myDocument.createElementNS( u"http://www.opengis.net/sld"_s, u"StyledLayerDescriptor"_s );
2141 QDomElement layerNode;
2142 if ( vlayer )
2143 {
2144 root.setAttribute( u"version"_s, u"1.1.0"_s );
2145 root.setAttribute( u"xsi:schemaLocation"_s, u"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd"_s );
2146 root.setAttribute( u"xmlns:ogc"_s, u"http://www.opengis.net/ogc"_s );
2147 root.setAttribute( u"xmlns:se"_s, u"http://www.opengis.net/se"_s );
2148 root.setAttribute( u"xmlns:xlink"_s, u"http://www.w3.org/1999/xlink"_s );
2149 root.setAttribute( u"xmlns:xsi"_s, u"http://www.w3.org/2001/XMLSchema-instance"_s );
2150 myDocument.appendChild( root );
2151
2152 // Create the NamedLayer element
2153 layerNode = myDocument.createElement( u"NamedLayer"_s );
2154 root.appendChild( layerNode );
2155 }
2156
2157 // note: Only SLD 1.0 version is generated because seems none is using SE1.1.0 at least for rasters
2158 if ( rlayer )
2159 {
2160 // Create the root element
2161 root.setAttribute( u"version"_s, u"1.0.0"_s );
2162 root.setAttribute( u"xmlns:gml"_s, u"http://www.opengis.net/gml"_s );
2163 root.setAttribute( u"xmlns:ogc"_s, u"http://www.opengis.net/ogc"_s );
2164 root.setAttribute( u"xmlns:sld"_s, u"http://www.opengis.net/sld"_s );
2165 myDocument.appendChild( root );
2166
2167 // Create the NamedLayer element
2168 layerNode = myDocument.createElement( u"UserLayer"_s );
2169 root.appendChild( layerNode );
2170 }
2171
2172 QVariantMap props = exportContext.extraProperties();
2173
2174 QVariant context;
2175 context.setValue( exportContext );
2176
2177 // TODO -- move this to proper members of QgsSldExportContext
2178 props[u"SldExportContext"_s] = context;
2179
2181 {
2182 props[u"scaleMinDenom"_s] = QString::number( mMinScale );
2183 props[u"scaleMaxDenom"_s] = QString::number( mMaxScale );
2184 }
2185 exportContext.setExtraProperties( props );
2186
2187 if ( vlayer )
2188 {
2189 if ( !vlayer->writeSld( layerNode, myDocument, exportContext ) )
2190 {
2191 return myDocument;
2192 }
2193 }
2194 else if ( rlayer )
2195 {
2196 if ( !rlayer->writeSld( layerNode, myDocument, exportContext ) )
2197 {
2198 return myDocument;
2199 }
2200 }
2201
2202 return myDocument;
2203}
2204
2205QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const
2206{
2207 QgsSldExportContext context;
2208 context.setExportFilePath( uri );
2209 return saveSldStyleV2( resultFlag, context );
2210}
2211
2212QString QgsMapLayer::saveSldStyleV2( bool &resultFlag, QgsSldExportContext &exportContext ) const
2213{
2215
2216 const QgsMapLayer *mlayer = qobject_cast<const QgsMapLayer *>( this );
2217
2218 const QString uri { exportContext.exportFilePath() };
2219
2220 // check if the uri is a file or ends with .sld,
2221 // which indicates that it should become one
2222 QString filename;
2223 if ( mlayer->providerType() == "ogr"_L1 )
2224 {
2225 QStringList theURIParts = uri.split( '|' );
2226 filename = theURIParts[0];
2227 }
2228 else if ( mlayer->providerType() == "gpx"_L1 )
2229 {
2230 QStringList theURIParts = uri.split( '?' );
2231 filename = theURIParts[0];
2232 }
2233 else if ( mlayer->providerType() == "delimitedtext"_L1 )
2234 {
2235 filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
2236 // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
2237 if ( filename.isEmpty() )
2238 filename = uri;
2239 }
2240 else
2241 {
2242 filename = uri;
2243 }
2244
2245 const QFileInfo myFileInfo( filename );
2246 if ( myFileInfo.exists() || filename.endsWith( ".sld"_L1, Qt::CaseInsensitive ) )
2247 {
2248 const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
2249 if ( !myDirInfo.isWritable() )
2250 {
2251 resultFlag = false;
2252 return tr( "The directory containing your dataset needs to be writable!" );
2253 }
2254
2255 // now construct the file name for our .sld style file
2256 const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";
2257
2258 QgsSldExportContext context { exportContext };
2259 context.setExportFilePath( myFileName );
2260
2261 QDomDocument myDocument = mlayer->exportSldStyleV3( context );
2262
2263 if ( !context.errors().empty() )
2264 {
2265 resultFlag = false;
2266 return context.errors().join( '\n' );
2267 }
2268
2269 QFile myFile( myFileName );
2270 if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
2271 {
2272 QTextStream myFileStream( &myFile );
2273 // save as utf-8 with 2 spaces for indents
2274 myDocument.save( myFileStream, 2 );
2275 myFile.close();
2276 resultFlag = true;
2277 return tr( "Created default style file as %1" ).arg( myFileName );
2278 }
2279 }
2280
2281 resultFlag = false;
2282 return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
2283}
2284
2285QString QgsMapLayer::loadSldStyle( const QString &uri, bool &resultFlag )
2286{
2288
2289 resultFlag = false;
2290
2291 QDomDocument myDocument;
2292
2293 // location of problem associated with errorMsg
2294 int line = 0, column = 0;
2295 QString myErrorMessage;
2296
2297 QFile myFile( uri );
2298 if ( myFile.open( QFile::ReadOnly ) )
2299 {
2300 // read file
2301#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
2302 QXmlStreamReader xmlReader( &myFile );
2303 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( u"sld"_s, u"http://www.opengis.net/sld"_s ) );
2304 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( u"fes"_s, u"http://www.opengis.net/fes/2.0"_s ) );
2305 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( u"ogc"_s, u"http://www.opengis.net/ogc"_s ) );
2306 const QDomDocument::ParseResult result = myDocument.setContent( &xmlReader, QDomDocument::ParseOption::UseNamespaceProcessing );
2307 if ( result )
2308 {
2309 resultFlag = true;
2310 }
2311 else
2312 {
2313 myErrorMessage = result.errorMessage;
2314 line = result.errorLine;
2315 column = result.errorColumn;
2316 }
2317#else
2318 resultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
2319#endif
2320 if ( !resultFlag )
2321 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
2322 myFile.close();
2323 }
2324 else
2325 {
2326 myErrorMessage = tr( "Unable to open file %1" ).arg( uri );
2327 }
2328
2329 if ( !resultFlag )
2330 {
2331 return myErrorMessage;
2332 }
2333
2334 // check for root SLD element
2335 const QDomElement myRoot = myDocument.firstChildElement( u"StyledLayerDescriptor"_s );
2336 if ( myRoot.isNull() )
2337 {
2338 myErrorMessage = u"Error: StyledLayerDescriptor element not found in %1"_s.arg( uri );
2339 resultFlag = false;
2340 return myErrorMessage;
2341 }
2342
2343 // now get the style node out and pass it over to the layer
2344 // to deserialise...
2345 const QDomElement namedLayerElem = myRoot.firstChildElement( u"NamedLayer"_s );
2346 if ( namedLayerElem.isNull() )
2347 {
2348 myErrorMessage = u"Info: NamedLayer element not found."_s;
2349 resultFlag = false;
2350 return myErrorMessage;
2351 }
2352
2353 QString errorMsg;
2354 resultFlag = readSld( namedLayerElem, errorMsg );
2355 if ( !resultFlag )
2356 {
2357 myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, errorMsg );
2358 return myErrorMessage;
2359 }
2360
2361 return QString();
2362}
2363
2364bool QgsMapLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
2365{
2367
2368 Q_UNUSED( node )
2369 Q_UNUSED( errorMessage )
2370 Q_UNUSED( context )
2371 Q_UNUSED( categories )
2372 return false;
2373}
2374
2375bool QgsMapLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2376{
2378
2379 Q_UNUSED( node )
2380 Q_UNUSED( doc )
2381 Q_UNUSED( errorMessage )
2382 Q_UNUSED( context )
2383 Q_UNUSED( categories )
2384 return false;
2385}
2386
2387
2388void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, bool loadDefaultStyleFlag )
2389{
2391
2393
2395 if ( loadDefaultStyleFlag )
2396 {
2398 }
2399
2401 {
2403 }
2404 setDataSource( dataSource, baseName.isEmpty() ? mLayerName : baseName, provider.isEmpty() ? mProviderKey : provider, options, flags );
2405}
2406
2407void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag )
2408{
2410
2412 if ( loadDefaultStyleFlag )
2413 {
2415 }
2416
2418 {
2420 }
2421 setDataSource( dataSource, baseName, provider, options, flags );
2422}
2423
2424void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
2425{
2427
2429 {
2431 }
2432 setDataSourcePrivate( dataSource, baseName, provider, options, flags );
2433 emit dataSourceChanged();
2434 emit dataChanged();
2436}
2437
2438
2439void QgsMapLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
2440{
2442
2443 Q_UNUSED( dataSource )
2444 Q_UNUSED( baseName )
2445 Q_UNUSED( provider )
2446 Q_UNUSED( options )
2447 Q_UNUSED( flags )
2448}
2449
2450
2452{
2454
2455 return mProviderKey;
2456}
2457
2458void QgsMapLayer::readCommonStyle( const QDomElement &layerElement, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
2459{
2461
2462 if ( categories.testFlag( Symbology3D ) )
2463 {
2464 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "3D Symbology" ) );
2465
2466 QgsAbstract3DRenderer *r3D = nullptr;
2467 QDomElement renderer3DElem = layerElement.firstChildElement( u"renderer-3d"_s );
2468 if ( !renderer3DElem.isNull() )
2469 {
2470 const QString type3D = renderer3DElem.attribute( u"type"_s );
2472 if ( meta3D )
2473 {
2474 r3D = meta3D->createRenderer( renderer3DElem, context );
2475 }
2476 }
2477 setRenderer3D( r3D );
2478 }
2479
2480 if ( categories.testFlag( CustomProperties ) )
2481 {
2482 // read custom properties before passing reading further to a subclass, so that
2483 // the subclass can also read custom properties
2484 readCustomProperties( layerElement );
2485 }
2486
2487 // use scale dependent visibility flag
2488 if ( categories.testFlag( Rendering ) )
2489 {
2490 setScaleBasedVisibility( layerElement.attribute( u"hasScaleBasedVisibilityFlag"_s ).toInt() == 1 );
2491 if ( layerElement.hasAttribute( u"minimumScale"_s ) )
2492 {
2493 // older element, when scales were reversed
2494 setMaximumScale( layerElement.attribute( u"minimumScale"_s ).toDouble() );
2495 setMinimumScale( layerElement.attribute( u"maximumScale"_s ).toDouble() );
2496 }
2497 else
2498 {
2499 setMaximumScale( layerElement.attribute( u"maxScale"_s ).toDouble() );
2500 setMinimumScale( layerElement.attribute( u"minScale"_s ).toDouble() );
2501 }
2502 if ( layerElement.hasAttribute( u"autoRefreshMode"_s ) )
2503 {
2504 setAutoRefreshInterval( layerElement.attribute( u"autoRefreshTime"_s ).toInt() );
2505 setAutoRefreshMode( qgsEnumKeyToValue( layerElement.attribute( u"autoRefreshMode"_s ), Qgis::AutoRefreshMode::Disabled ) );
2506 }
2507 }
2508
2509 if ( categories.testFlag( LayerConfiguration ) )
2510 {
2511 // flags
2512 const QDomElement flagsElem = layerElement.firstChildElement( u"flags"_s );
2513 LayerFlags flags = mFlags;
2514 const auto enumMap = qgsEnumMap<QgsMapLayer::LayerFlag>();
2515 for ( auto it = enumMap.constBegin(); it != enumMap.constEnd(); ++it )
2516 {
2517 const QDomNode flagNode = flagsElem.namedItem( it.value() );
2518 if ( flagNode.isNull() )
2519 continue;
2520 const bool flagValue = flagNode.toElement().text() == "1" ? true : false;
2521 if ( flags.testFlag( it.key() ) && !flagValue )
2522 flags &= ~it.key();
2523 else if ( !flags.testFlag( it.key() ) && flagValue )
2524 flags |= it.key();
2525 }
2526 setFlags( flags );
2527 }
2528
2529 if ( categories.testFlag( Temporal ) )
2530 {
2531 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Temporal" ) );
2532
2534 properties->readXml( layerElement.toElement(), context );
2535 }
2536
2537 if ( categories.testFlag( Elevation ) )
2538 {
2539 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Elevation" ) );
2540
2542 properties->readXml( layerElement.toElement(), context );
2543 }
2544
2545 if ( categories.testFlag( Notes ) )
2546 {
2547 const QDomElement notesElem = layerElement.firstChildElement( u"userNotes"_s );
2548 if ( !notesElem.isNull() )
2549 {
2550 const QString notes = notesElem.attribute( u"value"_s );
2551 QgsLayerNotesUtils::setLayerNotes( this, notes );
2552 }
2553 }
2554}
2555
2557{
2559
2560 return mUndoStack;
2561}
2562
2564{
2566
2567 return mUndoStackStyles;
2568}
2569
2571{
2573
2574 return mCustomProperties.keys();
2575}
2576
2577void QgsMapLayer::setCustomProperty( const QString &key, const QVariant &value )
2578{
2580
2581 if ( !mCustomProperties.contains( key ) || mCustomProperties.value( key ) != value )
2582 {
2583 mCustomProperties.setValue( key, value );
2584 emit customPropertyChanged( key );
2585 }
2586}
2587
2589{
2591
2592 mCustomProperties = properties;
2593 for ( const QString &key : mCustomProperties.keys() )
2594 {
2595 emit customPropertyChanged( key );
2596 }
2597}
2598
2600{
2602
2603 return mCustomProperties;
2604}
2605
2606QVariant QgsMapLayer::customProperty( const QString &value, const QVariant &defaultValue ) const
2607{
2608 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
2610
2611 return mCustomProperties.value( value, defaultValue );
2612}
2613
2614void QgsMapLayer::removeCustomProperty( const QString &key )
2615{
2617
2618 if ( mCustomProperties.contains( key ) )
2619 {
2620 mCustomProperties.remove( key );
2621 emit customPropertyChanged( key );
2622 }
2623}
2624
2625int QgsMapLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError )
2626{
2628
2629 return QgsProviderRegistry::instance()->listStyles( mProviderKey, mDataSource, ids, names, descriptions, msgError );
2630}
2631
2632QString QgsMapLayer::getStyleFromDatabase( const QString &styleId, QString &msgError )
2633{
2635
2636 return QgsProviderRegistry::instance()->getStyleById( mProviderKey, mDataSource, styleId, msgError );
2637}
2638
2639bool QgsMapLayer::deleteStyleFromDatabase( const QString &styleId, QString &msgError )
2640{
2642
2644}
2645
2646void QgsMapLayer::saveStyleToDatabase( const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories )
2647{
2648 saveStyleToDatabaseV2( name, description, useAsDefault, uiFileContent, msgError, categories );
2649}
2650
2652 const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories
2653)
2654{
2656
2658
2659 QString sldStyle, qmlStyle;
2660 QDomDocument qmlDocument;
2661 QgsReadWriteContext context;
2662 exportNamedStyle( qmlDocument, msgError, context, categories );
2663 if ( !msgError.isEmpty() )
2664 {
2666 }
2667 else
2668 {
2669 qmlStyle = qmlDocument.toString();
2670 }
2671
2672 QgsSldExportContext sldContext;
2673 QDomDocument sldDocument = this->exportSldStyleV3( sldContext );
2674 if ( !sldContext.errors().empty() )
2675 {
2677 }
2678 else
2679 {
2680 sldStyle = sldDocument.toString();
2681 }
2682
2683 if ( !QgsProviderRegistry::instance()->saveStyle( mProviderKey, mDataSource, qmlStyle, sldStyle, name, description, uiFileContent, useAsDefault, msgError ) )
2684 {
2686 }
2687 return results;
2688}
2689
2690QString QgsMapLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB, QgsMapLayer::StyleCategories categories, Qgis::LoadStyleFlags flags )
2691{
2693
2694 QString returnMessage;
2695 QString qml, errorMsg;
2696 QString styleName;
2697 if ( !loadFromLocalDB && dataProvider() && dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
2698 {
2700 }
2701
2702 // Style was successfully loaded from provider storage
2703 if ( !qml.isEmpty() )
2704 {
2705 QDomDocument myDocument( u"qgis"_s );
2706 myDocument.setContent( qml );
2707 resultFlag = importNamedStyle( myDocument, errorMsg );
2708 returnMessage = QObject::tr( "Loaded from Provider" );
2709 }
2710 else
2711 {
2713
2714 bool styleExists = false;
2715 bool styleSuccessfullyLoaded = false;
2716
2717 returnMessage = loadNamedProperty( theURI, PropertyType::Style, styleExists, styleSuccessfullyLoaded, categories, flags );
2718
2719 // TODO QGIS 5.0 -- fix API for loadNamedStyle so we can return styleExists too
2720 ( void ) styleExists;
2721 resultFlag = styleSuccessfullyLoaded;
2722 }
2723
2724 if ( !styleName.isEmpty() )
2725 {
2726 styleManager()->renameStyle( styleManager()->currentStyle(), styleName );
2727 }
2728
2729 if ( resultFlag )
2730 emit styleLoaded( categories );
2731
2732 return returnMessage;
2733}
2734
2741
2743{
2745
2746 return false;
2747}
2748
2750{
2752
2753 return false;
2754}
2755
2757{
2759
2760 return true;
2761}
2762
2764{
2766
2767 // invalid layers are temporary? -- who knows?!
2768 if ( !isValid() )
2769 return false;
2770
2771 if ( mProviderKey == "memory"_L1 )
2772 return true;
2773
2774 const QVariantMap sourceParts = QgsProviderRegistry::instance()->decodeUri( mProviderKey, mDataSource );
2775 const QString path = sourceParts.value( u"path"_s ).toString();
2776 if ( path.isEmpty() )
2777 return false;
2778
2779 // check if layer path is inside one of the standard temporary file locations for this platform
2780 const QStringList tempPaths = QStandardPaths::standardLocations( QStandardPaths::TempLocation );
2781 for ( const QString &tempPath : tempPaths )
2782 {
2783 if ( path.startsWith( tempPath ) )
2784 return true;
2785 }
2786
2787 return false;
2788}
2789
2790void QgsMapLayer::setValid( bool valid )
2791{
2793
2794 if ( mValid == valid )
2795 return;
2796
2797 mValid = valid;
2798 emit isValidChanged();
2799}
2800
2802{
2804
2805 if ( legend == mLegend.get() )
2806 return;
2807
2808 mLegend.reset( legend );
2809
2810
2811 if ( mLegend )
2812 {
2813 mLegend->setParent( this );
2814 connect( mLegend.get(), &QgsMapLayerLegend::itemsChanged, this, &QgsMapLayer::legendChanged, Qt::UniqueConnection );
2815 }
2816
2817 emit legendChanged();
2818}
2819
2821{
2823
2824 return mLegend.get();
2825}
2826
2828{
2830
2831 return mStyleManager.get();
2832}
2833
2835{
2837
2838 if ( renderer == m3DRenderer.get() )
2839 return;
2840
2841 m3DRenderer.reset( renderer );
2842
2843 emit renderer3DChanged();
2845}
2846
2848{
2850
2851 return m3DRenderer.get();
2852}
2853
2854void QgsMapLayer::triggerRepaint( bool deferredUpdate )
2855{
2857
2858 if ( mRepaintRequestedFired )
2859 return;
2860 mRepaintRequestedFired = true;
2861 emit repaintRequested( deferredUpdate );
2862 mRepaintRequestedFired = false;
2863}
2864
2871
2873{
2875
2876 mMetadata = metadata;
2877 // mMetadata.saveToLayer( this );
2878 emit metadataChanged();
2879}
2880
2882{
2884
2885 return QString();
2886}
2887
2888QDateTime QgsMapLayer::timestamp() const
2889{
2891
2892 return QDateTime();
2893}
2894
2902
2904{
2905 updateExtent( extent );
2906}
2907
2909{
2911
2912 updateExtent( extent );
2913}
2914
2915bool QgsMapLayer::isReadOnly() const
2916{
2918
2919 return true;
2920}
2921
2923{
2925
2926 return mOriginalXmlProperties;
2927}
2928
2930{
2932
2933 mOriginalXmlProperties = originalXmlProperties;
2934}
2935
2936QString QgsMapLayer::generateId( const QString &layerName )
2937{
2938 return QgsStringUtils::createUniqueId( layerName );
2939}
2940
2942{
2944
2945 return true;
2946}
2947
2954
2956{
2958
2959 return mapTipsEnabled() && !mMapTipTemplate.isEmpty();
2960}
2961
2968
2969QSet<QgsMapLayerDependency> QgsMapLayer::dependencies() const
2970{
2972
2973 return mDependencies;
2974}
2975
2976bool QgsMapLayer::setDependencies( const QSet<QgsMapLayerDependency> &oDeps )
2977{
2979
2980 QSet<QgsMapLayerDependency> deps;
2981 const auto constODeps = oDeps;
2982 for ( const QgsMapLayerDependency &dep : constODeps )
2983 {
2984 if ( dep.origin() == QgsMapLayerDependency::FromUser )
2985 deps << dep;
2986 }
2987
2988 mDependencies = deps;
2989 emit dependenciesChanged();
2990 return true;
2991}
2992
2994{
2996
2997 QgsDataProvider *lDataProvider = dataProvider();
2998
2999 if ( !lDataProvider )
3000 return;
3001
3002 if ( enabled && !isRefreshOnNotifyEnabled() )
3003 {
3004 lDataProvider->setListening( enabled );
3005 connect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
3006 }
3007 else if ( !enabled && isRefreshOnNotifyEnabled() )
3008 {
3009 // we don't want to disable provider listening because someone else could need it (e.g. actions)
3010 disconnect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
3011 }
3012 mIsRefreshOnNofifyEnabled = enabled;
3013}
3014
3016{
3017 // aggregate based tests aren't thread safe
3019
3020 if ( QgsMapLayerStore *store = qobject_cast<QgsMapLayerStore *>( parent() ) )
3021 {
3022 return qobject_cast<QgsProject *>( store->parent() );
3023 }
3024 return nullptr;
3025}
3026
3027void QgsMapLayer::onNotified( const QString &message )
3028{
3030
3031 if ( refreshOnNotifyMessage().isEmpty() || refreshOnNotifyMessage() == message )
3032 {
3034 emit dataChanged();
3035 }
3036}
3037
3038QgsRectangle QgsMapLayer::wgs84Extent( bool forceRecalculate ) const
3039{
3041
3042 if ( !crs().isEarthCrs() )
3043 {
3044 return QgsRectangle();
3045 }
3046
3047 // if this function is called without previous call to extent() it will return empty rectangle as both mExtent2D and mExtent3D are null
3048 // to avoid this call extent here to force extent calculation
3049 ( void ) extent();
3050
3052
3053 if ( !forceRecalculate && !mWgs84Extent.isNull() )
3054 {
3055 wgs84Extent = mWgs84Extent;
3056 }
3057 else if ( !mExtent2D.isNull() || !mExtent3D.isNull() )
3058 {
3059 QgsCoordinateTransform transformer { crs(), QgsCoordinateReferenceSystem( u"EPSG:4326"_s ), transformContext() };
3060 transformer.setBallparkTransformsAreAppropriate( true );
3061 try
3062 {
3063 if ( mExtent2D.isNull() )
3064 wgs84Extent = transformer.transformBoundingBox( mExtent3D.toRectangle() );
3065 else
3066 wgs84Extent = transformer.transformBoundingBox( mExtent2D );
3067 }
3068 catch ( const QgsCsException &cse )
3069 {
3070 QgsMessageLog::logMessage( tr( "Error transforming extent: %1" ).arg( cse.what() ) );
3072 }
3073 }
3074 return wgs84Extent;
3075}
3076
3077void QgsMapLayer::updateExtent( const QgsRectangle &extent ) const
3078{
3080
3081 if ( extent == mExtent2D )
3082 return;
3083
3084 mExtent2D = extent;
3085
3086 // do not update the wgs84 extent if we trust layer metadata
3088 return;
3089
3090 mWgs84Extent = wgs84Extent( true );
3091}
3092
3093void QgsMapLayer::updateExtent( const QgsBox3D &extent ) const
3094{
3096
3097 if ( extent == mExtent3D )
3098 return;
3099
3100 if ( extent.isNull() )
3101 {
3102 if ( !extent.toRectangle().isNull() )
3103 {
3104 // bad 3D extent param but valid in 2d --> update 2D extent
3105 updateExtent( extent.toRectangle() );
3106 }
3107 else
3108 {
3109 QgsDebugMsgLevel( u"Unable to update extent with empty parameter"_s, 1 );
3110 }
3111 }
3112 else
3113 {
3114 mExtent3D = extent;
3115
3116 // do not update the wgs84 extent if we trust layer metadata
3118 return;
3119
3120 mWgs84Extent = wgs84Extent( true );
3121 }
3122}
3123
3124bool QgsMapLayer::rebuildCrs3D( QString *error )
3125{
3126 bool res = true;
3127 if ( !mCRS.isValid() )
3128 {
3129 mCrs3D = QgsCoordinateReferenceSystem();
3130 }
3131 else if ( !mVerticalCrs.isValid() )
3132 {
3133 mCrs3D = mCRS;
3134 }
3135 else
3136 {
3137 switch ( mCRS.type() )
3138 {
3142 mCrs3D = mCRS;
3143 break;
3144
3146 {
3147 QString tempError;
3148 mCrs3D = mCRS.hasVerticalAxis() ? mCRS : QgsCoordinateReferenceSystem::createCompoundCrs( mCRS, mVerticalCrs, error ? *error : tempError );
3149 res = mCrs3D.isValid();
3150 break;
3151 }
3152
3154 // nonsense situation
3155 mCrs3D = QgsCoordinateReferenceSystem();
3156 res = false;
3157 break;
3158
3167 {
3168 QString tempError;
3169 mCrs3D = QgsCoordinateReferenceSystem::createCompoundCrs( mCRS, mVerticalCrs, error ? *error : tempError );
3170 res = mCrs3D.isValid();
3171 break;
3172 }
3173 }
3174 }
3175 return res;
3176}
3177
3179{
3181
3182 // do not update the wgs84 extent if we trust layer metadata
3184 return;
3185
3186 mWgs84Extent = QgsRectangle();
3187}
3188
3190{
3192
3193 QString metadata = u"<h1>"_s + tr( "General" ) + u"</h1>\n<hr>\n"_s + u"<table class=\"list-view\">\n"_s;
3194
3195 // name
3196 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Name" ) + u"</td><td>"_s + name() + u"</td></tr>\n"_s;
3197
3198 const QString lPublicSource = publicSource();
3199
3200 QString path;
3201 bool isLocalPath = false;
3202 if ( dataProvider() )
3203 {
3204 // local path
3205 QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( dataProvider()->name(), lPublicSource );
3206 if ( uriComponents.contains( u"path"_s ) )
3207 {
3208 path = uriComponents[u"path"_s].toString();
3209 QFileInfo fi( path );
3210 if ( fi.exists() )
3211 {
3212 isLocalPath = true;
3213 metadata += u"<tr><td class=\"highlight\">"_s
3214 + tr( "Path" )
3215 + u"</td><td>%1"_s.arg( u"<a href=\"%1\">%2</a>"_s.arg( QUrl::fromLocalFile( path ).toString(), QDir::toNativeSeparators( path ) ) )
3216 + u"</td></tr>\n"_s;
3217
3218 QDateTime lastModified = fi.lastModified();
3219 QString lastModifiedFileName;
3220 QSet<QString> sidecarFiles = QgsFileUtils::sidecarFilesForPath( path );
3221 if ( fi.isFile() )
3222 {
3223 qint64 fileSize = fi.size();
3224 if ( !sidecarFiles.isEmpty() )
3225 {
3226 lastModifiedFileName = fi.fileName();
3227 QStringList sidecarFileNames;
3228 for ( const QString &sidecarFile : sidecarFiles )
3229 {
3230 QFileInfo sidecarFi( sidecarFile );
3231 fileSize += sidecarFi.size();
3232 if ( sidecarFi.lastModified() > lastModified )
3233 {
3234 lastModified = sidecarFi.lastModified();
3235 lastModifiedFileName = sidecarFi.fileName();
3236 }
3237 sidecarFileNames << sidecarFi.fileName();
3238 }
3239 metadata += u"<tr><td class=\"highlight\">"_s
3240 + ( sidecarFiles.size() > 1 ? tr( "Sidecar files" ) : tr( "Sidecar file" ) )
3241 + u"</td><td>%1"_s.arg( sidecarFileNames.join( ", "_L1 ) )
3242 + u"</td></tr>\n"_s;
3243 }
3244 metadata += u"<tr><td class=\"highlight\">"_s
3245 + ( !sidecarFiles.isEmpty() ? tr( "Total size" ) : tr( "Size" ) )
3246 + u"</td><td>%1"_s.arg( QgsFileUtils::representFileSize( fileSize ) )
3247 + u"</td></tr>\n"_s;
3248 }
3249 metadata += u"<tr><td class=\"highlight\">"_s
3250 + tr( "Last modified" )
3251 + u"</td><td>%1"_s.arg( QLocale().toString( fi.lastModified() ) )
3252 + ( !lastModifiedFileName.isEmpty() ? u" (%1)"_s.arg( lastModifiedFileName ) : QString() )
3253 + u"</td></tr>\n"_s;
3254 }
3255 }
3256 if ( uriComponents.contains( u"url"_s ) )
3257 {
3258 QUrl decodedUri = QUrl::fromPercentEncoding( uriComponents[u"url"_s].toString().toLocal8Bit() );
3259 const QString url = decodedUri.toString();
3260 metadata += u"<tr><td class=\"highlight\">"_s + tr( "URL" ) + u"</td><td>%1"_s.arg( u"<a href=\"%1\">%2</a>"_s.arg( url, url ) ) + u"</td></tr>\n"_s;
3261 }
3262 }
3263
3264 // data source
3265 if ( lPublicSource != path || !isLocalPath )
3266 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Source" ) + u"</td><td>%1"_s.arg( lPublicSource != path ? lPublicSource : path ) + u"</td></tr>\n"_s;
3267
3268 // provider
3269 if ( dataProvider() )
3270 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Provider" ) + u"</td><td>%1"_s.arg( dataProvider()->name() ) + u"</td></tr>\n"_s;
3271
3272 // Layer ID
3273 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Layer ID" ) + u"</td><td>%1"_s.arg( id() ) + u"</td></tr>\n"_s;
3274
3275 metadata += "</table>\n<br><br>"_L1;
3276
3277 return metadata;
3278}
3279
3281{
3282 QString metadata;
3283 // custom properties
3284 if ( const auto keys = customPropertyKeys(); !keys.isEmpty() )
3285 {
3286 metadata += u"<h1>"_s + tr( "Custom properties" ) + u"</h1>\n<hr>\n"_s;
3287 metadata += "<table class=\"list-view\">\n<tbody>"_L1;
3288 for ( const QString &key : keys )
3289 {
3290 // keys prefaced with _ are considered private/internal details
3291 if ( key.startsWith( '_' ) )
3292 continue;
3293
3294 const QVariant propValue = customProperty( key );
3295 QString stringValue;
3296 if ( propValue.type() == QVariant::List || propValue.type() == QVariant::StringList )
3297 {
3298 for ( const QString &s : propValue.toStringList() )
3299 {
3300 stringValue += "<p style=\"margin: 0;\">" + s.toHtmlEscaped() + "</p>";
3301 }
3302 }
3303 else
3304 {
3305 stringValue = propValue.toString().toHtmlEscaped();
3306
3307 //if the result string is empty but propValue is not, the conversion has failed
3308 if ( stringValue.isEmpty() && !QgsVariantUtils::isNull( propValue ) )
3309 stringValue = tr( "<i>value cannot be displayed</i>" );
3310 }
3311
3312 metadata += u"<tr><td class=\"highlight\">%1</td><td>%2</td></tr>"_s.arg( key.toHtmlEscaped(), stringValue );
3313 }
3314 metadata += "</tbody></table>\n"_L1;
3315 metadata += "<br><br>\n"_L1;
3316 }
3317 return metadata;
3318}
3319
3321{
3323 QString metadata;
3324
3325 auto addCrsInfo = [&metadata]( const QgsCoordinateReferenceSystem &c, bool includeType, bool includeOperation, bool includeCelestialBody ) {
3326 if ( !c.isValid() )
3327 metadata += u"<tr><td colspan=\"2\" class=\"highlight\">"_s + tr( "Unknown" ) + u"</td></tr>\n"_s;
3328 else
3329 {
3330 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Name" ) + u"</td><td>"_s + c.userFriendlyIdentifier( Qgis::CrsIdentifierType::FullString ) + u"</td></tr>\n"_s;
3331
3332 // map units
3333 metadata += u"<tr><td class=\"highlight\">"_s
3334 + tr( "Units" )
3335 + u"</td><td>"_s
3336 + ( c.isGeographic() ? tr( "Geographic (uses latitude and longitude for coordinates)" ) : QgsUnitTypes::toString( c.mapUnits() ) )
3337 + u"</td></tr>\n"_s;
3338
3339 if ( includeType )
3340 {
3341 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Type" ) + u"</td><td>"_s + QgsCoordinateReferenceSystemUtils::crsTypeToString( c.type() ) + u"</td></tr>\n"_s;
3342 }
3343
3344 if ( includeOperation )
3345 {
3346 // operation
3347 const QgsProjOperation operation = c.operation();
3348 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Method" ) + u"</td><td>"_s + operation.description() + u"</td></tr>\n"_s;
3349 }
3350
3351 if ( includeCelestialBody )
3352 {
3353 // celestial body
3354 try
3355 {
3356 const QString celestialBody = c.celestialBodyName();
3357 if ( !celestialBody.isEmpty() )
3358 {
3359 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Celestial Body" ) + u"</td><td>"_s + celestialBody + u"</td></tr>\n"_s;
3360 }
3361 }
3362 catch ( QgsNotSupportedException & )
3363 {}
3364 }
3365
3366 QString accuracyString;
3367 // dynamic crs with no epoch?
3368 if ( c.isDynamic() && std::isnan( c.coordinateEpoch() ) )
3369 {
3370 accuracyString = tr( "Based on a dynamic CRS, but no coordinate epoch is set. Coordinates are ambiguous and of limited accuracy." );
3371 }
3372
3373 // based on datum ensemble?
3374 try
3375 {
3376 const QgsDatumEnsemble ensemble = c.datumEnsemble();
3377 if ( ensemble.isValid() )
3378 {
3379 QString id;
3380 if ( !ensemble.code().isEmpty() )
3381 id = u"<i>%1</i> (%2:%3)"_s.arg( ensemble.name(), ensemble.authority(), ensemble.code() );
3382 else
3383 id = u"<i>%1</i>”"_s.arg( ensemble.name() );
3384
3385 if ( ensemble.accuracy() > 0 )
3386 {
3387 accuracyString = tr( "Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg( id ).arg( ensemble.accuracy() );
3388 }
3389 else
3390 {
3391 accuracyString = tr( "Based on %1, which has a limited accuracy." ).arg( id );
3392 }
3393 }
3394 }
3395 catch ( QgsNotSupportedException & )
3396 {}
3397
3398 if ( !accuracyString.isEmpty() )
3399 {
3400 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Accuracy" ) + u"</td><td>"_s + accuracyString + u"</td></tr>\n"_s;
3401 }
3402
3403 // static/dynamic
3404 metadata += u"<tr><td class=\"highlight\">"_s
3405 + tr( "Reference" )
3406 + u"</td><td>%1</td></tr>\n"_s.arg( c.isDynamic() ? tr( "Dynamic (relies on a datum which is not plate-fixed)" ) : tr( "Static (relies on a datum which is plate-fixed)" ) );
3407
3408 // coordinate epoch
3409 if ( !std::isnan( c.coordinateEpoch() ) )
3410 {
3411 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Coordinate Epoch" ) + u"</td><td>%1</td></tr>\n"_s.arg( qgsDoubleToString( c.coordinateEpoch(), 3 ) );
3412 }
3413 }
3414 };
3415
3416 metadata += u"<h1>"_s + tr( "Coordinate Reference System (CRS)" ) + u"</h1>\n<hr>\n"_s;
3417 metadata += "<table class=\"list-view\">\n"_L1;
3418 addCrsInfo( crs().horizontalCrs(), true, true, true );
3419 metadata += "</table>\n<br><br>\n"_L1;
3420
3421 if ( verticalCrs().isValid() )
3422 {
3423 metadata += u"<h1>"_s + tr( "Vertical Coordinate Reference System (CRS)" ) + u"</h1>\n<hr>\n"_s;
3424 metadata += "<table class=\"list-view\">\n"_L1;
3425 addCrsInfo( verticalCrs(), false, false, false );
3426 metadata += "</table>\n<br><br>\n"_L1;
3427 }
3428
3429 return metadata;
3430}
static QString version()
Version string.
Definition qgis.cpp:682
@ FullString
Full definition – possibly a very lengthy string, e.g. with no truncation of custom WKT definitions.
Definition qgis.h:2586
@ Vertical
Vertical CRS.
Definition qgis.h:2499
@ Temporal
Temporal CRS.
Definition qgis.h:2502
@ Compound
Compound (horizontal + vertical) CRS.
Definition qgis.h:2501
@ Projected
Projected CRS.
Definition qgis.h:2500
@ Other
Other type.
Definition qgis.h:2505
@ Bound
Bound CRS.
Definition qgis.h:2504
@ DerivedProjected
Derived projected CRS.
Definition qgis.h:2506
@ Unknown
Unknown type.
Definition qgis.h:2494
@ Engineering
Engineering CRS.
Definition qgis.h:2503
@ Geographic3d
3D geopraphic CRS
Definition qgis.h:2498
@ Geodetic
Geodetic CRS.
Definition qgis.h:2495
@ Geographic2d
2D geographic CRS
Definition qgis.h:2497
@ Geocentric
Geocentric CRS.
Definition qgis.h:2496
@ RemoveCredentials
Completely remove credentials (eg passwords) from the URI. This flag is not compatible with the Redac...
Definition qgis.h:1508
@ RedactCredentials
Replace the value of credentials (eg passwords) with 'xxxxxxxx'. This flag is not compatible with the...
Definition qgis.h:1509
@ ForceFirstLetterToCapital
Convert just the first letter of each word to uppercase, leave the rest untouched.
Definition qgis.h:3609
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Unknown
Unknown types.
Definition qgis.h:383
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
Definition qgis.h:512
LayerType
Types of layers that can be added to a map.
Definition qgis.h:206
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
Definition qgis.h:212
@ Vector
Vector layer.
Definition qgis.h:207
QFlags< MapLayerProperty > MapLayerProperties
Map layer properties.
Definition qgis.h:2451
QFlags< LoadStyleFlag > LoadStyleFlags
Flags for loading layer styles.
Definition qgis.h:263
@ LoadDefaultStyle
Reset the layer's style to the default for the datasource.
Definition qgis.h:495
@ ForceReadOnly
Open layer in a read-only mode.
Definition qgis.h:498
@ SkipGetExtent
Skip the extent from provider.
Definition qgis.h:496
@ TrustDataSource
Trust datasource config (primary key unicity, geometry type and srid, etc). Improves provider load ti...
Definition qgis.h:492
@ 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:253
AutoRefreshMode
Map layer automatic refresh modes.
Definition qgis.h:2461
@ RedrawOnly
Redraw current data only.
Definition qgis.h:2464
@ ReloadData
Reload data (and draw the new data).
Definition qgis.h:2463
@ Disabled
Automatic refreshing is disabled.
Definition qgis.h:2462
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:45
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:100
QString code() const
Identification code, e.g.
Definition qgsdatums.h:126
QString authority() const
Authority name, e.g.
Definition qgsdatums.h:121
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:106
QString name() const
Display name of datum ensemble.
Definition qgsdatums.h:111
double accuracy() const
Positional accuracy (in meters).
Definition qgsdatums.h:116
A container for error messages.
Definition qgserror.h:83
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.
@ FromUser
Dependency given by the user.
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 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:87
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.
Q_INVOKABLE 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 Q_INVOKABLE 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:92
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:90
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:86
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:89
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:93
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:99
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:97
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:95
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:88
QgsCoordinateReferenceSystem verticalCrs
Definition qgsmaplayer.h:91
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:96
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(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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:114
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.
static QString createUniqueId(const QString &base=QString())
Generates a unique identifier by appending a random UUID to base.
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 QgsBox3D readBox3D(const QDomElement &element)
Decodes a DOM element to a 3D box.
static QDomElement writeRectangle(const QgsRectangle &rect, QDomDocument &doc, const QString &elementName=u"extent"_s)
Encodes a rectangle to a DOM element.
static QgsRectangle readRectangle(const QDomElement &element)
static QDomElement writeBox3D(const QgsBox3D &box, QDomDocument &doc, const QString &elementName=u"extent3D"_s)
Encodes a 3D box to a DOM 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:7743
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7395
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7724
const QMap< T, QString > qgsEnumMap()
Returns a map of all enum entries.
Definition qgis.h:7707
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7488
void(* CUSTOM_CRS_VALIDATION)(QgsCoordinateReferenceSystem &)
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71
#define QGIS_CHECK_QOBJECT_THREAD_EQUALITY(other)
#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.