QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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 "qgslayertree.h"
22 #include "qgslogger.h"
23 #include "qgsmaplayer.h"
24 #include "qgspathresolver.h"
25 #include "qgspluginlayer.h"
26 #include "qgspluginlayerregistry.h"
27 #include "qgsproject.h"
28 #include "qgsrasterlayer.h"
29 #include "qgsreadwritecontext.h"
30 #include "qgsvectorlayer.h"
31 #include "qgsvectortilelayer.h"
32 #include "qgsapplication.h"
33 #include "qgsmaplayerfactory.h"
34 #include "qgsmeshlayer.h"
35 #include "qgspointcloudlayer.h"
36 #include "qgsannotationlayer.h"
37 #include "qgsfileutils.h"
38 #include "qgsgrouplayer.h"
39 
40 bool 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 
67 bool 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 
202 bool QgsLayerDefinition::exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage )
203 {
204  return exportLayerDefinition( path, selectedTreeNodes, QgsProject::instance()->filePathStorage(), errorMessage );
205 }
206 
207 bool 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 
238 bool 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 
271 QDomDocument 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 
288 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( QDomDocument &document, QgsReadWriteContext &context )
289 {
290  QString errorMessage;
291  return loadLayerDefinitionLayersInternal( document, context, errorMessage );
292 }
293 
294 QList<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 QgsMapLayerType layerType = QgsMapLayerFactory::typeFromString( type, ok );
311  if ( ok )
312  {
313  switch ( layerType )
314  {
316  layer = new QgsVectorLayer();
317  break;
318 
320  layer = new QgsRasterLayer();
321  break;
322 
324  {
325  const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
327  break;
328  }
329 
331  layer = new QgsMeshLayer();
332  break;
333 
335  layer = new QgsVectorTileLayer;
336  break;
337 
339  layer = new QgsPointCloudLayer();
340  break;
341 
344  break;
345 
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 
367 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( const QString &qlrfile )
368 {
369  QFile file( qlrfile );
370  if ( !file.open( QIODevice::ReadOnly ) )
371  {
372  QgsDebugMsg( QStringLiteral( "Can't open file" ) );
373  return QList<QgsMapLayer *>();
374  }
375 
376  QDomDocument doc;
377  if ( !doc.setContent( &file ) )
378  {
379  QgsDebugMsg( 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
386  return QgsLayerDefinition::loadLayerDefinitionLayers( doc, context );
387 }
388 
389 void 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  }
442  layerElem = layerElem.nextSiblingElement( );
443  }
444 
445  // check that all dependencies are present
446  const auto constDependencies = dependencies;
447  for ( const QVector< QString > &ids : constDependencies )
448  {
449  const auto constIds = ids;
450  for ( const QString &depId : constIds )
451  {
452  if ( !dependencies.contains( depId ) )
453  {
454  // some dependencies are not satisfied
455  mHasMissingDependency = true;
456  layerElem = firstElement;
457  while ( ! layerElem.isNull() )
458  {
459  mSortedLayerNodes << layerElem;
460  layerElem = layerElem.nextSiblingElement( );
461  }
462  mSortedLayerIds = layerIds;
463  return;
464  }
465  }
466  }
467 
468  // cycles should be very rare, since layers with cyclic dependencies may only be created by
469  // manually modifying the project file
470  mHasCycle = false;
471 
472  while ( !layersToSort.empty() && !mHasCycle )
473  {
474  QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin();
475  while ( it != layersToSort.end() )
476  {
477  const QString idToSort = it->first;
478  const QDomNode node = it->second;
479  mHasCycle = true;
480  bool resolved = true;
481  const auto deps { dependencies.value( idToSort ) };
482  for ( const QString &dep : deps )
483  {
484  if ( !sortedLayers.contains( dep ) )
485  {
486  resolved = false;
487  break;
488  }
489  }
490  if ( resolved ) // dependencies for this layer are resolved
491  {
492  sortedLayers << idToSort;
493  mSortedLayerNodes << node;
494  mSortedLayerIds << idToSort;
495  it = layersToSort.erase( it ); // erase and go to the next
496  mHasCycle = false;
497  }
498  else
499  {
500  ++it;
501  }
502  }
503  }
504 }
505 
507  : mHasCycle( false )
508  , mHasMissingDependency( false )
509 {
510  init( doc );
511 }
512 
514  : mHasCycle( false )
515  , mHasMissingDependency( false )
516 {
517  QString qgsProjectFile = fileName;
518  QgsProjectArchive archive;
519  if ( fileName.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
520  {
521  archive.unzip( fileName );
522  qgsProjectFile = archive.projectFile();
523  }
524 
525  QDomDocument doc;
526  QFile pFile( qgsProjectFile );
527  ( void )pFile.open( QIODevice::ReadOnly );
528  ( void )doc.setContent( &pFile );
529  init( doc );
530 }
531 
532 
FilePathType
File path types.
Definition: qgis.h:533
@ Relative
Relative path.
@ Absolute
Absolute path.
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.
QVector< QDomNode > sortedLayerNodes() const
Gets the layer nodes in an order where they can be loaded incrementally without dependency break.
bool hasMissingDependency() const
Whether some dependency is missing.
DependencySorter(const QDomDocument &doc)
Constructor.
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...
virtual QgsLayerTreeNode * clone() const =0
Create a copy of the node. Returns new instance.
QList< QgsLayerTreeNode * > children()
Gets list of children of the node. Children are owned by the parent.
static QgsMapLayerType 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
bool readLayerXml(const QDomElement &layerElement, QgsReadWriteContext &context, QgsMapLayer::ReadFlags flags=QgsMapLayer::ReadFlags())
Sets state from DOM document.
static QString generateId(const QString &layerName)
Generates an unique identifier for this layer, the generate ID is prefixed by layerName.
Represents a mesh layer supporting display of data on structured or unstructured meshes.
Definition: qgsmeshlayer.h:97
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:134
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
Definition: qgsarchive.cpp:147
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:101
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:470
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.
QgsMapLayerType
Types of layers that can be added to a map.
Definition: qgis.h:47
@ PointCloudLayer
Point cloud layer. Added in QGIS 3.18.
@ MeshLayer
Mesh layer. Added in QGIS 3.2.
@ VectorLayer
Vector layer.
@ RasterLayer
Raster layer.
@ GroupLayer
Composite group layer. Added in QGIS 3.24.
@ VectorTileLayer
Vector tile layer. Added in QGIS 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ PluginLayer
Plugin based layer.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QString & typeName
Setting options for loading group layers.
Definition: qgsgrouplayer.h:52