@@ -40,6 +40,33 @@ bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsLayerTreeGrou
4040
4141 QgsLayerTreeGroup *root = new QgsLayerTreeGroup ();
4242
43+ // reorder maplayer nodes based on dependencies
44+ // dependencies have to be resolved before IDs get changed
45+ DependencySorter depSorter ( doc );
46+ if ( !depSorter.hasMissingDependency () )
47+ {
48+ QVector<QDomNode> sortedLayerNodes = depSorter.sortedLayerNodes ();
49+ QVector<QDomNode> clonedSorted;
50+ foreach ( QDomNode node, sortedLayerNodes )
51+ {
52+ clonedSorted << node.cloneNode ();
53+ }
54+ QDomNode layersNode = doc.elementsByTagName ( " maplayers" ).at ( 0 );
55+ // remove old children
56+ QDomNodeList childNodes = layersNode.childNodes ();
57+ for ( int i = 0 ; i < childNodes.size (); i++ )
58+ {
59+ layersNode.removeChild ( childNodes.at ( i ) );
60+ }
61+ // replace with new ones
62+ foreach ( QDomNode node, clonedSorted )
63+ {
64+ layersNode.appendChild ( node );
65+ }
66+ }
67+ // if a dependency is missing, we still try to load layers, since dependencies may already be loaded
68+
69+ // IDs of layers should be changed otherwise we may have more then one layer with the same id
4370 // We have to replace the IDs before we load them because it's too late once they are loaded
4471 QDomNodeList ids = doc.elementsByTagName ( " id" );
4572 for ( int i = 0 ; i < ids.size (); ++i )
@@ -85,8 +112,7 @@ bool QgsLayerDefinition::loadLayerDefinition( QDomDocument doc, QgsLayerTreeGrou
85112 loadInLegend = false ;
86113 }
87114
88- QList<QgsMapLayer*> layers = QgsMapLayer::fromLayerDefinition ( doc );
89- QgsMapLayerRegistry::instance ()->addMapLayers ( layers, loadInLegend );
115+ QList<QgsMapLayer*> layers = QgsMapLayer::fromLayerDefinition ( doc, /* addToRegistry*/ true , loadInLegend );
90116
91117 // Now that all layers are loaded, refresh the vectorjoins to get the joined fields
92118 Q_FOREACH ( QgsMapLayer* layer, layers )
@@ -159,3 +185,115 @@ bool QgsLayerDefinition::exportLayerDefinition( QDomDocument doc, const QList<Qg
159185 qgiselm.appendChild ( layerselm );
160186 return true ;
161187}
188+
189+ void QgsLayerDefinition::DependencySorter::init ( QDomDocument doc )
190+ {
191+ // Determine a loading order of layers based on a graph of dependencies
192+ QMap< QString, QVector< QString > > dependencies;
193+ QStringList sortedLayers;
194+ QList< QPair<QString, QDomNode> > layersToSort;
195+ QStringList layerIds;
196+
197+ QDomNodeList nl = doc.elementsByTagName ( " maplayer" );
198+ for ( int i = 0 ; i < nl.count (); i++ )
199+ {
200+ QVector<QString> deps;
201+ QDomNode node = nl.item ( i );
202+ QDomElement element = node.toElement ();
203+
204+ QString id = node.namedItem ( " id" ).toElement ().text ();
205+ layerIds << id;
206+
207+ // dependencies for this layer
208+ QDomElement layerDependenciesElem = node.firstChildElement ( " layerDependencies" );
209+ if ( !layerDependenciesElem.isNull () )
210+ {
211+ QDomNodeList dependencyList = layerDependenciesElem.elementsByTagName ( " layer" );
212+ for ( int j = 0 ; j < dependencyList.size (); ++j )
213+ {
214+ QDomElement depElem = dependencyList.at ( j ).toElement ();
215+ deps << depElem.attribute ( " id" );
216+ }
217+ }
218+ dependencies[id] = deps;
219+
220+ if ( deps.empty () )
221+ {
222+ sortedLayers << id;
223+ mSortedLayerNodes << node;
224+ }
225+ else
226+ layersToSort << qMakePair ( id, node );
227+ }
228+
229+ // check that all dependencies are present
230+ foreach ( QString id, dependencies.keys () )
231+ {
232+ foreach ( QString depId, dependencies[id] )
233+ {
234+ if ( !dependencies.contains ( depId ) )
235+ {
236+ // some dependencies are not satisfied
237+ mHasMissingDependency = true ;
238+ for ( int i = 0 ; i < nl.size (); i++ )
239+ mSortedLayerNodes << nl.at ( i );
240+ mSortedLayerIds = layerIds;
241+ return ;
242+ }
243+ }
244+ }
245+
246+ // cycles should be very rare, since layers with cyclic dependencies may only be created by
247+ // manually modifying the project file
248+ mHasCycle = false ;
249+
250+ while ( !layersToSort.empty () && !mHasCycle )
251+ {
252+ QList< QPair<QString, QDomNode> >::iterator it = layersToSort.begin ();
253+ while ( it != layersToSort.end () )
254+ {
255+ QString idToSort = it->first ;
256+ QDomNode node = it->second ;
257+ mHasCycle = true ;
258+ bool resolved = true ;
259+ foreach ( QString dep, dependencies[idToSort] )
260+ {
261+ if ( !sortedLayers.contains ( dep ) )
262+ {
263+ resolved = false ;
264+ break ;
265+ }
266+ }
267+ if ( resolved ) // dependencies for this layer are resolved
268+ {
269+ sortedLayers << idToSort;
270+ mSortedLayerNodes << node;
271+ mSortedLayerIds << idToSort;
272+ it = layersToSort.erase ( it ); // erase and go to the next
273+ mHasCycle = false ;
274+ }
275+ else
276+ {
277+ it++;
278+ }
279+ }
280+ }
281+ }
282+
283+ QgsLayerDefinition::DependencySorter::DependencySorter ( QDomDocument doc ) :
284+ mHasCycle( false ), mHasMissingDependency( false )
285+ {
286+ init ( doc );
287+ }
288+
289+ QgsLayerDefinition::DependencySorter::DependencySorter ( const QString& fileName ) :
290+ mHasCycle( false ), mHasMissingDependency( false )
291+ {
292+ QDomDocument doc;
293+ QFile pFile ( fileName );
294+ pFile.open ( QIODevice::ReadOnly );
295+ doc.setContent ( &pFile );
296+ init ( doc );
297+ }
298+
299+
0 commit comments