QGIS API Documentation 4.3.0-Master (0de80482b60)
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 QString projectFileName;
1642 if ( QgsProject *lProject = project() )
1643 {
1644 projectFileName = lProject->fileName();
1645 }
1646 // TODO QGIS 5.0 -- Remove the else branch with fallback to current QGIS project, the code will work but if the MapLayer is not associated with project it will not provide result
1647 else
1648 {
1649 QgsDebugError( "QgsMapLayer is not associated with QGIS project. Using current QGIS project as fallback. This will stop working in QGIS 5.0." );
1650 projectFileName = QgsProject::instance()->fileName(); // skip-keyword-check
1651 }
1652
1653 const QFileInfo project( projectFileName );
1654 QgsDebugMsgLevel( u"project fileName: %1"_s.arg( project.absoluteFilePath() ), 4 );
1655
1656 QString xml;
1657 switch ( type )
1658 {
1659 case QgsMapLayer::Style:
1660 {
1661 if ( loadNamedStyleFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( u"qgis.qmldb"_s ), uri, xml )
1662 || ( project.exists() && loadNamedStyleFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) )
1663 || loadNamedStyleFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( u"resources/qgis.qmldb"_s ), uri, xml ) )
1664 {
1665 namedPropertyExists = true;
1666 propertySuccessfullyLoaded = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1667 if ( !propertySuccessfullyLoaded )
1668 {
1669 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1670 }
1671 }
1672 else
1673 {
1675 {
1676 myErrorMessage = tr( "Style not found in database" );
1677 }
1678 }
1679 break;
1680 }
1682 {
1683 if ( loadNamedMetadataFromDatabase( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( u"qgis.qmldb"_s ), uri, xml )
1684 || ( project.exists() && loadNamedMetadataFromDatabase( project.absoluteDir().absoluteFilePath( project.baseName() + ".qmldb" ), uri, xml ) )
1685 || loadNamedMetadataFromDatabase( QDir( QgsApplication::pkgDataPath() ).absoluteFilePath( u"resources/qgis.qmldb"_s ), uri, xml ) )
1686 {
1687 namedPropertyExists = true;
1688 propertySuccessfullyLoaded = myDocument.setContent( xml, &myErrorMessage, &line, &column );
1689 if ( !propertySuccessfullyLoaded )
1690 {
1691 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
1692 }
1693 }
1694 else
1695 {
1696 myErrorMessage = tr( "Metadata not found in database" );
1697 }
1698 break;
1699 }
1700 }
1701 }
1702
1703 if ( !propertySuccessfullyLoaded )
1704 {
1705 return myErrorMessage;
1706 }
1707
1708 switch ( type )
1709 {
1710 case QgsMapLayer::Style:
1711 propertySuccessfullyLoaded = importNamedStyle( myDocument, myErrorMessage, categories );
1712 if ( !propertySuccessfullyLoaded )
1713 myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1714 break;
1716 propertySuccessfullyLoaded = importNamedMetadata( myDocument, myErrorMessage );
1717 if ( !propertySuccessfullyLoaded )
1718 myErrorMessage = tr( "Loading metadata file %1 failed because:\n%2" ).arg( uri, myErrorMessage );
1719 break;
1720 }
1721 return myErrorMessage;
1722}
1723
1724bool QgsMapLayer::importNamedMetadata( QDomDocument &document, QString &errorMessage )
1725{
1727
1728 const QDomElement myRoot = document.firstChildElement( u"qgis"_s );
1729 if ( myRoot.isNull() )
1730 {
1731 errorMessage = tr( "Root <qgis> element could not be found" );
1732 return false;
1733 }
1734
1735 return mMetadata.readMetadataXml( myRoot );
1736}
1737
1738bool QgsMapLayer::importNamedStyle( QDomDocument &myDocument, QString &myErrorMessage, QgsMapLayer::StyleCategories categories )
1739{
1741
1742 const QDomElement myRoot = myDocument.firstChildElement( u"qgis"_s );
1743 if ( myRoot.isNull() )
1744 {
1745 myErrorMessage = tr( "Root <qgis> element could not be found" );
1746 return false;
1747 }
1748
1749 // get style file version string, if any
1750 const QgsProjectVersion fileVersion( myRoot.attribute( u"version"_s ) );
1751 const QgsProjectVersion thisVersion( Qgis::version() );
1752
1753 if ( thisVersion > fileVersion )
1754 {
1755 QgsProjectFileTransform styleFile( myDocument, fileVersion );
1756 styleFile.updateRevision( thisVersion );
1757 }
1758
1759 // Get source categories
1760 const QgsMapLayer::StyleCategories sourceCategories = QgsXmlUtils::readFlagAttribute( myRoot, u"styleCategories"_s, QgsMapLayer::AllStyleCategories );
1761
1762 //Test for matching geometry type on vector layers when applying, if geometry type is given in the style
1763 if ( ( sourceCategories.testFlag( QgsMapLayer::Symbology ) || sourceCategories.testFlag( QgsMapLayer::Symbology3D ) )
1764 && ( categories.testFlag( QgsMapLayer::Symbology ) || categories.testFlag( QgsMapLayer::Symbology3D ) ) )
1765 {
1766 if ( type() == Qgis::LayerType::Vector && !myRoot.firstChildElement( u"layerGeometryType"_s ).isNull() )
1767 {
1768 QgsVectorLayer *vl = qobject_cast<QgsVectorLayer *>( this );
1769 const Qgis::GeometryType importLayerGeometryType = static_cast<Qgis::GeometryType>( myRoot.firstChildElement( u"layerGeometryType"_s ).text().toInt() );
1770 if ( importLayerGeometryType != Qgis::GeometryType::Unknown && vl->geometryType() != importLayerGeometryType )
1771 {
1772 myErrorMessage = tr( "Cannot apply style with symbology to layer with a different geometry type" );
1773 return false;
1774 }
1775 }
1776 }
1777
1778 // Pass the intersection between the desired categories and those that are really in the document
1780 return readSymbology( myRoot, myErrorMessage, context, categories & sourceCategories ); // TODO: support relative paths in QML?
1781}
1782
1783void QgsMapLayer::exportNamedMetadata( QDomDocument &doc, QString &errorMsg ) const
1784{
1786
1787 QDomImplementation DomImplementation;
1788 const QDomDocumentType documentType = DomImplementation.createDocumentType( u"qgis"_s, u"http://mrcc.com/qgis.dtd"_s, u"SYSTEM"_s );
1789 QDomDocument myDocument( documentType );
1790
1791 QDomElement myRootNode = myDocument.createElement( u"qgis"_s );
1792 myRootNode.setAttribute( u"version"_s, Qgis::version() );
1793 myDocument.appendChild( myRootNode );
1794
1795 if ( !mMetadata.writeMetadataXml( myRootNode, myDocument ) )
1796 {
1797 errorMsg = QObject::tr( "Could not save metadata" );
1798 return;
1800
1801 doc = myDocument;
1802}
1803
1804void QgsMapLayer::exportNamedStyle( QDomDocument &doc, QString &errorMsg, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
1805{
1807
1808 QDomImplementation DomImplementation;
1809 const QDomDocumentType documentType = DomImplementation.createDocumentType( u"qgis"_s, u"http://mrcc.com/qgis.dtd"_s, u"SYSTEM"_s );
1810 QDomDocument myDocument( documentType );
1811
1812 QDomElement myRootNode = myDocument.createElement( u"qgis"_s );
1813 myRootNode.setAttribute( u"version"_s, Qgis::version() );
1814 myDocument.appendChild( myRootNode );
1815
1816 if ( !writeSymbology( myRootNode, myDocument, errorMsg, context, categories ) ) // TODO: support relative paths in QML?
1817 {
1818 errorMsg = QObject::tr( "Could not save symbology because:\n%1" ).arg( errorMsg );
1819 return;
1820 }
1821
1822 /*
1823 * Check to see if the layer is vector - in which case we should also export its geometryType
1824 * to avoid eventually pasting to a layer with a different geometry
1825 */
1826 if ( type() == Qgis::LayerType::Vector )
1827 {
1828 //Getting the selectionLayer geometry
1829 const QgsVectorLayer *vl = qobject_cast<const QgsVectorLayer *>( this );
1830 const QString geoType = QString::number( static_cast<int>( vl->geometryType() ) );
1831
1832 //Adding geometryinformation
1833 QDomElement layerGeometryType = myDocument.createElement( u"layerGeometryType"_s );
1834 const QDomText type = myDocument.createTextNode( geoType );
1835
1836 layerGeometryType.appendChild( type );
1837 myRootNode.appendChild( layerGeometryType );
1838 }
1839
1840 doc = myDocument;
1841}
1842
1843QString QgsMapLayer::saveDefaultStyle( bool &resultFlag )
1844{
1846
1847 return saveDefaultStyle( resultFlag, AllStyleCategories );
1848}
1849
1850QString QgsMapLayer::saveDefaultStyle( bool &resultFlag, StyleCategories categories )
1851{
1853
1854 return saveNamedStyle( styleURI(), resultFlag, categories );
1855}
1856
1857QString QgsMapLayer::saveNamedMetadata( const QString &uri, bool &resultFlag )
1858{
1860
1861 return saveNamedProperty( uri, QgsMapLayer::Metadata, resultFlag );
1862}
1863
1864QString QgsMapLayer::loadNamedMetadata( const QString &uri, bool &resultFlag )
1865{
1867
1868 bool metadataExists = false;
1869 bool metadataSuccessfullyLoaded = false;
1870 const QString message = loadNamedProperty( uri, QgsMapLayer::Metadata, metadataExists, metadataSuccessfullyLoaded );
1871
1872 // TODO QGIS 5.0 -- fix API for loadNamedMetadata so we can return metadataExists too
1873 ( void ) metadataExists;
1874 resultFlag = metadataSuccessfullyLoaded;
1875 return message;
1876}
1877
1878QString QgsMapLayer::saveNamedProperty( const QString &uri, QgsMapLayer::PropertyType type, bool &resultFlag, StyleCategories categories )
1879{
1881
1882 // check if the uri is a file or ends with .qml/.qmd,
1883 // which indicates that it should become one
1884 // everything else goes to the database
1885 QString filename;
1886
1887 QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( this );
1888 if ( vlayer && vlayer->providerType() == "ogr"_L1 )
1889 {
1890 QStringList theURIParts = uri.split( '|' );
1891 filename = theURIParts[0];
1892 }
1893 else if ( vlayer && vlayer->providerType() == "gpx"_L1 )
1894 {
1895 QStringList theURIParts = uri.split( '?' );
1896 filename = theURIParts[0];
1897 }
1898 else if ( vlayer && vlayer->providerType() == "delimitedtext"_L1 )
1899 {
1900 filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
1901 // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
1902 if ( filename.isEmpty() )
1903 filename = uri;
1904 }
1905 else
1906 {
1907 filename = uri;
1908 }
1909
1910 QString myErrorMessage;
1911 QDomDocument myDocument;
1912 switch ( type )
1913 {
1914 case Metadata:
1915 exportNamedMetadata( myDocument, myErrorMessage );
1916 break;
1917
1918 case Style:
1919 const QgsReadWriteContext context;
1920 exportNamedStyle( myDocument, myErrorMessage, context, categories );
1921 break;
1922 }
1923
1924 const QFileInfo myFileInfo( filename );
1925 if ( myFileInfo.exists() || filename.endsWith( QgsMapLayer::extensionPropertyType( type ), Qt::CaseInsensitive ) )
1926 {
1927 const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
1928 if ( !myDirInfo.isWritable() )
1929 {
1930 resultFlag = false;
1931 return tr( "The directory containing your dataset needs to be writable!" );
1932 }
1933
1934 // now construct the file name for our .qml or .qmd file
1935 const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + QgsMapLayer::extensionPropertyType( type );
1936
1937 QFile myFile( myFileName );
1938 if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
1939 {
1940 QTextStream myFileStream( &myFile );
1941 // save as utf-8 with 2 spaces for indents
1942 myDocument.save( myFileStream, 2 );
1943 myFile.close();
1944 resultFlag = true;
1945 switch ( type )
1946 {
1947 case Metadata:
1948 return tr( "Created default metadata file as %1" ).arg( myFileName );
1949
1950 case Style:
1951 return tr( "Created default style file as %1" ).arg( myFileName );
1952 }
1953 }
1954 else
1955 {
1956 resultFlag = false;
1957 switch ( type )
1958 {
1959 case Metadata:
1960 return tr( "ERROR: Failed to created default metadata file as %1. Check file permissions and retry." ).arg( myFileName );
1961
1962 case Style:
1963 return tr( "ERROR: Failed to created default style file as %1. Check file permissions and retry." ).arg( myFileName );
1964 }
1965 }
1966 }
1967 else
1968 {
1969 const QString qml = myDocument.toString();
1970
1971 // read from database
1972 sqlite3_database_unique_ptr database;
1973 sqlite3_statement_unique_ptr statement;
1974
1975 int myResult = database.open( QDir( QgsApplication::qgisSettingsDirPath() ).absoluteFilePath( u"qgis.qmldb"_s ) );
1976 if ( myResult != SQLITE_OK )
1977 {
1978 return tr( "User database could not be opened." );
1979 }
1980
1981 QByteArray param0 = uri.toUtf8();
1982 QByteArray param1 = qml.toUtf8();
1983
1984 QString mySql;
1985 switch ( type )
1986 {
1987 case Metadata:
1988 mySql = u"create table if not exists tbl_metadata(metadata varchar primary key,qmd varchar)"_s;
1989 break;
1990
1991 case Style:
1992 mySql = u"create table if not exists tbl_styles(style varchar primary key,qml varchar)"_s;
1993 break;
1994 }
1995
1996 statement = database.prepare( mySql, myResult );
1997 if ( myResult == SQLITE_OK )
1998 {
1999 if ( sqlite3_step( statement.get() ) != SQLITE_DONE )
2000 {
2001 resultFlag = false;
2002 switch ( type )
2003 {
2004 case Metadata:
2005 return tr( "The metadata table could not be created." );
2006
2007 case Style:
2008 return tr( "The style table could not be created." );
2009 }
2010 }
2011 }
2012
2013 switch ( type )
2014 {
2015 case Metadata:
2016 mySql = u"insert into tbl_metadata(metadata,qmd) values (?,?)"_s;
2017 break;
2018
2019 case Style:
2020 mySql = u"insert into tbl_styles(style,qml) values (?,?)"_s;
2021 break;
2022 }
2023 statement = database.prepare( mySql, myResult );
2024 if ( myResult == SQLITE_OK )
2025 {
2026 if ( sqlite3_bind_text( statement.get(), 1, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK
2027 && sqlite3_bind_text( statement.get(), 2, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK
2028 && sqlite3_step( statement.get() ) == SQLITE_DONE )
2029 {
2030 resultFlag = true;
2031 switch ( type )
2032 {
2033 case Metadata:
2034 myErrorMessage = tr( "The metadata %1 was saved to database" ).arg( uri );
2035 break;
2036
2037 case Style:
2038 myErrorMessage = tr( "The style %1 was saved to database" ).arg( uri );
2039 break;
2040 }
2041 }
2042 }
2043
2044 if ( !resultFlag )
2045 {
2046 QString mySql;
2047 switch ( type )
2048 {
2049 case Metadata:
2050 mySql = u"update tbl_metadata set qmd=? where metadata=?"_s;
2051 break;
2052
2053 case Style:
2054 mySql = u"update tbl_styles set qml=? where style=?"_s;
2055 break;
2056 }
2057 statement = database.prepare( mySql, myResult );
2058 if ( myResult == SQLITE_OK )
2059 {
2060 if ( sqlite3_bind_text( statement.get(), 2, param0.data(), param0.length(), SQLITE_STATIC ) == SQLITE_OK
2061 && sqlite3_bind_text( statement.get(), 1, param1.data(), param1.length(), SQLITE_STATIC ) == SQLITE_OK
2062 && sqlite3_step( statement.get() ) == SQLITE_DONE )
2063 {
2064 resultFlag = true;
2065 switch ( type )
2066 {
2067 case Metadata:
2068 myErrorMessage = tr( "The metadata %1 was updated in the database." ).arg( uri );
2069 break;
2070
2071 case Style:
2072 myErrorMessage = tr( "The style %1 was updated in the database." ).arg( uri );
2073 break;
2074 }
2075 }
2076 else
2077 {
2078 resultFlag = false;
2079 switch ( type )
2080 {
2081 case Metadata:
2082 myErrorMessage = tr( "The metadata %1 could not be updated in the database." ).arg( uri );
2083 break;
2084
2085 case Style:
2086 myErrorMessage = tr( "The style %1 could not be updated in the database." ).arg( uri );
2087 break;
2088 }
2089 }
2090 }
2091 else
2092 {
2093 resultFlag = false;
2094 switch ( type )
2095 {
2096 case Metadata:
2097 myErrorMessage = tr( "The metadata %1 could not be inserted into database." ).arg( uri );
2098 break;
2099
2100 case Style:
2101 myErrorMessage = tr( "The style %1 could not be inserted into database." ).arg( uri );
2102 break;
2103 }
2104 }
2105 }
2106 }
2107
2108 return myErrorMessage;
2109}
2110
2111QString QgsMapLayer::saveNamedStyle( const QString &uri, bool &resultFlag, StyleCategories categories )
2112{
2114
2115 return saveNamedProperty( uri, QgsMapLayer::Style, resultFlag, categories );
2116}
2117
2118void QgsMapLayer::exportSldStyle( QDomDocument &doc, QString &errorMsg ) const
2119{
2120 QgsSldExportContext exportContext;
2121 doc = exportSldStyleV3( exportContext );
2122 if ( !exportContext.errors().empty() )
2123 errorMsg = exportContext.errors().join( "\n" );
2124}
2125
2126void QgsMapLayer::exportSldStyleV2( QDomDocument &doc, QString &errorMsg, QgsSldExportContext &exportContext ) const
2127{
2129 doc = exportSldStyleV3( exportContext );
2130 if ( !exportContext.errors().empty() )
2131 errorMsg = exportContext.errors().join( "\n" );
2132}
2133
2134QDomDocument QgsMapLayer::exportSldStyleV3( QgsSldExportContext &exportContext ) const
2135{
2137
2138 QDomDocument myDocument = QDomDocument();
2139
2140 const QDomNode header = myDocument.createProcessingInstruction( u"xml"_s, u"version=\"1.0\" encoding=\"UTF-8\""_s );
2141 myDocument.appendChild( header );
2142
2143 const QgsVectorLayer *vlayer = qobject_cast<const QgsVectorLayer *>( this );
2144 const QgsRasterLayer *rlayer = qobject_cast<const QgsRasterLayer *>( this );
2145 if ( !vlayer && !rlayer )
2146 {
2147 exportContext.pushError( tr( "Could not save symbology because:\n%1" ).arg( tr( "Only vector and raster layers are supported" ) ) );
2148 return myDocument;
2149 }
2150
2151 // Create the root element
2152 QDomElement root = myDocument.createElementNS( u"http://www.opengis.net/sld"_s, u"StyledLayerDescriptor"_s );
2153 QDomElement layerNode;
2154 if ( vlayer )
2155 {
2156 root.setAttribute( u"version"_s, u"1.1.0"_s );
2157 root.setAttribute( u"xsi:schemaLocation"_s, u"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd"_s );
2158 root.setAttribute( u"xmlns:ogc"_s, u"http://www.opengis.net/ogc"_s );
2159 root.setAttribute( u"xmlns:se"_s, u"http://www.opengis.net/se"_s );
2160 root.setAttribute( u"xmlns:xlink"_s, u"http://www.w3.org/1999/xlink"_s );
2161 root.setAttribute( u"xmlns:xsi"_s, u"http://www.w3.org/2001/XMLSchema-instance"_s );
2162 myDocument.appendChild( root );
2163
2164 // Create the NamedLayer element
2165 layerNode = myDocument.createElement( u"NamedLayer"_s );
2166 root.appendChild( layerNode );
2167 }
2168
2169 // note: Only SLD 1.0 version is generated because seems none is using SE1.1.0 at least for rasters
2170 if ( rlayer )
2171 {
2172 // Create the root element
2173 root.setAttribute( u"version"_s, u"1.0.0"_s );
2174 root.setAttribute( u"xmlns:gml"_s, u"http://www.opengis.net/gml"_s );
2175 root.setAttribute( u"xmlns:ogc"_s, u"http://www.opengis.net/ogc"_s );
2176 root.setAttribute( u"xmlns:sld"_s, u"http://www.opengis.net/sld"_s );
2177 myDocument.appendChild( root );
2178
2179 // Create the NamedLayer element
2180 layerNode = myDocument.createElement( u"UserLayer"_s );
2181 root.appendChild( layerNode );
2182 }
2183
2184 QVariantMap props = exportContext.extraProperties();
2185
2186 QVariant context;
2187 context.setValue( exportContext );
2188
2189 // TODO -- move this to proper members of QgsSldExportContext
2190 props[u"SldExportContext"_s] = context;
2191
2193 {
2194 props[u"scaleMinDenom"_s] = QString::number( mMinScale );
2195 props[u"scaleMaxDenom"_s] = QString::number( mMaxScale );
2196 }
2197 exportContext.setExtraProperties( props );
2198
2199 if ( vlayer )
2200 {
2201 if ( !vlayer->writeSld( layerNode, myDocument, exportContext ) )
2202 {
2203 return myDocument;
2204 }
2205 }
2206 else if ( rlayer )
2207 {
2208 if ( !rlayer->writeSld( layerNode, myDocument, exportContext ) )
2209 {
2210 return myDocument;
2211 }
2212 }
2213
2214 return myDocument;
2215}
2216
2217QString QgsMapLayer::saveSldStyle( const QString &uri, bool &resultFlag ) const
2218{
2219 QgsSldExportContext context;
2220 context.setExportFilePath( uri );
2221 return saveSldStyleV2( resultFlag, context );
2222}
2223
2224QString QgsMapLayer::saveSldStyleV2( bool &resultFlag, QgsSldExportContext &exportContext ) const
2225{
2227
2228 const QgsMapLayer *mlayer = qobject_cast<const QgsMapLayer *>( this );
2229
2230 const QString uri { exportContext.exportFilePath() };
2231
2232 // check if the uri is a file or ends with .sld,
2233 // which indicates that it should become one
2234 QString filename;
2235 if ( mlayer->providerType() == "ogr"_L1 )
2236 {
2237 QStringList theURIParts = uri.split( '|' );
2238 filename = theURIParts[0];
2239 }
2240 else if ( mlayer->providerType() == "gpx"_L1 )
2241 {
2242 QStringList theURIParts = uri.split( '?' );
2243 filename = theURIParts[0];
2244 }
2245 else if ( mlayer->providerType() == "delimitedtext"_L1 )
2246 {
2247 filename = QUrl::fromEncoded( uri.toLatin1() ).toLocalFile();
2248 // toLocalFile() returns an empty string if theURI is a plain Windows-path, e.g. "C:/style.qml"
2249 if ( filename.isEmpty() )
2250 filename = uri;
2251 }
2252 else
2253 {
2254 filename = uri;
2255 }
2256
2257 const QFileInfo myFileInfo( filename );
2258 if ( myFileInfo.exists() || filename.endsWith( ".sld"_L1, Qt::CaseInsensitive ) )
2259 {
2260 const QFileInfo myDirInfo( myFileInfo.path() ); //excludes file name
2261 if ( !myDirInfo.isWritable() )
2262 {
2263 resultFlag = false;
2264 return tr( "The directory containing your dataset needs to be writable!" );
2265 }
2266
2267 // now construct the file name for our .sld style file
2268 const QString myFileName = myFileInfo.path() + QDir::separator() + myFileInfo.completeBaseName() + ".sld";
2269
2270 QgsSldExportContext context { exportContext };
2271 context.setExportFilePath( myFileName );
2272
2273 QDomDocument myDocument = mlayer->exportSldStyleV3( context );
2274
2275 if ( !context.errors().empty() )
2276 {
2277 resultFlag = false;
2278 return context.errors().join( '\n' );
2279 }
2280
2281 QFile myFile( myFileName );
2282 if ( myFile.open( QFile::WriteOnly | QFile::Truncate ) )
2283 {
2284 QTextStream myFileStream( &myFile );
2285 // save as utf-8 with 2 spaces for indents
2286 myDocument.save( myFileStream, 2 );
2287 myFile.close();
2288 resultFlag = true;
2289 return tr( "Created default style file as %1" ).arg( myFileName );
2290 }
2291 }
2292
2293 resultFlag = false;
2294 return tr( "ERROR: Failed to created SLD style file as %1. Check file permissions and retry." ).arg( filename );
2295}
2296
2297QString QgsMapLayer::loadSldStyle( const QString &uri, bool &resultFlag )
2298{
2300
2301 resultFlag = false;
2302
2303 QDomDocument myDocument;
2304
2305 // location of problem associated with errorMsg
2306 int line = 0, column = 0;
2307 QString myErrorMessage;
2308
2309 QFile myFile( uri );
2310 if ( myFile.open( QFile::ReadOnly ) )
2311 {
2312 // read file
2313#if QT_VERSION >= QT_VERSION_CHECK( 6, 5, 0 )
2314 QXmlStreamReader xmlReader( &myFile );
2315 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( u"sld"_s, u"http://www.opengis.net/sld"_s ) );
2316 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( u"fes"_s, u"http://www.opengis.net/fes/2.0"_s ) );
2317 xmlReader.addExtraNamespaceDeclaration( QXmlStreamNamespaceDeclaration( u"ogc"_s, u"http://www.opengis.net/ogc"_s ) );
2318 const QDomDocument::ParseResult result = myDocument.setContent( &xmlReader, QDomDocument::ParseOption::UseNamespaceProcessing );
2319 if ( result )
2320 {
2321 resultFlag = true;
2322 }
2323 else
2324 {
2325 myErrorMessage = result.errorMessage;
2326 line = result.errorLine;
2327 column = result.errorColumn;
2328 }
2329#else
2330 resultFlag = myDocument.setContent( &myFile, true, &myErrorMessage, &line, &column );
2331#endif
2332 if ( !resultFlag )
2333 myErrorMessage = tr( "%1 at line %2 column %3" ).arg( myErrorMessage ).arg( line ).arg( column );
2334 myFile.close();
2335 }
2336 else
2337 {
2338 myErrorMessage = tr( "Unable to open file %1" ).arg( uri );
2339 }
2340
2341 if ( !resultFlag )
2342 {
2343 return myErrorMessage;
2344 }
2345
2346 // check for root SLD element
2347 const QDomElement myRoot = myDocument.firstChildElement( u"StyledLayerDescriptor"_s );
2348 if ( myRoot.isNull() )
2349 {
2350 myErrorMessage = u"Error: StyledLayerDescriptor element not found in %1"_s.arg( uri );
2351 resultFlag = false;
2352 return myErrorMessage;
2353 }
2354
2355 // now get the style node out and pass it over to the layer
2356 // to deserialise...
2357 const QDomElement namedLayerElem = myRoot.firstChildElement( u"NamedLayer"_s );
2358 if ( namedLayerElem.isNull() )
2359 {
2360 myErrorMessage = u"Info: NamedLayer element not found."_s;
2361 resultFlag = false;
2362 return myErrorMessage;
2363 }
2364
2365 QString errorMsg;
2366 resultFlag = readSld( namedLayerElem, errorMsg );
2367 if ( !resultFlag )
2368 {
2369 myErrorMessage = tr( "Loading style file %1 failed because:\n%2" ).arg( uri, errorMsg );
2370 return myErrorMessage;
2371 }
2372
2373 return QString();
2374}
2375
2376bool QgsMapLayer::readStyle( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
2377{
2379
2380 Q_UNUSED( node )
2381 Q_UNUSED( errorMessage )
2382 Q_UNUSED( context )
2383 Q_UNUSED( categories )
2384 return false;
2385}
2386
2387bool QgsMapLayer::writeStyle( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories ) const
2388{
2390
2391 Q_UNUSED( node )
2392 Q_UNUSED( doc )
2393 Q_UNUSED( errorMessage )
2394 Q_UNUSED( context )
2395 Q_UNUSED( categories )
2396 return false;
2397}
2398
2399
2400void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, bool loadDefaultStyleFlag )
2401{
2403
2405
2407 if ( loadDefaultStyleFlag )
2408 {
2410 }
2411
2413 {
2415 }
2416 setDataSource( dataSource, baseName.isEmpty() ? mLayerName : baseName, provider.isEmpty() ? mProviderKey : provider, options, flags );
2417}
2418
2419void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, bool loadDefaultStyleFlag )
2420{
2422
2424 if ( loadDefaultStyleFlag )
2425 {
2427 }
2428
2430 {
2432 }
2433 setDataSource( dataSource, baseName, provider, options, flags );
2434}
2435
2436void QgsMapLayer::setDataSource( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
2437{
2439
2441 {
2443 }
2444 setDataSourcePrivate( dataSource, baseName, provider, options, flags );
2445 emit dataSourceChanged();
2446 emit dataChanged();
2448}
2449
2450
2451void QgsMapLayer::setDataSourcePrivate( const QString &dataSource, const QString &baseName, const QString &provider, const QgsDataProvider::ProviderOptions &options, Qgis::DataProviderReadFlags flags )
2452{
2454
2455 Q_UNUSED( dataSource )
2456 Q_UNUSED( baseName )
2457 Q_UNUSED( provider )
2458 Q_UNUSED( options )
2459 Q_UNUSED( flags )
2460}
2461
2462
2464{
2466
2467 return mProviderKey;
2468}
2469
2470void QgsMapLayer::readCommonStyle( const QDomElement &layerElement, const QgsReadWriteContext &context, QgsMapLayer::StyleCategories categories )
2471{
2473
2474 if ( categories.testFlag( Symbology3D ) )
2475 {
2476 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "3D Symbology" ) );
2477
2478 QgsAbstract3DRenderer *r3D = nullptr;
2479 QDomElement renderer3DElem = layerElement.firstChildElement( u"renderer-3d"_s );
2480 if ( !renderer3DElem.isNull() )
2481 {
2482 const QString type3D = renderer3DElem.attribute( u"type"_s );
2484 if ( meta3D )
2485 {
2486 r3D = meta3D->createRenderer( renderer3DElem, context );
2487 }
2488 }
2489 setRenderer3D( r3D );
2490 }
2491
2492 if ( categories.testFlag( CustomProperties ) )
2493 {
2494 // read custom properties before passing reading further to a subclass, so that
2495 // the subclass can also read custom properties
2496 readCustomProperties( layerElement );
2497 }
2498
2499 // use scale dependent visibility flag
2500 if ( categories.testFlag( Rendering ) )
2501 {
2502 setScaleBasedVisibility( layerElement.attribute( u"hasScaleBasedVisibilityFlag"_s ).toInt() == 1 );
2503 if ( layerElement.hasAttribute( u"minimumScale"_s ) )
2504 {
2505 // older element, when scales were reversed
2506 setMaximumScale( layerElement.attribute( u"minimumScale"_s ).toDouble() );
2507 setMinimumScale( layerElement.attribute( u"maximumScale"_s ).toDouble() );
2508 }
2509 else
2510 {
2511 setMaximumScale( layerElement.attribute( u"maxScale"_s ).toDouble() );
2512 setMinimumScale( layerElement.attribute( u"minScale"_s ).toDouble() );
2513 }
2514 if ( layerElement.hasAttribute( u"autoRefreshMode"_s ) )
2515 {
2516 setAutoRefreshInterval( layerElement.attribute( u"autoRefreshTime"_s ).toInt() );
2517 setAutoRefreshMode( qgsEnumKeyToValue( layerElement.attribute( u"autoRefreshMode"_s ), Qgis::AutoRefreshMode::Disabled ) );
2518 }
2519 }
2520
2521 if ( categories.testFlag( LayerConfiguration ) )
2522 {
2523 // flags
2524 const QDomElement flagsElem = layerElement.firstChildElement( u"flags"_s );
2525 LayerFlags flags = mFlags;
2526 const auto enumMap = qgsEnumMap<QgsMapLayer::LayerFlag>();
2527 for ( auto it = enumMap.constBegin(); it != enumMap.constEnd(); ++it )
2528 {
2529 const QDomNode flagNode = flagsElem.namedItem( it.value() );
2530 if ( flagNode.isNull() )
2531 continue;
2532 const bool flagValue = flagNode.toElement().text() == "1" ? true : false;
2533 if ( flags.testFlag( it.key() ) && !flagValue )
2534 flags &= ~it.key();
2535 else if ( !flags.testFlag( it.key() ) && flagValue )
2536 flags |= it.key();
2537 }
2538 setFlags( flags );
2539 }
2540
2541 if ( categories.testFlag( Temporal ) )
2542 {
2543 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Temporal" ) );
2544
2546 properties->readXml( layerElement.toElement(), context );
2547 }
2548
2549 if ( categories.testFlag( Elevation ) )
2550 {
2551 const QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Elevation" ) );
2552
2554 properties->readXml( layerElement.toElement(), context );
2555 }
2556
2557 if ( categories.testFlag( Notes ) )
2558 {
2559 const QDomElement notesElem = layerElement.firstChildElement( u"userNotes"_s );
2560 if ( !notesElem.isNull() )
2561 {
2562 const QString notes = notesElem.attribute( u"value"_s );
2563 QgsLayerNotesUtils::setLayerNotes( this, notes );
2564 }
2565 }
2566}
2567
2569{
2571
2572 return mUndoStack;
2573}
2574
2576{
2578
2579 return mUndoStackStyles;
2580}
2581
2583{
2585
2586 return mCustomProperties.keys();
2587}
2588
2589void QgsMapLayer::setCustomProperty( const QString &key, const QVariant &value )
2590{
2592
2593 if ( !mCustomProperties.contains( key ) || mCustomProperties.value( key ) != value )
2594 {
2595 mCustomProperties.setValue( key, value );
2596 emit customPropertyChanged( key );
2597 }
2598}
2599
2601{
2603
2604 mCustomProperties = properties;
2605 for ( const QString &key : mCustomProperties.keys() )
2606 {
2607 emit customPropertyChanged( key );
2608 }
2609}
2610
2612{
2614
2615 return mCustomProperties;
2616}
2617
2618QVariant QgsMapLayer::customProperty( const QString &value, const QVariant &defaultValue ) const
2619{
2620 // non fatal for now -- the "rasterize" processing algorithm is not thread safe and calls this
2622
2623 return mCustomProperties.value( value, defaultValue );
2624}
2625
2626void QgsMapLayer::removeCustomProperty( const QString &key )
2627{
2629
2630 if ( mCustomProperties.contains( key ) )
2631 {
2632 mCustomProperties.remove( key );
2633 emit customPropertyChanged( key );
2634 }
2635}
2636
2637int QgsMapLayer::listStylesInDatabase( QStringList &ids, QStringList &names, QStringList &descriptions, QString &msgError )
2638{
2640
2641 return QgsProviderRegistry::instance()->listStyles( mProviderKey, mDataSource, ids, names, descriptions, msgError );
2642}
2643
2644QString QgsMapLayer::getStyleFromDatabase( const QString &styleId, QString &msgError )
2645{
2647
2648 return QgsProviderRegistry::instance()->getStyleById( mProviderKey, mDataSource, styleId, msgError );
2649}
2650
2651bool QgsMapLayer::deleteStyleFromDatabase( const QString &styleId, QString &msgError )
2652{
2654
2656}
2657
2658void QgsMapLayer::saveStyleToDatabase( const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories )
2659{
2660 saveStyleToDatabaseV2( name, description, useAsDefault, uiFileContent, msgError, categories );
2661}
2662
2664 const QString &name, const QString &description, bool useAsDefault, const QString &uiFileContent, QString &msgError, QgsMapLayer::StyleCategories categories
2665)
2666{
2668
2670
2671 QString sldStyle, qmlStyle;
2672 QDomDocument qmlDocument;
2673 QgsReadWriteContext context;
2674 exportNamedStyle( qmlDocument, msgError, context, categories );
2675 if ( !msgError.isEmpty() )
2676 {
2678 }
2679 else
2680 {
2681 qmlStyle = qmlDocument.toString();
2682 }
2683
2684 QgsSldExportContext sldContext;
2685 QDomDocument sldDocument = this->exportSldStyleV3( sldContext );
2686 if ( !sldContext.errors().empty() )
2687 {
2689 }
2690 else
2691 {
2692 sldStyle = sldDocument.toString();
2693 }
2694
2695 if ( !QgsProviderRegistry::instance()->saveStyle( mProviderKey, mDataSource, qmlStyle, sldStyle, name, description, uiFileContent, useAsDefault, msgError ) )
2696 {
2698 }
2699 return results;
2700}
2701
2702QString QgsMapLayer::loadNamedStyle( const QString &theURI, bool &resultFlag, bool loadFromLocalDB, QgsMapLayer::StyleCategories categories, Qgis::LoadStyleFlags flags )
2703{
2705
2706 QString returnMessage;
2707 QString qml, errorMsg;
2708 QString styleName;
2709 if ( !loadFromLocalDB && dataProvider() && dataProvider()->styleStorageCapabilities().testFlag( Qgis::ProviderStyleStorageCapability::LoadFromDatabase ) )
2710 {
2712 }
2713
2714 // Style was successfully loaded from provider storage
2715 if ( !qml.isEmpty() )
2716 {
2717 QDomDocument myDocument( u"qgis"_s );
2718 myDocument.setContent( qml );
2719 resultFlag = importNamedStyle( myDocument, errorMsg );
2720 returnMessage = QObject::tr( "Loaded from Provider" );
2721 }
2722 else
2723 {
2725
2726 bool styleExists = false;
2727 bool styleSuccessfullyLoaded = false;
2728
2729 returnMessage = loadNamedProperty( theURI, PropertyType::Style, styleExists, styleSuccessfullyLoaded, categories, flags );
2730
2731 // TODO QGIS 5.0 -- fix API for loadNamedStyle so we can return styleExists too
2732 ( void ) styleExists;
2733 resultFlag = styleSuccessfullyLoaded;
2734 }
2735
2736 if ( !styleName.isEmpty() )
2737 {
2738 styleManager()->renameStyle( styleManager()->currentStyle(), styleName );
2739 }
2740
2741 if ( resultFlag )
2742 emit styleLoaded( categories );
2743
2744 return returnMessage;
2745}
2746
2753
2755{
2757
2758 return false;
2759}
2760
2762{
2764
2765 return false;
2766}
2767
2769{
2771
2772 return true;
2773}
2774
2776{
2778
2779 // invalid layers are temporary? -- who knows?!
2780 if ( !isValid() )
2781 return false;
2782
2783 if ( mProviderKey == "memory"_L1 )
2784 return true;
2785
2786 const QVariantMap sourceParts = QgsProviderRegistry::instance()->decodeUri( mProviderKey, mDataSource );
2787 const QString path = sourceParts.value( u"path"_s ).toString();
2788 if ( path.isEmpty() )
2789 return false;
2790
2791 // check if layer path is inside one of the standard temporary file locations for this platform
2792 const QStringList tempPaths = QStandardPaths::standardLocations( QStandardPaths::TempLocation );
2793 for ( const QString &tempPath : tempPaths )
2794 {
2795 if ( path.startsWith( tempPath ) )
2796 return true;
2797 }
2798
2799 return false;
2800}
2801
2802void QgsMapLayer::setValid( bool valid )
2803{
2805
2806 if ( mValid == valid )
2807 return;
2808
2809 mValid = valid;
2810 emit isValidChanged();
2811}
2812
2814{
2816
2817 if ( legend == mLegend.get() )
2818 return;
2819
2820 mLegend.reset( legend );
2821
2822
2823 if ( mLegend )
2824 {
2825 mLegend->setParent( this );
2826 connect( mLegend.get(), &QgsMapLayerLegend::itemsChanged, this, &QgsMapLayer::legendChanged, Qt::UniqueConnection );
2827 }
2828
2829 emit legendChanged();
2830}
2831
2833{
2835
2836 return mLegend.get();
2837}
2838
2840{
2842
2843 return mStyleManager.get();
2844}
2845
2847{
2849
2850 if ( renderer == m3DRenderer.get() )
2851 return;
2852
2853 m3DRenderer.reset( renderer );
2854
2855 emit renderer3DChanged();
2857}
2858
2860{
2862
2863 return m3DRenderer.get();
2864}
2865
2866void QgsMapLayer::triggerRepaint( bool deferredUpdate )
2867{
2869
2870 if ( mRepaintRequestedFired )
2871 return;
2872 mRepaintRequestedFired = true;
2873 emit repaintRequested( deferredUpdate );
2874 mRepaintRequestedFired = false;
2875}
2876
2883
2885{
2887
2888 mMetadata = metadata;
2889 // mMetadata.saveToLayer( this );
2890 emit metadataChanged();
2891}
2892
2894{
2896
2897 return QString();
2898}
2899
2900QDateTime QgsMapLayer::timestamp() const
2901{
2903
2904 return QDateTime();
2905}
2906
2914
2916{
2917 updateExtent( extent );
2918}
2919
2921{
2923
2924 updateExtent( extent );
2925}
2926
2927bool QgsMapLayer::isReadOnly() const
2928{
2930
2931 return true;
2932}
2933
2935{
2937
2938 return mOriginalXmlProperties;
2939}
2940
2942{
2944
2945 mOriginalXmlProperties = originalXmlProperties;
2946}
2947
2948QString QgsMapLayer::generateId( const QString &layerName )
2949{
2950 return QgsStringUtils::createUniqueId( layerName );
2951}
2952
2954{
2956
2957 return true;
2958}
2959
2966
2968{
2970
2971 return mapTipsEnabled() && !mMapTipTemplate.isEmpty();
2972}
2973
2980
2981QSet<QgsMapLayerDependency> QgsMapLayer::dependencies() const
2982{
2984
2985 return mDependencies;
2986}
2987
2988bool QgsMapLayer::setDependencies( const QSet<QgsMapLayerDependency> &oDeps )
2989{
2991
2992 QSet<QgsMapLayerDependency> deps;
2993 const auto constODeps = oDeps;
2994 for ( const QgsMapLayerDependency &dep : constODeps )
2995 {
2996 if ( dep.origin() == QgsMapLayerDependency::FromUser )
2997 deps << dep;
2998 }
2999
3000 mDependencies = deps;
3001 emit dependenciesChanged();
3002 return true;
3003}
3004
3006{
3008
3009 QgsDataProvider *lDataProvider = dataProvider();
3010
3011 if ( !lDataProvider )
3012 return;
3013
3014 if ( enabled && !isRefreshOnNotifyEnabled() )
3015 {
3016 lDataProvider->setListening( enabled );
3017 connect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
3018 }
3019 else if ( !enabled && isRefreshOnNotifyEnabled() )
3020 {
3021 // we don't want to disable provider listening because someone else could need it (e.g. actions)
3022 disconnect( lDataProvider, &QgsDataProvider::notify, this, &QgsMapLayer::onNotified );
3023 }
3024 mIsRefreshOnNofifyEnabled = enabled;
3025}
3026
3028{
3029 // aggregate based tests aren't thread safe
3031
3032 if ( QgsMapLayerStore *store = qobject_cast<QgsMapLayerStore *>( parent() ) )
3033 {
3034 return qobject_cast<QgsProject *>( store->parent() );
3035 }
3036 return nullptr;
3037}
3038
3039void QgsMapLayer::onNotified( const QString &message )
3040{
3042
3043 if ( refreshOnNotifyMessage().isEmpty() || refreshOnNotifyMessage() == message )
3044 {
3046 emit dataChanged();
3047 }
3048}
3049
3050QgsRectangle QgsMapLayer::wgs84Extent( bool forceRecalculate ) const
3051{
3053
3054 if ( !crs().isEarthCrs() )
3055 {
3056 return QgsRectangle();
3057 }
3058
3059 // if this function is called without previous call to extent() it will return empty rectangle as both mExtent2D and mExtent3D are null
3060 // to avoid this call extent here to force extent calculation
3061 ( void ) extent();
3062
3064
3065 if ( !forceRecalculate && !mWgs84Extent.isNull() )
3066 {
3067 wgs84Extent = mWgs84Extent;
3068 }
3069 else if ( !mExtent2D.isNull() || !mExtent3D.isNull() )
3070 {
3071 QgsCoordinateTransform transformer { crs(), QgsCoordinateReferenceSystem( u"EPSG:4326"_s ), transformContext() };
3072 transformer.setBallparkTransformsAreAppropriate( true );
3073 try
3074 {
3075 if ( mExtent2D.isNull() )
3076 wgs84Extent = transformer.transformBoundingBox( mExtent3D.toRectangle() );
3077 else
3078 wgs84Extent = transformer.transformBoundingBox( mExtent2D );
3079 }
3080 catch ( const QgsCsException &cse )
3081 {
3082 QgsMessageLog::logMessage( tr( "Error transforming extent: %1" ).arg( cse.what() ) );
3084 }
3085 }
3086 return wgs84Extent;
3087}
3088
3089void QgsMapLayer::updateExtent( const QgsRectangle &extent ) const
3090{
3092
3093 if ( extent == mExtent2D )
3094 return;
3095
3096 mExtent2D = extent;
3097
3098 // do not update the wgs84 extent if we trust layer metadata
3100 return;
3101
3102 mWgs84Extent = wgs84Extent( true );
3103}
3104
3105void QgsMapLayer::updateExtent( const QgsBox3D &extent ) const
3106{
3108
3109 if ( extent == mExtent3D )
3110 return;
3111
3112 if ( extent.isNull() )
3113 {
3114 if ( !extent.toRectangle().isNull() )
3115 {
3116 // bad 3D extent param but valid in 2d --> update 2D extent
3117 updateExtent( extent.toRectangle() );
3118 }
3119 else
3120 {
3121 QgsDebugMsgLevel( u"Unable to update extent with empty parameter"_s, 1 );
3122 }
3123 }
3124 else
3125 {
3126 mExtent3D = extent;
3127
3128 // do not update the wgs84 extent if we trust layer metadata
3130 return;
3131
3132 mWgs84Extent = wgs84Extent( true );
3133 }
3134}
3135
3136bool QgsMapLayer::rebuildCrs3D( QString *error )
3137{
3138 bool res = true;
3139 if ( !mCRS.isValid() )
3140 {
3141 mCrs3D = QgsCoordinateReferenceSystem();
3142 }
3143 else if ( !mVerticalCrs.isValid() )
3144 {
3145 mCrs3D = mCRS;
3146 }
3147 else
3148 {
3149 switch ( mCRS.type() )
3150 {
3154 mCrs3D = mCRS;
3155 break;
3156
3158 {
3159 QString tempError;
3160 mCrs3D = mCRS.hasVerticalAxis() ? mCRS : QgsCoordinateReferenceSystem::createCompoundCrs( mCRS, mVerticalCrs, error ? *error : tempError );
3161 res = mCrs3D.isValid();
3162 break;
3163 }
3164
3166 // nonsense situation
3167 mCrs3D = QgsCoordinateReferenceSystem();
3168 res = false;
3169 break;
3170
3179 {
3180 QString tempError;
3181 mCrs3D = QgsCoordinateReferenceSystem::createCompoundCrs( mCRS, mVerticalCrs, error ? *error : tempError );
3182 res = mCrs3D.isValid();
3183 break;
3184 }
3185 }
3186 }
3187 return res;
3188}
3189
3191{
3193
3194 // do not update the wgs84 extent if we trust layer metadata
3196 return;
3197
3198 mWgs84Extent = QgsRectangle();
3199}
3200
3202{
3204
3205 QString metadata = u"<h1>"_s + tr( "General" ) + u"</h1>\n<hr>\n"_s + u"<table class=\"list-view\">\n"_s;
3206
3207 // name
3208 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Name" ) + u"</td><td>"_s + name() + u"</td></tr>\n"_s;
3209
3210 const QString lPublicSource = publicSource();
3211
3212 QString path;
3213 bool isLocalPath = false;
3214 if ( dataProvider() )
3215 {
3216 // local path
3217 QVariantMap uriComponents = QgsProviderRegistry::instance()->decodeUri( dataProvider()->name(), lPublicSource );
3218 if ( uriComponents.contains( u"path"_s ) )
3219 {
3220 path = uriComponents[u"path"_s].toString();
3221 QFileInfo fi( path );
3222 if ( fi.exists() )
3223 {
3224 isLocalPath = true;
3225 metadata += u"<tr><td class=\"highlight\">"_s
3226 + tr( "Path" )
3227 + u"</td><td>%1"_s.arg( u"<a href=\"%1\">%2</a>"_s.arg( QUrl::fromLocalFile( path ).toString(), QDir::toNativeSeparators( path ) ) )
3228 + u"</td></tr>\n"_s;
3229
3230 QDateTime lastModified = fi.lastModified();
3231 QString lastModifiedFileName;
3232 QSet<QString> sidecarFiles = QgsFileUtils::sidecarFilesForPath( path );
3233 if ( fi.isFile() )
3234 {
3235 qint64 fileSize = fi.size();
3236 if ( !sidecarFiles.isEmpty() )
3237 {
3238 lastModifiedFileName = fi.fileName();
3239 QStringList sidecarFileNames;
3240 for ( const QString &sidecarFile : sidecarFiles )
3241 {
3242 QFileInfo sidecarFi( sidecarFile );
3243 fileSize += sidecarFi.size();
3244 if ( sidecarFi.lastModified() > lastModified )
3245 {
3246 lastModified = sidecarFi.lastModified();
3247 lastModifiedFileName = sidecarFi.fileName();
3248 }
3249 sidecarFileNames << sidecarFi.fileName();
3250 }
3251 metadata += u"<tr><td class=\"highlight\">"_s
3252 + ( sidecarFiles.size() > 1 ? tr( "Sidecar files" ) : tr( "Sidecar file" ) )
3253 + u"</td><td>%1"_s.arg( sidecarFileNames.join( ", "_L1 ) )
3254 + u"</td></tr>\n"_s;
3255 }
3256 metadata += u"<tr><td class=\"highlight\">"_s
3257 + ( !sidecarFiles.isEmpty() ? tr( "Total size" ) : tr( "Size" ) )
3258 + u"</td><td>%1"_s.arg( QgsFileUtils::representFileSize( fileSize ) )
3259 + u"</td></tr>\n"_s;
3260 }
3261 metadata += u"<tr><td class=\"highlight\">"_s
3262 + tr( "Last modified" )
3263 + u"</td><td>%1"_s.arg( QLocale().toString( fi.lastModified() ) )
3264 + ( !lastModifiedFileName.isEmpty() ? u" (%1)"_s.arg( lastModifiedFileName ) : QString() )
3265 + u"</td></tr>\n"_s;
3266 }
3267 }
3268 if ( uriComponents.contains( u"url"_s ) )
3269 {
3270 QUrl decodedUri = QUrl::fromPercentEncoding( uriComponents[u"url"_s].toString().toLocal8Bit() );
3271 const QString url = decodedUri.toString();
3272 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;
3273 }
3274 }
3275
3276 // data source
3277 if ( lPublicSource != path || !isLocalPath )
3278 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Source" ) + u"</td><td>%1"_s.arg( lPublicSource != path ? lPublicSource : path ) + u"</td></tr>\n"_s;
3279
3280 // provider
3281 if ( dataProvider() )
3282 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Provider" ) + u"</td><td>%1"_s.arg( dataProvider()->name() ) + u"</td></tr>\n"_s;
3283
3284 // Layer ID
3285 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Layer ID" ) + u"</td><td>%1"_s.arg( id() ) + u"</td></tr>\n"_s;
3286
3287 metadata += "</table>\n<br><br>"_L1;
3288
3289 return metadata;
3290}
3291
3293{
3294 QString metadata;
3295 // custom properties
3296 if ( const auto keys = customPropertyKeys(); !keys.isEmpty() )
3297 {
3298 metadata += u"<h1>"_s + tr( "Custom properties" ) + u"</h1>\n<hr>\n"_s;
3299 metadata += "<table class=\"list-view\">\n<tbody>"_L1;
3300 for ( const QString &key : keys )
3301 {
3302 // keys prefaced with _ are considered private/internal details
3303 if ( key.startsWith( '_' ) )
3304 continue;
3305
3306 const QVariant propValue = customProperty( key );
3307 QString stringValue;
3308 if ( propValue.type() == QVariant::List || propValue.type() == QVariant::StringList )
3309 {
3310 for ( const QString &s : propValue.toStringList() )
3311 {
3312 stringValue += "<p style=\"margin: 0;\">" + s.toHtmlEscaped() + "</p>";
3313 }
3314 }
3315 else
3316 {
3317 stringValue = propValue.toString().toHtmlEscaped();
3318
3319 //if the result string is empty but propValue is not, the conversion has failed
3320 if ( stringValue.isEmpty() && !QgsVariantUtils::isNull( propValue ) )
3321 stringValue = tr( "<i>value cannot be displayed</i>" );
3322 }
3323
3324 metadata += u"<tr><td class=\"highlight\">%1</td><td>%2</td></tr>"_s.arg( key.toHtmlEscaped(), stringValue );
3325 }
3326 metadata += "</tbody></table>\n"_L1;
3327 metadata += "<br><br>\n"_L1;
3328 }
3329 return metadata;
3330}
3331
3333{
3335 QString metadata;
3336
3337 auto addCrsInfo = [&metadata]( const QgsCoordinateReferenceSystem &c, bool includeType, bool includeOperation, bool includeCelestialBody ) {
3338 if ( !c.isValid() )
3339 metadata += u"<tr><td colspan=\"2\" class=\"highlight\">"_s + tr( "Unknown" ) + u"</td></tr>\n"_s;
3340 else
3341 {
3342 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Name" ) + u"</td><td>"_s + c.userFriendlyIdentifier( Qgis::CrsIdentifierType::FullString ) + u"</td></tr>\n"_s;
3343
3344 // map units
3345 metadata += u"<tr><td class=\"highlight\">"_s
3346 + tr( "Units" )
3347 + u"</td><td>"_s
3348 + ( c.isGeographic() ? tr( "Geographic (uses latitude and longitude for coordinates)" ) : QgsUnitTypes::toString( c.mapUnits() ) )
3349 + u"</td></tr>\n"_s;
3350
3351 if ( includeType )
3352 {
3353 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Type" ) + u"</td><td>"_s + QgsCoordinateReferenceSystemUtils::crsTypeToString( c.type() ) + u"</td></tr>\n"_s;
3354 }
3355
3356 if ( includeOperation )
3357 {
3358 // operation
3359 const QgsProjOperation operation = c.operation();
3360 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Method" ) + u"</td><td>"_s + operation.description() + u"</td></tr>\n"_s;
3361 }
3362
3363 if ( includeCelestialBody )
3364 {
3365 // celestial body
3366 try
3367 {
3368 const QString celestialBody = c.celestialBodyName();
3369 if ( !celestialBody.isEmpty() )
3370 {
3371 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Celestial Body" ) + u"</td><td>"_s + celestialBody + u"</td></tr>\n"_s;
3372 }
3373 }
3374 catch ( QgsNotSupportedException & )
3375 {}
3376 }
3377
3378 QString accuracyString;
3379 // dynamic crs with no epoch?
3380 if ( c.isDynamic() && std::isnan( c.coordinateEpoch() ) )
3381 {
3382 accuracyString = tr( "Based on a dynamic CRS, but no coordinate epoch is set. Coordinates are ambiguous and of limited accuracy." );
3383 }
3384
3385 // based on datum ensemble?
3386 try
3387 {
3388 const QgsDatumEnsemble ensemble = c.datumEnsemble();
3389 if ( ensemble.isValid() )
3390 {
3391 QString id;
3392 if ( !ensemble.code().isEmpty() )
3393 id = u"<i>%1</i> (%2:%3)"_s.arg( ensemble.name(), ensemble.authority(), ensemble.code() );
3394 else
3395 id = u"<i>%1</i>”"_s.arg( ensemble.name() );
3396
3397 if ( ensemble.accuracy() > 0 )
3398 {
3399 accuracyString = tr( "Based on %1, which has a limited accuracy of <b>at best %2 meters</b>." ).arg( id ).arg( ensemble.accuracy() );
3400 }
3401 else
3402 {
3403 accuracyString = tr( "Based on %1, which has a limited accuracy." ).arg( id );
3404 }
3405 }
3406 }
3407 catch ( QgsNotSupportedException & )
3408 {}
3409
3410 if ( !accuracyString.isEmpty() )
3411 {
3412 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Accuracy" ) + u"</td><td>"_s + accuracyString + u"</td></tr>\n"_s;
3413 }
3414
3415 // static/dynamic
3416 metadata += u"<tr><td class=\"highlight\">"_s
3417 + tr( "Reference" )
3418 + 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)" ) );
3419
3420 // coordinate epoch
3421 if ( !std::isnan( c.coordinateEpoch() ) )
3422 {
3423 metadata += u"<tr><td class=\"highlight\">"_s + tr( "Coordinate Epoch" ) + u"</td><td>%1</td></tr>\n"_s.arg( qgsDoubleToString( c.coordinateEpoch(), 3 ) );
3424 }
3425 }
3426 };
3427
3428 metadata += u"<h1>"_s + tr( "Coordinate Reference System (CRS)" ) + u"</h1>\n<hr>\n"_s;
3429 metadata += "<table class=\"list-view\">\n"_L1;
3430 addCrsInfo( crs().horizontalCrs(), true, true, true );
3431 metadata += "</table>\n<br><br>\n"_L1;
3432
3433 if ( verticalCrs().isValid() )
3434 {
3435 metadata += u"<h1>"_s + tr( "Vertical Coordinate Reference System (CRS)" ) + u"</h1>\n<hr>\n"_s;
3436 metadata += "<table class=\"list-view\">\n"_L1;
3437 addCrsInfo( verticalCrs(), false, false, false );
3438 metadata += "</table>\n<br><br>\n"_L1;
3439 }
3440
3441 return metadata;
3442}
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.
QString fileName
Definition qgsproject.h:118
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:7850
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition qgis.h:7464
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7831
const QMap< T, QString > qgsEnumMap()
Returns a map of all enum entries.
Definition qgis.h:7814
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7557
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.