QGIS API Documentation 3.32.0-Lima (311a8cb8a6)
qgslayerdefinition.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayerdefinition.cpp
3 ---------------------
4 begin : January 2015
5 copyright : (C) 2015 by Nathan Woodrow
6 email : woodrow dot nathan at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15#include <QFileInfo>
16#include <QFile>
17#include <QDir>
18#include <QTextStream>
19
20#include "qgslayerdefinition.h"
21#include "qgslogger.h"
22#include "qgsmaplayer.h"
23#include "qgspathresolver.h"
24#include "qgspluginlayer.h"
26#include "qgsproject.h"
27#include "qgsrasterlayer.h"
28#include "qgsreadwritecontext.h"
29#include "qgsvectorlayer.h"
30#include "qgsvectortilelayer.h"
31#include "qgsapplication.h"
32#include "qgsmaplayerfactory.h"
33#include "qgsmeshlayer.h"
34#include "qgspointcloudlayer.h"
35#include "qgsfileutils.h"
36#include "qgsgrouplayer.h"
37#include "qgslayertreegroup.h"
38#include "qgslayertreelayer.h"
39
40bool QgsLayerDefinition::loadLayerDefinition( const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage )
41{
42 QFile file( path );
43 if ( !file.open( QIODevice::ReadOnly ) )
44 {
45 errorMessage = QStringLiteral( "Can not open file" );
46 return false;
47 }
48
49 QDomDocument doc;
50 QString message;
51 if ( !doc.setContent( &file, &message ) )
52 {
53 errorMessage = message;
54 return false;
55 }
56
57 const QFileInfo fileinfo( file );
58 QDir::setCurrent( fileinfo.absoluteDir().path() );
59
60 QgsReadWriteContext context;
61 context.setPathResolver( QgsPathResolver( path ) );
62 context.setProjectTranslator( project );
63
64 return loadLayerDefinition( doc, project, rootGroup, errorMessage, context );
65}
66
67bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage, QgsReadWriteContext &context )
68{
69 errorMessage.clear();
70
72
73 // reorder maplayer nodes based on dependencies
74 // dependencies have to be resolved before IDs get changed
75 const DependencySorter depSorter( doc );
76 if ( !depSorter.hasMissingDependency() )
77 {
78 const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
79 QVector<QDomNode> clonedSorted;
80 const auto constSortedLayerNodes = sortedLayerNodes;
81 for ( const QDomNode &node : constSortedLayerNodes )
82 {
83 clonedSorted << node.cloneNode();
84 }
85 QDomNode layersNode = doc.elementsByTagName( QStringLiteral( "maplayers" ) ).at( 0 );
86 // replace old children with new ones
87 QDomNode childNode = layersNode.firstChild();
88 for ( int i = 0; ! childNode.isNull(); i++ )
89 {
90 layersNode.replaceChild( clonedSorted.at( i ), childNode );
91 childNode = childNode.nextSibling();
92 }
93 }
94 // if a dependency is missing, we still try to load layers, since dependencies may already be loaded
95
96 // IDs of layers should be changed otherwise we may have more then one layer with the same id
97 // We have to replace the IDs before we load them because it's too late once they are loaded
98 const QDomNodeList treeLayerNodes = doc.elementsByTagName( QStringLiteral( "layer-tree-layer" ) );
99 for ( int i = 0; i < treeLayerNodes.length(); ++i )
100 {
101 const QDomNode treeLayerNode = treeLayerNodes.item( i );
102 QDomElement treeLayerElem = treeLayerNode.toElement();
103 const QString oldid = treeLayerElem.attribute( QStringLiteral( "id" ) );
104 const QString layername = treeLayerElem.attribute( QStringLiteral( "name" ) );
105 const QString newid = QgsMapLayer::generateId( layername );
106 treeLayerElem.setAttribute( QStringLiteral( "id" ), newid );
107
108 // Replace IDs for map layers
109 const QDomNodeList ids = doc.elementsByTagName( QStringLiteral( "id" ) );
110 QDomNode idnode = ids.at( 0 );
111 for ( int j = 0; ! idnode.isNull() ; ++j )
112 {
113 idnode = ids.at( j );
114 const QDomElement idElem = idnode.toElement();
115 if ( idElem.text() == oldid )
116 {
117 idElem.firstChild().setNodeValue( newid );
118 }
119 }
120
121 // change layer IDs for vector joins
122 const QDomNodeList vectorJoinNodes = doc.elementsByTagName( QStringLiteral( "join" ) ); // TODO: Find a better way of searching for vectorjoins, there might be other <join> elements within the project.
123 for ( int j = 0; j < vectorJoinNodes.size(); ++j )
124 {
125 const QDomNode joinNode = vectorJoinNodes.at( j );
126 const QDomElement joinElement = joinNode.toElement();
127 if ( joinElement.attribute( QStringLiteral( "joinLayerId" ) ) == oldid )
128 {
129 joinNode.toElement().setAttribute( QStringLiteral( "joinLayerId" ), newid );
130 }
131 }
132
133 // change IDs of dependencies
134 const QDomNodeList dataDeps = doc.elementsByTagName( QStringLiteral( "dataDependencies" ) );
135 for ( int i = 0; i < dataDeps.size(); i++ )
136 {
137 const QDomNodeList layers = dataDeps.at( i ).childNodes();
138 for ( int j = 0; j < layers.size(); j++ )
139 {
140 QDomElement elt = layers.at( j ).toElement();
141 if ( elt.attribute( QStringLiteral( "id" ) ) == oldid )
142 {
143 elt.setAttribute( QStringLiteral( "id" ), newid );
144 }
145 }
146 }
147
148 // Change IDs of widget config values
149 const QDomNodeList widgetConfig = doc.elementsByTagName( QStringLiteral( "editWidget" ) );
150 for ( int i = 0; i < widgetConfig.size(); i++ )
151 {
152 const QDomNodeList config = widgetConfig.at( i ).childNodes();
153 for ( int j = 0; j < config.size(); j++ )
154 {
155 const QDomNodeList optMap = config.at( j ).childNodes();
156 for ( int z = 0; z < optMap.size(); z++ )
157 {
158 const QDomNodeList opts = optMap.at( z ).childNodes();
159 for ( int k = 0; k < opts.size(); k++ )
160 {
161 QDomElement opt = opts.at( k ).toElement();
162 if ( opt.attribute( QStringLiteral( "value" ) ) == oldid )
163 {
164 opt.setAttribute( QStringLiteral( "value" ), newid );
165 }
166 }
167 }
168 }
169 }
170 }
171
172 QDomElement layerTreeElem = doc.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
173 bool loadInLegend = true;
174 if ( !layerTreeElem.isNull() )
175 {
176 root->readChildrenFromXml( layerTreeElem, context );
177 loadInLegend = false;
178 }
179
180 const QList<QgsMapLayer *> layers = QgsLayerDefinition::loadLayerDefinitionLayersInternal( doc, context, errorMessage );
181
182 project->addMapLayers( layers, loadInLegend );
183
184 // Now that all layers are loaded, refresh the vectorjoins to get the joined fields
185 const auto constLayers = layers;
186 for ( QgsMapLayer *layer : constLayers )
187 {
188 layer->resolveReferences( project );
189 }
190
191 root->resolveReferences( project );
192
193 const QList<QgsLayerTreeNode *> nodes = root->children();
194 root->abandonChildren();
195 delete root;
196
197 rootGroup->insertChildNodes( -1, nodes );
198
199 return true;
200}
201
202bool QgsLayerDefinition::exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage )
203{
204 return exportLayerDefinition( path, selectedTreeNodes, QgsProject::instance()->filePathStorage(), errorMessage );
205}
206
207bool QgsLayerDefinition::exportLayerDefinition( const QString &p, const QList<QgsLayerTreeNode *> &selectedTreeNodes, Qgis::FilePathType pathType, QString &errorMessage )
208{
209 const QString path = QgsFileUtils::ensureFileNameHasExtension( p, { QStringLiteral( "qlr" )} );
210
211 QFile file( path );
212 if ( !file.open( QFile::WriteOnly | QFile::Truncate ) )
213 {
214 errorMessage = file.errorString();
215 return false;
216 }
217
218 QgsReadWriteContext context;
219 switch ( pathType )
220 {
222 context.setPathResolver( QgsPathResolver( QString() ) );
223 break;
225 context.setPathResolver( QgsPathResolver( path ) );
226 break;
227 }
228
229 const QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
230 if ( !exportLayerDefinition( doc, selectedTreeNodes, errorMessage, context ) )
231 return false;
232
233 QTextStream qlayerstream( &file );
234 doc.save( qlayerstream, 2 );
235 return true;
236}
237
238bool QgsLayerDefinition::exportLayerDefinition( QDomDocument doc, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage, const QgsReadWriteContext &context )
239{
240 Q_UNUSED( errorMessage )
241 QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
242 doc.appendChild( qgiselm );
243 const QList<QgsLayerTreeNode *> nodes = selectedTreeNodes;
245 const auto constNodes = nodes;
246 for ( QgsLayerTreeNode *node : constNodes )
247 {
248 QgsLayerTreeNode *newnode = node->clone();
249 root->addChildNode( newnode );
250 }
251 root->writeXml( qgiselm, context );
252
253 QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
254 const QList<QgsLayerTreeLayer *> layers = root->findLayers();
255 const auto constLayers = layers;
256 for ( QgsLayerTreeLayer *layer : constLayers )
257 {
258 if ( ! layer->layer() )
259 {
260 QgsDebugMsgLevel( QStringLiteral( "Not a valid map layer: skipping %1" ).arg( layer->name( ) ), 4 );
261 continue;
262 }
263 QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
264 layer->layer()->writeLayerXml( layerelm, doc, context );
265 layerselm.appendChild( layerelm );
266 }
267 qgiselm.appendChild( layerselm );
268 return true;
269}
270
271QDomDocument QgsLayerDefinition::exportLayerDefinitionLayers( const QList<QgsMapLayer *> &layers, const QgsReadWriteContext &context )
272{
273 QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
274 QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
275 doc.appendChild( qgiselm );
276 QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
277 const auto constLayers = layers;
278 for ( QgsMapLayer *layer : constLayers )
279 {
280 QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
281 layer->writeLayerXml( layerelm, doc, context );
282 layerselm.appendChild( layerelm );
283 }
284 qgiselm.appendChild( layerselm );
285 return doc;
286}
287
288QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( QDomDocument &document, QgsReadWriteContext &context )
289{
290 QString errorMessage;
291 return loadLayerDefinitionLayersInternal( document, context, errorMessage );
292}
293
294QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayersInternal( QDomDocument &document, QgsReadWriteContext &context, QString &errorMessage )
295{
296 QList<QgsMapLayer *> layers;
297 QDomElement layerElem = document.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
298 // For QLR:
299 if ( layerElem.isNull() )
300 {
301 layerElem = document.documentElement().firstChildElement( QStringLiteral( "maplayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
302 }
303
304 while ( ! layerElem.isNull() )
305 {
306 const QString type = layerElem.attribute( QStringLiteral( "type" ) );
307 QgsMapLayer *layer = nullptr;
308
309 bool ok = false;
310 const Qgis::LayerType layerType = QgsMapLayerFactory::typeFromString( type, ok );
311 if ( ok )
312 {
313 switch ( layerType )
314 {
315 case Qgis::LayerType::Vector:
316 layer = new QgsVectorLayer();
317 break;
318
319 case Qgis::LayerType::Raster:
320 layer = new QgsRasterLayer();
321 break;
322
323 case Qgis::LayerType::Plugin:
324 {
325 const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
327 break;
328 }
329
330 case Qgis::LayerType::Mesh:
331 layer = new QgsMeshLayer();
332 break;
333
334 case Qgis::LayerType::VectorTile:
335 layer = new QgsVectorTileLayer;
336 break;
337
338 case Qgis::LayerType::PointCloud:
339 layer = new QgsPointCloudLayer();
340 break;
341
342 case Qgis::LayerType::Group:
344 break;
345
346 case Qgis::LayerType::Annotation:
347 break;
348 }
349 }
350
351 if ( layer )
352 {
353 // always add the layer, even if the source is invalid -- this allows users to fix the source
354 // at a later stage and still retain all the layer properties intact
355 layer->readLayerXml( layerElem, context );
356 layers << layer;
357 }
358 else
359 {
360 errorMessage = QObject::tr( "Unsupported layer type: %1" ).arg( type );
361 }
362 layerElem = layerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
363 }
364 return layers;
365}
366
367QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( const QString &qlrfile )
368{
369 QFile file( qlrfile );
370 if ( !file.open( QIODevice::ReadOnly ) )
371 {
372 QgsDebugError( QStringLiteral( "Can't open file" ) );
373 return QList<QgsMapLayer *>();
374 }
375
376 QDomDocument doc;
377 if ( !doc.setContent( &file ) )
378 {
379 QgsDebugError( QStringLiteral( "Can't set content" ) );
380 return QList<QgsMapLayer *>();
381 }
382
383 QgsReadWriteContext context;
384 context.setPathResolver( QgsPathResolver( qlrfile ) );
385 //no project translator defined here
387}
388
389void QgsLayerDefinition::DependencySorter::init( const QDomDocument &doc )
390{
391 // Determine a loading order of layers based on a graph of dependencies
392 QMap< QString, QVector< QString > > dependencies;
393 QStringList sortedLayers;
394 QList< QPair<QString, QDomNode> > layersToSort;
395 QStringList layerIds;
396
397 QDomElement layerElem = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
398 // For QLR:
399 if ( layerElem.isNull() )
400 {
401 layerElem = doc.documentElement().firstChildElement( QStringLiteral( "maplayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
402 }
403 // For tests (I don't know if there is a real use case for such a document except for test_qgslayerdefinition.py)
404 if ( layerElem.isNull() )
405 {
406 layerElem = doc.documentElement().firstChildElement( QStringLiteral( "maplayer" ) );
407 }
408
409 const QDomElement &firstElement { layerElem };
410
411 QVector<QString> deps; //avoid expensive allocation for list for every iteration
412 while ( !layerElem.isNull() )
413 {
414 deps.resize( 0 ); // preserve capacity - don't use clear
415
416 const QString id = layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text();
417 layerIds << id;
418
419 // dependencies for this layer
420 const QDomElement layerDependenciesElem = layerElem.firstChildElement( QStringLiteral( "layerDependencies" ) );
421 if ( !layerDependenciesElem.isNull() )
422 {
423 const QDomNodeList dependencyList = layerDependenciesElem.elementsByTagName( QStringLiteral( "layer" ) );
424 for ( int j = 0; j < dependencyList.size(); ++j )
425 {
426 const QDomElement depElem = dependencyList.at( j ).toElement();
427 deps << depElem.attribute( QStringLiteral( "id" ) );
428 }
429 }
430 dependencies[id] = deps;
431
432 if ( deps.empty() )
433 {
434 sortedLayers << id;
435 mSortedLayerNodes << layerElem;
436 mSortedLayerIds << id;
437 }
438 else
439 {
440 layersToSort << qMakePair( id, layerElem );
441 mDependentLayerIds.insert( id );
442 }
443 layerElem = layerElem.nextSiblingElement( );
444 }
445
446 // check that all dependencies are present
447 const auto constDependencies = dependencies;
448 for ( const QVector< QString > &ids : constDependencies )
449 {
450 const auto constIds = ids;
451 for ( const QString &depId : constIds )
452 {
453 if ( !dependencies.contains( depId ) )
454 {
455 // some dependencies are not satisfied
456 mHasMissingDependency = true;
457 layerElem = firstElement;
458 while ( ! layerElem.isNull() )
459 {
460 mSortedLayerNodes << layerElem;
461 layerElem = layerElem.nextSiblingElement( );
462 }
463 mSortedLayerIds = layerIds;
464 return;
465 }
466 }
467 }
468
469 // cycles should be very rare, since layers with cyclic dependencies may only be created by
470 // manually modifying the project file
471 mHasCycle = false;
472
473 while ( !layersToSort.empty() && !mHasCycle )
474 {
475 QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin();
476 while ( it != layersToSort.end() )
477 {
478 const QString idToSort = it->first;
479 const QDomNode node = it->second;
480 mHasCycle = true;
481 bool resolved = true;
482 const auto deps { dependencies.value( idToSort ) };
483 for ( const QString &dep : deps )
484 {
485 if ( !sortedLayers.contains( dep ) )
486 {
487 resolved = false;
488 break;
489 }
490 }
491 if ( resolved ) // dependencies for this layer are resolved
492 {
493 sortedLayers << idToSort;
494 mSortedLayerNodes << node;
495 mSortedLayerIds << idToSort;
496 it = layersToSort.erase( it ); // erase and go to the next
497 mHasCycle = false;
498 }
499 else
500 {
501 ++it;
502 }
503 }
504 }
505}
506
508 : mHasCycle( false )
509 , mHasMissingDependency( false )
510{
511 init( doc );
512}
513
515 : mHasCycle( false )
516 , mHasMissingDependency( false )
517{
518 QString qgsProjectFile = fileName;
519 QgsProjectArchive archive;
520 if ( fileName.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
521 {
522 archive.unzip( fileName );
523 qgsProjectFile = archive.projectFile();
524 }
525
526 QDomDocument doc;
527 QFile pFile( qgsProjectFile );
528 ( void )pFile.open( QIODevice::ReadOnly );
529 ( void )doc.setContent( &pFile );
530 init( doc );
531}
532
534{
535 return mDependentLayerIds.contains( layerId );
536}
537
538
FilePathType
File path types.
Definition: qgis.h:1128
@ Relative
Relative path.
@ Absolute
Absolute path.
LayerType
Types of layers that can be added to a map.
Definition: qgis.h:114
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
Contains information about the context in which a coordinate transform is executed.
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
A map layer which consists of a set of child layers, where all component layers are rendered as a sin...
Definition: qgsgrouplayer.h:42
Class used to work with layer dependencies stored in a XML project or layer definition file.
bool hasMissingDependency() const
Whether some dependency is missing.
bool isLayerDependent(const QString &layerId) const
Returns whether the layer associated with thelayerId is dependent from another layer.
DependencySorter(const QDomDocument &doc)
Constructor.
QVector< QDomNode > sortedLayerNodes() const
Gets the layer nodes in an order where they can be loaded incrementally without dependency break.
static bool loadLayerDefinition(const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage)
Loads the QLR at path into QGIS. New layers are added to given project into layer tree specified by r...
static QDomDocument exportLayerDefinitionLayers(const QList< QgsMapLayer * > &layers, const QgsReadWriteContext &context)
Returns the given layer as a layer definition document Layer definitions store the data source as wel...
static QList< QgsMapLayer * > loadLayerDefinitionLayers(QDomDocument &document, QgsReadWriteContext &context)
Creates new layers from a layer definition document.
static bool exportLayerDefinition(const QString &path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Exports the selected layer tree nodes to a QLR file.
Layer tree group node serves as a container for layers and further groups.
void resolveReferences(const QgsProject *project, bool looseMatching=false) override
Calls resolveReferences() on child tree nodes.
void writeXml(QDomElement &parentElement, const QgsReadWriteContext &context) override
Write group (tree) as XML element <layer-tree-group> and add it to the given parent element.
void addChildNode(QgsLayerTreeNode *node)
Append an existing node.
void insertChildNodes(int index, const QList< QgsLayerTreeNode * > &nodes)
Insert existing nodes at specified position.
void readChildrenFromXml(QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
This class is a base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the childrens, disconnect all the forwarded and external signals and sets their parent to nul...
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
static Qgis::LayerType typeFromString(const QString &string, bool &ok)
Returns the map layer type corresponding a string value.
Base class for all map layer types.
Definition: qgsmaplayer.h:73
static QString generateId(const QString &layerName)
Generates an unique identifier for this layer, the generate ID is prefixed by layerName.
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags(), QgsDataProvider *preloadedProvider=nullptr)
Sets state from DOM document.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:100
Resolves relative paths into absolute paths and vice versa.
QgsPluginLayer * createLayer(const QString &typeName, const QString &uri=QString())
Returns new layer if corresponding plugin has been found else returns nullptr.
Represents a map layer supporting display of point clouds.
Class allowing to manage the zip/unzip actions on project file.
Definition: qgsarchive.h:122
QString projectFile() const
Returns the current .qgs project file or an empty string if there's none.
Definition: qgsarchive.cpp:140
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Definition: qgsarchive.cpp:153
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
QList< QgsMapLayer * > addMapLayers(const QList< QgsMapLayer * > &mapLayers, bool addToLegend=true, bool takeOwnership=true)
Add a list of layers to the map of loaded layers.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:484
Represents a raster layer.
The class is used as a container of context for various read/write operations on other objects.
void setProjectTranslator(QgsProjectTranslator *projectTranslator)
Sets the project translator.
void setPathResolver(const QgsPathResolver &resolver)
Sets up path resolver for conversion between relative and absolute paths.
Represents a vector layer which manages a vector based data sets.
Implements a map layer that is dedicated to rendering of vector tiles.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38
const QString & typeName
Setting options for loading group layers.
Definition: qgsgrouplayer.h:52