18 #include <QTextStream>
41 if ( !file.open( QIODevice::ReadOnly ) )
43 errorMessage = QStringLiteral(
"Can not open file" );
49 if ( !doc.setContent( &file, &message ) )
51 errorMessage = message;
55 QFileInfo fileinfo( file );
56 QDir::setCurrent( fileinfo.absoluteDir().path() );
77 QVector<QDomNode> clonedSorted;
78 const auto constSortedLayerNodes = sortedLayerNodes;
79 for (
const QDomNode &node : constSortedLayerNodes )
81 clonedSorted << node.cloneNode();
83 QDomNode layersNode = doc.elementsByTagName( QStringLiteral(
"maplayers" ) ).at( 0 );
85 QDomNode childNode = layersNode.firstChild();
86 for (
int i = 0; ! childNode.isNull(); i++ )
88 layersNode.replaceChild( clonedSorted.at( i ), childNode );
89 childNode = childNode.nextSibling();
96 const QDomNodeList treeLayerNodes = doc.elementsByTagName( QStringLiteral(
"layer-tree-layer" ) );
97 QDomNode treeLayerNode = treeLayerNodes.at( 0 );
98 for (
int i = 0; ! treeLayerNode.isNull(); ++i )
100 treeLayerNode = treeLayerNodes.at( i );
101 QDomElement treeLayerElem = treeLayerNode.toElement();
102 QString oldid = treeLayerElem.attribute( QStringLiteral(
"id" ) );
103 QString layername = treeLayerElem.attribute( QStringLiteral(
"name" ) );
105 treeLayerElem.setAttribute( QStringLiteral(
"id" ), newid );
108 const QDomNodeList ids = doc.elementsByTagName( QStringLiteral(
"id" ) );
109 QDomNode idnode = ids.at( 0 );
110 for (
int j = 0; ! idnode.isNull() ; ++j )
112 idnode = ids.at( j );
113 QDomElement idElem = idnode.toElement();
114 if ( idElem.text() == oldid )
116 idElem.firstChild().setNodeValue( newid );
121 const QDomNodeList vectorJoinNodes = doc.elementsByTagName( QStringLiteral(
"join" ) );
122 for (
int j = 0; j < vectorJoinNodes.size(); ++j )
124 QDomNode joinNode = vectorJoinNodes.at( j );
125 QDomElement joinElement = joinNode.toElement();
126 if ( joinElement.attribute( QStringLiteral(
"joinLayerId" ) ) == oldid )
128 joinNode.toElement().setAttribute( QStringLiteral(
"joinLayerId" ), newid );
133 const QDomNodeList dataDeps = doc.elementsByTagName( QStringLiteral(
"dataDependencies" ) );
134 for (
int i = 0; i < dataDeps.size(); i++ )
136 QDomNodeList layers = dataDeps.at( i ).childNodes();
137 for (
int j = 0; j < layers.size(); j++ )
139 QDomElement elt = layers.at( j ).toElement();
140 if ( elt.attribute( QStringLiteral(
"id" ) ) == oldid )
142 elt.setAttribute( QStringLiteral(
"id" ), newid );
148 QDomNodeList widgetConfig = doc.elementsByTagName( QStringLiteral(
"editWidget" ) );
149 for (
int i = 0; i < widgetConfig.size(); i++ )
151 QDomNodeList config = widgetConfig.at( i ).childNodes();
152 for (
int j = 0; j < config.size(); j++ )
154 QDomNodeList optMap = config.at( j ).childNodes();
155 for (
int z = 0; z < optMap.size(); z++ )
157 QDomNodeList opts = optMap.at( z ).childNodes();
158 for (
int k = 0; k < opts.size(); k++ )
160 QDomElement opt = opts.at( k ).toElement();
161 if ( opt.attribute( QStringLiteral(
"value" ) ) == oldid )
163 opt.setAttribute( QStringLiteral(
"value" ), newid );
171 QDomElement layerTreeElem = doc.documentElement().firstChildElement( QStringLiteral(
"layer-tree-group" ) );
172 bool loadInLegend =
true;
173 if ( !layerTreeElem.isNull() )
176 loadInLegend =
false;
179 QList<QgsMapLayer *> layers = QgsLayerDefinition::loadLayerDefinitionLayersInternal( doc, context, errorMessage );
184 const auto constLayers = layers;
187 if (
QgsVectorLayer *vlayer = qobject_cast< QgsVectorLayer * >( layer ) )
189 vlayer->resolveReferences( project );
195 QList<QgsLayerTreeNode *> nodes = root->
children();
206 if ( !path.endsWith( QLatin1String(
".qlr" ) ) )
207 path = path.append(
".qlr" );
211 if ( !file.open( QFile::WriteOnly | QFile::Truncate ) )
213 errorMessage = file.errorString();
221 QDomDocument doc( QStringLiteral(
"qgis-layer-definition" ) );
225 QTextStream qlayerstream( &file );
226 doc.save( qlayerstream, 2 );
232 Q_UNUSED( errorMessage )
233 QDomElement qgiselm = doc.createElement( QStringLiteral(
"qlr" ) );
234 doc.appendChild( qgiselm );
235 QList<QgsLayerTreeNode *> nodes = selectedTreeNodes;
237 const auto constNodes = nodes;
245 QDomElement layerselm = doc.createElement( QStringLiteral(
"maplayers" ) );
246 QList<QgsLayerTreeLayer *> layers = root->
findLayers();
247 const auto constLayers = layers;
250 if ( ! layer->layer() )
252 QgsDebugMsgLevel( QStringLiteral(
"Not a valid map layer: skipping %1" ).arg( layer->name( ) ), 4 );
255 QDomElement layerelm = doc.createElement( QStringLiteral(
"maplayer" ) );
256 layer->layer()->writeLayerXml( layerelm, doc, context );
257 layerselm.appendChild( layerelm );
259 qgiselm.appendChild( layerselm );
265 QDomDocument doc( QStringLiteral(
"qgis-layer-definition" ) );
266 QDomElement qgiselm = doc.createElement( QStringLiteral(
"qlr" ) );
267 doc.appendChild( qgiselm );
268 QDomElement layerselm = doc.createElement( QStringLiteral(
"maplayers" ) );
269 const auto constLayers = layers;
272 QDomElement layerelm = doc.createElement( QStringLiteral(
"maplayer" ) );
273 layer->writeLayerXml( layerelm, doc, context );
274 layerselm.appendChild( layerelm );
276 qgiselm.appendChild( layerselm );
282 QString errorMessage;
283 return loadLayerDefinitionLayersInternal( document, context, errorMessage );
286 QList<QgsMapLayer *> QgsLayerDefinition::loadLayerDefinitionLayersInternal( QDomDocument &document,
QgsReadWriteContext &context, QString &errorMessage )
288 QList<QgsMapLayer *> layers;
289 QDomElement layerElem = document.documentElement().firstChildElement( QStringLiteral(
"projectlayers" ) ).firstChildElement( QStringLiteral(
"maplayer" ) );
291 if ( layerElem.isNull() )
293 layerElem = document.documentElement().firstChildElement( QStringLiteral(
"maplayers" ) ).firstChildElement( QStringLiteral(
"maplayer" ) );
296 while ( ! layerElem.isNull() )
298 const QString type = layerElem.attribute( QStringLiteral(
"type" ) );
317 QString
typeName = layerElem.attribute( QStringLiteral(
"name" ) );
348 errorMessage = QObject::tr(
"Unsupported layer type: %1" ).arg( type );
350 layerElem = layerElem.nextSiblingElement( QStringLiteral(
"maplayer" ) );
357 QFile file( qlrfile );
358 if ( !file.open( QIODevice::ReadOnly ) )
360 QgsDebugMsg( QStringLiteral(
"Can't open file" ) );
361 return QList<QgsMapLayer *>();
365 if ( !doc.setContent( &file ) )
367 QgsDebugMsg( QStringLiteral(
"Can't set content" ) );
368 return QList<QgsMapLayer *>();
377 void QgsLayerDefinition::DependencySorter::init(
const QDomDocument &doc )
380 QMap< QString, QVector< QString > > dependencies;
381 QStringList sortedLayers;
382 QList< QPair<QString, QDomNode> > layersToSort;
383 QStringList layerIds;
385 QDomElement layerElem = doc.documentElement().firstChildElement( QStringLiteral(
"projectlayers" ) ).firstChildElement( QStringLiteral(
"maplayer" ) );
387 if ( layerElem.isNull() )
389 layerElem = doc.documentElement().firstChildElement( QStringLiteral(
"maplayers" ) ).firstChildElement( QStringLiteral(
"maplayer" ) );
392 if ( layerElem.isNull() )
394 layerElem = doc.documentElement().firstChildElement( QStringLiteral(
"maplayer" ) );
397 const QDomElement &firstElement { layerElem };
399 QVector<QString> deps;
400 while ( !layerElem.isNull() )
404 QString
id = layerElem.namedItem( QStringLiteral(
"id" ) ).toElement().text();
408 QDomElement layerDependenciesElem = layerElem.firstChildElement( QStringLiteral(
"layerDependencies" ) );
409 if ( !layerDependenciesElem.isNull() )
411 QDomNodeList dependencyList = layerDependenciesElem.elementsByTagName( QStringLiteral(
"layer" ) );
412 for (
int j = 0; j < dependencyList.size(); ++j )
414 QDomElement depElem = dependencyList.at( j ).toElement();
415 deps << depElem.attribute( QStringLiteral(
"id" ) );
418 dependencies[id] = deps;
423 mSortedLayerNodes << layerElem;
424 mSortedLayerIds << id;
428 layersToSort << qMakePair(
id, layerElem );
430 layerElem = layerElem.nextSiblingElement( );
434 const auto constDependencies = dependencies;
435 for (
const QVector< QString > &ids : constDependencies )
437 const auto constIds = ids;
438 for (
const QString &depId : constIds )
440 if ( !dependencies.contains( depId ) )
443 mHasMissingDependency =
true;
444 layerElem = firstElement;
445 while ( ! layerElem.isNull() )
447 mSortedLayerNodes << layerElem;
448 layerElem = layerElem.nextSiblingElement( );
450 mSortedLayerIds = layerIds;
460 while ( !layersToSort.empty() && !mHasCycle )
462 QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin();
463 while ( it != layersToSort.end() )
465 QString idToSort = it->first;
466 QDomNode node = it->second;
468 bool resolved =
true;
469 const auto deps { dependencies.value( idToSort ) };
470 for (
const QString &dep : deps )
472 if ( !sortedLayers.contains( dep ) )
480 sortedLayers << idToSort;
481 mSortedLayerNodes << node;
482 mSortedLayerIds << idToSort;
483 it = layersToSort.erase( it );
496 , mHasMissingDependency( false )
503 , mHasMissingDependency( false )
505 QString qgsProjectFile = fileName;
507 if ( fileName.endsWith( QLatin1String(
".qgz" ), Qt::CaseInsensitive ) )
509 archive.
unzip( fileName );
514 QFile pFile( qgsProjectFile );
515 ( void )pFile.open( QIODevice::ReadOnly );
516 ( void )doc.setContent( &pFile );
static QgsPluginLayerRegistry * pluginLayerRegistry()
Returns the application's plugin layer registry, used for managing plugin layer types.
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(QString path, const QList< QgsLayerTreeNode * > &selectedTreeNodes, QString &errorMessage)
Export 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.
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.
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.
QString projectFile() const
Returns the current .qgs project file or an empty string if there's none.
bool unzip(const QString &zipFilename) override
Clear the current content of this archive and unzip.
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.
bool readBoolEntry(const QString &scope, const QString &key, bool def=false, bool *ok=nullptr) const
Reads a boolean from the specified scope and key.
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.
@ PointCloudLayer
Added in 3.18.
@ VectorTileLayer
Added in 3.14.
@ AnnotationLayer
Contains freeform, georeferenced annotations. Added in QGIS 3.16.
#define QgsDebugMsgLevel(str, level)