QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 
39 bool QgsLayerDefinition::loadLayerDefinition( const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage )
40 {
41  QFile file( path );
42  if ( !file.open( QIODevice::ReadOnly ) )
43  {
44  errorMessage = QStringLiteral( "Can not open file" );
45  return false;
46  }
47 
48  QDomDocument doc;
49  QString message;
50  if ( !doc.setContent( &file, &message ) )
51  {
52  errorMessage = message;
53  return false;
54  }
55 
56  const QFileInfo fileinfo( file );
57  QDir::setCurrent( fileinfo.absoluteDir().path() );
58 
59  QgsReadWriteContext context;
60  context.setPathResolver( QgsPathResolver( path ) );
61  context.setProjectTranslator( project );
62 
63  return loadLayerDefinition( doc, project, rootGroup, errorMessage, context );
64 }
65 
66 bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage, QgsReadWriteContext &context )
67 {
68  errorMessage.clear();
69 
71 
72  // reorder maplayer nodes based on dependencies
73  // dependencies have to be resolved before IDs get changed
74  const DependencySorter depSorter( doc );
75  if ( !depSorter.hasMissingDependency() )
76  {
77  const QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes();
78  QVector<QDomNode> clonedSorted;
79  const auto constSortedLayerNodes = sortedLayerNodes;
80  for ( const QDomNode &node : constSortedLayerNodes )
81  {
82  clonedSorted << node.cloneNode();
83  }
84  QDomNode layersNode = doc.elementsByTagName( QStringLiteral( "maplayers" ) ).at( 0 );
85  // replace old children with new ones
86  QDomNode childNode = layersNode.firstChild();
87  for ( int i = 0; ! childNode.isNull(); i++ )
88  {
89  layersNode.replaceChild( clonedSorted.at( i ), childNode );
90  childNode = childNode.nextSibling();
91  }
92  }
93  // if a dependency is missing, we still try to load layers, since dependencies may already be loaded
94 
95  // IDs of layers should be changed otherwise we may have more then one layer with the same id
96  // We have to replace the IDs before we load them because it's too late once they are loaded
97  const QDomNodeList treeLayerNodes = doc.elementsByTagName( QStringLiteral( "layer-tree-layer" ) );
98  for ( int i = 0; i < treeLayerNodes.length(); ++i )
99  {
100  const QDomNode treeLayerNode = treeLayerNodes.item( i );
101  QDomElement treeLayerElem = treeLayerNode.toElement();
102  const QString oldid = treeLayerElem.attribute( QStringLiteral( "id" ) );
103  const QString layername = treeLayerElem.attribute( QStringLiteral( "name" ) );
104  const QString newid = QgsMapLayer::generateId( layername );
105  treeLayerElem.setAttribute( QStringLiteral( "id" ), newid );
106 
107  // Replace IDs for map layers
108  const QDomNodeList ids = doc.elementsByTagName( QStringLiteral( "id" ) );
109  QDomNode idnode = ids.at( 0 );
110  for ( int j = 0; ! idnode.isNull() ; ++j )
111  {
112  idnode = ids.at( j );
113  const QDomElement idElem = idnode.toElement();
114  if ( idElem.text() == oldid )
115  {
116  idElem.firstChild().setNodeValue( newid );
117  }
118  }
119 
120  // change layer IDs for vector joins
121  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.
122  for ( int j = 0; j < vectorJoinNodes.size(); ++j )
123  {
124  const QDomNode joinNode = vectorJoinNodes.at( j );
125  const QDomElement joinElement = joinNode.toElement();
126  if ( joinElement.attribute( QStringLiteral( "joinLayerId" ) ) == oldid )
127  {
128  joinNode.toElement().setAttribute( QStringLiteral( "joinLayerId" ), newid );
129  }
130  }
131 
132  // change IDs of dependencies
133  const QDomNodeList dataDeps = doc.elementsByTagName( QStringLiteral( "dataDependencies" ) );
134  for ( int i = 0; i < dataDeps.size(); i++ )
135  {
136  const QDomNodeList layers = dataDeps.at( i ).childNodes();
137  for ( int j = 0; j < layers.size(); j++ )
138  {
139  QDomElement elt = layers.at( j ).toElement();
140  if ( elt.attribute( QStringLiteral( "id" ) ) == oldid )
141  {
142  elt.setAttribute( QStringLiteral( "id" ), newid );
143  }
144  }
145  }
146 
147  // Change IDs of widget config values
148  const QDomNodeList widgetConfig = doc.elementsByTagName( QStringLiteral( "editWidget" ) );
149  for ( int i = 0; i < widgetConfig.size(); i++ )
150  {
151  const QDomNodeList config = widgetConfig.at( i ).childNodes();
152  for ( int j = 0; j < config.size(); j++ )
153  {
154  const QDomNodeList optMap = config.at( j ).childNodes();
155  for ( int z = 0; z < optMap.size(); z++ )
156  {
157  const QDomNodeList opts = optMap.at( z ).childNodes();
158  for ( int k = 0; k < opts.size(); k++ )
159  {
160  QDomElement opt = opts.at( k ).toElement();
161  if ( opt.attribute( QStringLiteral( "value" ) ) == oldid )
162  {
163  opt.setAttribute( QStringLiteral( "value" ), newid );
164  }
165  }
166  }
167  }
168  }
169  }
170 
171  QDomElement layerTreeElem = doc.documentElement().firstChildElement( QStringLiteral( "layer-tree-group" ) );
172  bool loadInLegend = true;
173  if ( !layerTreeElem.isNull() )
174  {
175  root->readChildrenFromXml( layerTreeElem, context );
176  loadInLegend = false;
177  }
178 
179  const QList<QgsMapLayer *> layers = QgsLayerDefinition::loadLayerDefinitionLayersInternal( doc, context, errorMessage );
180 
181  project->addMapLayers( layers, loadInLegend );
182 
183  // Now that all layers are loaded, refresh the vectorjoins to get the joined fields
184  const auto constLayers = layers;
185  for ( QgsMapLayer *layer : constLayers )
186  {
187  if ( QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( layer ) )
188  {
189  vlayer->resolveReferences( project );
190  }
191  }
192 
193  root->resolveReferences( project );
194 
195  const QList<QgsLayerTreeNode *> nodes = root->children();
196  root->abandonChildren();
197  delete root;
198 
199  rootGroup->insertChildNodes( -1, nodes );
200 
201  return true;
202 }
203 
204 bool QgsLayerDefinition::exportLayerDefinition( const QString &path, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage )
205 {
206  return exportLayerDefinition( path, selectedTreeNodes, QgsProject::instance()->filePathStorage(), errorMessage );
207 }
208 
209 bool QgsLayerDefinition::exportLayerDefinition( const QString &p, const QList<QgsLayerTreeNode *> &selectedTreeNodes, Qgis::FilePathType pathType, QString &errorMessage )
210 {
211  const QString path = QgsFileUtils::ensureFileNameHasExtension( p, { QStringLiteral( "qlr" )} );
212 
213  QFile file( path );
214  if ( !file.open( QFile::WriteOnly | QFile::Truncate ) )
215  {
216  errorMessage = file.errorString();
217  return false;
218  }
219 
220  QgsReadWriteContext context;
221  switch ( pathType )
222  {
224  context.setPathResolver( QgsPathResolver( QString() ) );
225  break;
227  context.setPathResolver( QgsPathResolver( path ) );
228  break;
229  }
230 
231  const QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
232  if ( !exportLayerDefinition( doc, selectedTreeNodes, errorMessage, context ) )
233  return false;
234 
235  QTextStream qlayerstream( &file );
236  doc.save( qlayerstream, 2 );
237  return true;
238 }
239 
240 bool QgsLayerDefinition::exportLayerDefinition( QDomDocument doc, const QList<QgsLayerTreeNode *> &selectedTreeNodes, QString &errorMessage, const QgsReadWriteContext &context )
241 {
242  Q_UNUSED( errorMessage )
243  QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
244  doc.appendChild( qgiselm );
245  const QList<QgsLayerTreeNode *> nodes = selectedTreeNodes;
247  const auto constNodes = nodes;
248  for ( QgsLayerTreeNode *node : constNodes )
249  {
250  QgsLayerTreeNode *newnode = node->clone();
251  root->addChildNode( newnode );
252  }
253  root->writeXml( qgiselm, context );
254 
255  QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
256  const QList<QgsLayerTreeLayer *> layers = root->findLayers();
257  const auto constLayers = layers;
258  for ( QgsLayerTreeLayer *layer : constLayers )
259  {
260  if ( ! layer->layer() )
261  {
262  QgsDebugMsgLevel( QStringLiteral( "Not a valid map layer: skipping %1" ).arg( layer->name( ) ), 4 );
263  continue;
264  }
265  QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
266  layer->layer()->writeLayerXml( layerelm, doc, context );
267  layerselm.appendChild( layerelm );
268  }
269  qgiselm.appendChild( layerselm );
270  return true;
271 }
272 
273 QDomDocument QgsLayerDefinition::exportLayerDefinitionLayers( const QList<QgsMapLayer *> &layers, const QgsReadWriteContext &context )
274 {
275  QDomDocument doc( QStringLiteral( "qgis-layer-definition" ) );
276  QDomElement qgiselm = doc.createElement( QStringLiteral( "qlr" ) );
277  doc.appendChild( qgiselm );
278  QDomElement layerselm = doc.createElement( QStringLiteral( "maplayers" ) );
279  const auto constLayers = layers;
280  for ( QgsMapLayer *layer : constLayers )
281  {
282  QDomElement layerelm = doc.createElement( QStringLiteral( "maplayer" ) );
283  layer->writeLayerXml( layerelm, doc, context );
284  layerselm.appendChild( layerelm );
285  }
286  qgiselm.appendChild( layerselm );
287  return doc;
288 }
289 
290 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( QDomDocument &document, QgsReadWriteContext &context )
291 {
292  QString errorMessage;
293  return loadLayerDefinitionLayersInternal( document, context, errorMessage );
294 }
295 
296 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayersInternal( QDomDocument &document, QgsReadWriteContext &context, QString &errorMessage )
297 {
298  QList<QgsMapLayer *> layers;
299  QDomElement layerElem = document.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
300  // For QLR:
301  if ( layerElem.isNull() )
302  {
303  layerElem = document.documentElement().firstChildElement( QStringLiteral( "maplayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
304  }
305 
306  while ( ! layerElem.isNull() )
307  {
308  const QString type = layerElem.attribute( QStringLiteral( "type" ) );
309  QgsMapLayer *layer = nullptr;
310 
311  bool ok = false;
312  const QgsMapLayerType layerType = QgsMapLayerFactory::typeFromString( type, ok );
313  if ( ok )
314  {
315  switch ( layerType )
316  {
318  layer = new QgsVectorLayer();
319  break;
320 
322  layer = new QgsRasterLayer();
323  break;
324 
326  {
327  const QString typeName = layerElem.attribute( QStringLiteral( "name" ) );
329  break;
330  }
331 
333  layer = new QgsMeshLayer();
334  break;
335 
337  layer = new QgsVectorTileLayer;
338  break;
339 
341  layer = new QgsPointCloudLayer();
342  break;
343 
345  break;
346  }
347  }
348 
349  if ( layer )
350  {
351  // always add the layer, even if the source is invalid -- this allows users to fix the source
352  // at a later stage and still retain all the layer properties intact
353  layer->readLayerXml( layerElem, context );
354  layers << layer;
355  }
356  else
357  {
358  errorMessage = QObject::tr( "Unsupported layer type: %1" ).arg( type );
359  }
360  layerElem = layerElem.nextSiblingElement( QStringLiteral( "maplayer" ) );
361  }
362  return layers;
363 }
364 
365 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayers( const QString &qlrfile )
366 {
367  QFile file( qlrfile );
368  if ( !file.open( QIODevice::ReadOnly ) )
369  {
370  QgsDebugMsg( QStringLiteral( "Can't open file" ) );
371  return QList<QgsMapLayer *>();
372  }
373 
374  QDomDocument doc;
375  if ( !doc.setContent( &file ) )
376  {
377  QgsDebugMsg( QStringLiteral( "Can't set content" ) );
378  return QList<QgsMapLayer *>();
379  }
380 
381  QgsReadWriteContext context;
382  context.setPathResolver( QgsPathResolver( qlrfile ) );
383  //no project translator defined here
384  return QgsLayerDefinition::loadLayerDefinitionLayers( doc, context );
385 }
386 
387 void QgsLayerDefinition::DependencySorter::init( const QDomDocument &doc )
388 {
389  // Determine a loading order of layers based on a graph of dependencies
390  QMap< QString, QVector< QString > > dependencies;
391  QStringList sortedLayers;
392  QList< QPair<QString, QDomNode> > layersToSort;
393  QStringList layerIds;
394 
395  QDomElement layerElem = doc.documentElement().firstChildElement( QStringLiteral( "projectlayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
396  // For QLR:
397  if ( layerElem.isNull() )
398  {
399  layerElem = doc.documentElement().firstChildElement( QStringLiteral( "maplayers" ) ).firstChildElement( QStringLiteral( "maplayer" ) );
400  }
401  // For tests (I don't know if there is a real use case for such a document except for test_qgslayerdefinition.py)
402  if ( layerElem.isNull() )
403  {
404  layerElem = doc.documentElement().firstChildElement( QStringLiteral( "maplayer" ) );
405  }
406 
407  const QDomElement &firstElement { layerElem };
408 
409  QVector<QString> deps; //avoid expensive allocation for list for every iteration
410  while ( !layerElem.isNull() )
411  {
412  deps.resize( 0 ); // preserve capacity - don't use clear
413 
414  const QString id = layerElem.namedItem( QStringLiteral( "id" ) ).toElement().text();
415  layerIds << id;
416 
417  // dependencies for this layer
418  const QDomElement layerDependenciesElem = layerElem.firstChildElement( QStringLiteral( "layerDependencies" ) );
419  if ( !layerDependenciesElem.isNull() )
420  {
421  const QDomNodeList dependencyList = layerDependenciesElem.elementsByTagName( QStringLiteral( "layer" ) );
422  for ( int j = 0; j < dependencyList.size(); ++j )
423  {
424  const QDomElement depElem = dependencyList.at( j ).toElement();
425  deps << depElem.attribute( QStringLiteral( "id" ) );
426  }
427  }
428  dependencies[id] = deps;
429 
430  if ( deps.empty() )
431  {
432  sortedLayers << id;
433  mSortedLayerNodes << layerElem;
434  mSortedLayerIds << id;
435  }
436  else
437  {
438  layersToSort << qMakePair( id, layerElem );
439  }
440  layerElem = layerElem.nextSiblingElement( );
441  }
442 
443  // check that all dependencies are present
444  const auto constDependencies = dependencies;
445  for ( const QVector< QString > &ids : constDependencies )
446  {
447  const auto constIds = ids;
448  for ( const QString &depId : constIds )
449  {
450  if ( !dependencies.contains( depId ) )
451  {
452  // some dependencies are not satisfied
453  mHasMissingDependency = true;
454  layerElem = firstElement;
455  while ( ! layerElem.isNull() )
456  {
457  mSortedLayerNodes << layerElem;
458  layerElem = layerElem.nextSiblingElement( );
459  }
460  mSortedLayerIds = layerIds;
461  return;
462  }
463  }
464  }
465 
466  // cycles should be very rare, since layers with cyclic dependencies may only be created by
467  // manually modifying the project file
468  mHasCycle = false;
469 
470  while ( !layersToSort.empty() && !mHasCycle )
471  {
472  QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin();
473  while ( it != layersToSort.end() )
474  {
475  const QString idToSort = it->first;
476  const QDomNode node = it->second;
477  mHasCycle = true;
478  bool resolved = true;
479  const auto deps { dependencies.value( idToSort ) };
480  for ( const QString &dep : deps )
481  {
482  if ( !sortedLayers.contains( dep ) )
483  {
484  resolved = false;
485  break;
486  }
487  }
488  if ( resolved ) // dependencies for this layer are resolved
489  {
490  sortedLayers << idToSort;
491  mSortedLayerNodes << node;
492  mSortedLayerIds << idToSort;
493  it = layersToSort.erase( it ); // erase and go to the next
494  mHasCycle = false;
495  }
496  else
497  {
498  ++it;
499  }
500  }
501  }
502 }
503 
505  : mHasCycle( false )
506  , mHasMissingDependency( false )
507 {
508  init( doc );
509 }
510 
512  : mHasCycle( false )
513  , mHasMissingDependency( false )
514 {
515  QString qgsProjectFile = fileName;
516  QgsProjectArchive archive;
517  if ( fileName.endsWith( QLatin1String( ".qgz" ), Qt::CaseInsensitive ) )
518  {
519  archive.unzip( fileName );
520  qgsProjectFile = archive.projectFile();
521  }
522 
523  QDomDocument doc;
524  QFile pFile( qgsProjectFile );
525  ( void )pFile.open( QIODevice::ReadOnly );
526  ( void )doc.setContent( &pFile );
527  init( doc );
528 }
529 
530 
FilePathType
File path types.
Definition: qgis.h:519
@ Relative
Relative path.
@ Absolute
Absolute path.
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
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:467
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
Added in 3.18.
@ MeshLayer
Added in 3.2.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
const QString & typeName