44using namespace Qt::StringLiterals;
51 if ( !file.open( QIODevice::ReadOnly ) )
53 errorMessage = u
"Can not open file"_s;
59 if ( !doc.setContent( &file, &message ) )
61 errorMessage = message;
65 const QFileInfo fileinfo( file );
66 QDir::setCurrent( fileinfo.absoluteDir().path() );
72 return loadLayerDefinition( doc, project, rootGroup, errorMessage, context, insertMethod, insertPoint );
79 QString &errorMessage,
95 QVector<QDomNode> clonedSorted;
96 const auto constSortedLayerNodes = sortedLayerNodes;
97 for (
const QDomNode &node : constSortedLayerNodes )
99 clonedSorted << node.cloneNode();
101 QDomNode layersNode = doc.elementsByTagName( u
"maplayers"_s ).at( 0 );
103 QDomNode childNode = layersNode.firstChild();
104 for (
int i = 0; !childNode.isNull(); i++ )
106 layersNode.replaceChild( clonedSorted.at( i ), childNode );
107 childNode = childNode.nextSibling();
114 const QDomNodeList treeLayerNodes = doc.elementsByTagName( u
"layer-tree-layer"_s );
115 for (
int i = 0; i < treeLayerNodes.length(); ++i )
117 const QDomNode treeLayerNode = treeLayerNodes.item( i );
118 QDomElement treeLayerElem = treeLayerNode.toElement();
119 const QString oldid = treeLayerElem.attribute( u
"id"_s );
120 const QString layername = treeLayerElem.attribute( u
"name"_s );
122 treeLayerElem.setAttribute( u
"id"_s, newid );
125 const QDomNodeList ids = doc.elementsByTagName( u
"id"_s );
126 QDomNode idnode = ids.at( 0 );
127 for (
int j = 0; !idnode.isNull(); ++j )
129 idnode = ids.at( j );
130 const QDomElement idElem = idnode.toElement();
131 if ( idElem.text() == oldid )
133 idElem.firstChild().setNodeValue( newid );
138 const QDomNodeList vectorJoinNodes = doc.elementsByTagName( u
"join"_s );
139 for (
int j = 0; j < vectorJoinNodes.size(); ++j )
141 const QDomNode joinNode = vectorJoinNodes.at( j );
142 const QDomElement joinElement = joinNode.toElement();
143 if ( joinElement.attribute( u
"joinLayerId"_s ) == oldid )
145 joinNode.toElement().setAttribute( u
"joinLayerId"_s, newid );
150 const QDomNodeList dataDeps = doc.elementsByTagName( u
"dataDependencies"_s );
151 for (
int i = 0; i < dataDeps.size(); i++ )
153 const QDomNodeList layers = dataDeps.at( i ).childNodes();
154 for (
int j = 0; j < layers.size(); j++ )
156 QDomElement elt = layers.at( j ).toElement();
157 if ( elt.attribute( u
"id"_s ) == oldid )
159 elt.setAttribute( u
"id"_s, newid );
165 const QDomNodeList widgetConfig = doc.elementsByTagName( u
"editWidget"_s );
166 for (
int i = 0; i < widgetConfig.size(); i++ )
168 const QDomNodeList config = widgetConfig.at( i ).childNodes();
169 for (
int j = 0; j < config.size(); j++ )
171 const QDomNodeList optMap = config.at( j ).childNodes();
172 for (
int z = 0; z < optMap.size(); z++ )
174 const QDomNodeList opts = optMap.at( z ).childNodes();
175 for (
int k = 0; k < opts.size(); k++ )
177 QDomElement opt = opts.at( k ).toElement();
178 if ( opt.attribute( u
"value"_s ) == oldid )
180 opt.setAttribute( u
"value"_s, newid );
188 QDomElement layerTreeElem = doc.documentElement().firstChildElement( u
"layer-tree-group"_s );
189 bool loadInLegend =
true;
190 if ( !layerTreeElem.isNull() )
196 loadInLegend =
false;
199 const QList<QgsMapLayer *> layers = QgsLayerDefinition::loadLayerDefinitionLayersInternal( doc, context, errorMessage );
204 const auto constLayers = layers;
207 layer->resolveReferences( project );
212 const QList<QgsLayerTreeNode *> nodes = root.
children();
215 switch ( insertMethod )
247 if ( !file.open( QFile::WriteOnly | QFile::Truncate ) )
249 errorMessage = file.errorString();
264 const QDomDocument doc( u
"qgis-layer-definition"_s );
268 QTextStream qlayerstream( &file );
269 doc.save( qlayerstream, 2 );
275 Q_UNUSED( errorMessage )
276 QDomElement qgiselm = doc.createElement( u
"qlr"_s );
277 doc.appendChild( qgiselm );
286 QDomElement layerselm = doc.createElement( u
"maplayers"_s );
287 const QList<QgsLayerTreeLayer *> layers = root.
findLayers();
290 if ( !layer->layer() )
292 QgsDebugMsgLevel( u
"Not a valid map layer: skipping %1"_s.arg( layer->name() ), 4 );
295 QDomElement layerelm = doc.createElement( u
"maplayer"_s );
296 layer->layer()->writeLayerXml( layerelm, doc, context );
297 layerselm.appendChild( layerelm );
299 qgiselm.appendChild( layerselm );
305 QDomDocument doc( u
"qgis-layer-definition"_s );
306 QDomElement qgiselm = doc.createElement( u
"qlr"_s );
307 doc.appendChild( qgiselm );
308 QDomElement layerselm = doc.createElement( u
"maplayers"_s );
309 const auto constLayers = layers;
312 QDomElement layerelm = doc.createElement( u
"maplayer"_s );
313 layer->writeLayerXml( layerelm, doc, context );
314 layerselm.appendChild( layerelm );
316 qgiselm.appendChild( layerselm );
322 QString errorMessage;
323 return loadLayerDefinitionLayersInternal( document, context, errorMessage );
326QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayersInternal( QDomDocument &document,
QgsReadWriteContext &context, QString &errorMessage )
328 QList<QgsMapLayer *> layers;
329 QDomElement layerElem = document.documentElement().firstChildElement( u
"projectlayers"_s ).firstChildElement( u
"maplayer"_s );
331 if ( layerElem.isNull() )
333 layerElem = document.documentElement().firstChildElement( u
"maplayers"_s ).firstChildElement( u
"maplayer"_s );
336 while ( !layerElem.isNull() )
338 const QString type = layerElem.attribute( u
"type"_s );
339 QgsMapLayer *layer =
nullptr;
348 layer =
new QgsVectorLayer();
352 layer =
new QgsRasterLayer();
357 const QString typeName = layerElem.attribute( u
"name"_s );
363 layer =
new QgsMeshLayer();
367 layer =
new QgsVectorTileLayer;
371 layer =
new QgsPointCloudLayer();
375 layer =
new QgsTiledSceneLayer;
379 layer =
new QgsGroupLayer( QString(), QgsGroupLayer::LayerOptions( QgsCoordinateTransformContext() ) );
396 errorMessage = QObject::tr(
"Unsupported layer type: %1" ).arg( type );
398 layerElem = layerElem.nextSiblingElement( u
"maplayer"_s );
405 QFile file( qlrfile );
406 if ( !file.open( QIODevice::ReadOnly ) )
409 return QList<QgsMapLayer *>();
413 if ( !doc.setContent( &file ) )
416 return QList<QgsMapLayer *>();
425void QgsLayerDefinition::DependencySorter::init(
const QDomDocument &doc )
428 QMap< QString, QVector< QString > > dependencies;
429 QStringList sortedLayers;
430 QList< QPair<QString, QDomNode> > layersToSort;
431 QStringList layerIds;
433 QDomElement layerElem = doc.documentElement().firstChildElement( u
"projectlayers"_s ).firstChildElement( u
"maplayer"_s );
435 if ( layerElem.isNull() )
437 layerElem = doc.documentElement().firstChildElement( u
"maplayers"_s ).firstChildElement( u
"maplayer"_s );
440 if ( layerElem.isNull() )
442 layerElem = doc.documentElement().firstChildElement( u
"maplayer"_s );
445 const QDomElement &firstElement { layerElem };
447 QVector<QString> deps;
448 while ( !layerElem.isNull() )
452 const QString
id = layerElem.namedItem( u
"id"_s ).toElement().text();
456 const QDomElement layerDependenciesElem = layerElem.firstChildElement( u
"layerDependencies"_s );
457 if ( !layerDependenciesElem.isNull() )
459 const QDomNodeList dependencyList = layerDependenciesElem.elementsByTagName( u
"layer"_s );
460 for (
int j = 0; j < dependencyList.size(); ++j )
462 const QDomElement depElem = dependencyList.at( j ).toElement();
463 deps << depElem.attribute( u
"id"_s );
466 dependencies[id] = deps;
471 mSortedLayerNodes << layerElem;
472 mSortedLayerIds << id;
476 layersToSort << qMakePair(
id, layerElem );
477 mDependentLayerIds.insert(
id );
479 layerElem = layerElem.nextSiblingElement();
483 const auto constDependencies = dependencies;
484 for (
const QVector< QString > &ids : constDependencies )
486 const auto constIds = ids;
487 for (
const QString &depId : constIds )
489 if ( !dependencies.contains( depId ) )
492 mHasMissingDependency =
true;
493 layerElem = firstElement;
494 while ( !layerElem.isNull() )
496 mSortedLayerNodes << layerElem;
497 layerElem = layerElem.nextSiblingElement();
499 mSortedLayerIds = layerIds;
509 while ( !layersToSort.empty() && !mHasCycle )
511 QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin();
512 while ( it != layersToSort.end() )
514 const QString idToSort = it->first;
515 const QDomNode node = it->second;
517 bool resolved =
true;
518 const auto deps { dependencies.value( idToSort ) };
519 for (
const QString &dep : deps )
521 if ( !sortedLayers.contains( dep ) )
529 sortedLayers << idToSort;
530 mSortedLayerNodes << node;
531 mSortedLayerIds << idToSort;
532 it = layersToSort.erase( it );
545 , mHasMissingDependency( false )
552 , mHasMissingDependency( false )
554 QString qgsProjectFile = fileName;
556 if ( fileName.endsWith(
".qgz"_L1, Qt::CaseInsensitive ) )
558 archive.unzip( fileName );
559 qgsProjectFile = archive.projectFile();
563 QFile pFile( qgsProjectFile );
564 ( void ) pFile.open( QIODevice::ReadOnly );
565 ( void ) doc.setContent( &pFile );
571 return mDependentLayerIds.contains( layerId );
LayerTreeInsertionMethod
Layer tree insertion methods.
@ TopOfTree
Layers are added at the top of the layer tree.
@ AboveInsertionPoint
Layers are added in the tree above the insertion point.
FilePathType
File path types.
LayerType
Types of layers that can be added to a map.
@ Group
Composite group layer. Added in QGIS 3.24.
@ Plugin
Plugin based layer.
@ TiledScene
Tiled scene layer. Added in QGIS 3.34.
@ Annotation
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
@ VectorTile
Vector tile layer. Added in QGIS 3.14.
@ Mesh
Mesh layer. Added in QGIS 3.2.
@ PointCloud
Point cloud layer. Added in QGIS 3.18.
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.
Handles sorting of 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 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 loadLayerDefinition(const QString &path, QgsProject *project, QgsLayerTreeGroup *rootGroup, QString &errorMessage, Qgis::LayerTreeInsertionMethod insertMethod=Qgis::LayerTreeInsertionMethod::OptimalInInsertionGroup, const QgsLayerTreeRegistryBridge::InsertionPoint *insertPoint=nullptr)
Loads the QLR at path into QGIS.
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 readChildrenFromXml(const QDomElement &element, const QgsReadWriteContext &context)
Read children from XML and append them to the group.
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.
QList< QgsLayerTreeLayer * > findLayers() const
Find all layer nodes.
Layer tree node points to a map layer.
Base class for nodes in a layer tree.
QList< QgsLayerTreeNode * > abandonChildren()
Removes the children, disconnect all the forwarded and external signals and sets their parent to null...
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 void regenerateGroupIds(QgsLayerTreeNode *node)
Assigns a new unique id to every group in the sub-tree rooted at node.
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.
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.
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.
Allows managing the zip/unzip actions on project files.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
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.
A container for the context for various read/write operations on 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.
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
A structure to define the insertion point to the layer tree.
QgsLayerTreeGroup * group