@@ -152,18 +152,20 @@ export function importQgisProject(
152152 const project = createEmptyProject ( projectName , {
153153 mapView : parseMapView ( document ) ,
154154 } ) ;
155- const parsedGroups = parseLayerGroups ( document ) ;
156- const groupByLayerId = layerGroupAssignments ( document , parsedGroups . ids ) ;
157- const visibilityByLayerId = layerVisibility ( document ) ;
155+ const treeRoot = layerTreeRoot ( document ) ;
156+ const parsedGroups = parseLayerGroups ( treeRoot ) ;
157+ const groupByLayerId = layerGroupAssignments ( treeRoot , parsedGroups . ids ) ;
158+ const visibilityByLayerId = layerVisibility ( treeRoot ) ;
158159 const mapLayers = Array . from ( document . querySelectorAll ( "projectlayers > maplayer" ) ) ;
159160 const byId = new Map (
160161 mapLayers . map ( ( element ) => [ text ( element . querySelector ( ":scope > id" ) ) , element ] ) ,
161162 ) ;
163+ const orderedIds = layerOrder ( treeRoot , mapLayers ) ;
162164 const warnings : QgisProjectImportWarning [ ] = [ ] ;
163165 const layers : GeoLibreLayer [ ] = [ ] ;
164166 const rasters : QgisRasterImport [ ] = [ ] ;
165167
166- for ( const id of layerOrder ( document , mapLayers ) ) {
168+ for ( const id of orderedIds ) {
167169 const element = byId . get ( id ) ;
168170 if ( ! element ) continue ;
169171 const name = text ( element . querySelector ( ":scope > layername" ) ) || id || "QGIS layer" ;
@@ -173,7 +175,7 @@ export function importQgisProject(
173175 if (
174176 isOpenStreetMapBasemap ( element , provider , dataSource ) &&
175177 ! groupByLayerId . has ( id ) &&
176- id === layerOrder ( document , mapLayers ) [ 0 ]
178+ id === orderedIds [ 0 ]
177179 ) {
178180 project . basemapStyleUrl = DEFAULT_BASEMAP ;
179181 project . basemapVisible = visibilityByLayerId . get ( id ) ?? true ;
@@ -185,16 +187,15 @@ export function importQgisProject(
185187
186188 if ( isSupportedRasterLayer ( element , provider , source ) ) {
187189 const state = parseRasterState ( element ) ;
190+ const beforeId = nextLayerId ( id , orderedIds ) ;
188191 rasters . push ( {
189192 id,
190193 name,
191194 sourcePath : source ,
192195 visible : visibilityByLayerId . get ( id ) ?? true ,
193196 opacity : parseOpacity ( element ) ,
194197 ...( groupByLayerId . get ( id ) ? { groupId : groupByLayerId . get ( id ) } : { } ) ,
195- ...( nextLayerId ( id , layerOrder ( document , mapLayers ) )
196- ? { beforeId : nextLayerId ( id , layerOrder ( document , mapLayers ) ) }
197- : { } ) ,
198+ ...( beforeId ? { beforeId } : { } ) ,
198199 ...( state ? { state } : { } ) ,
199200 } ) ;
200201 continue ;
@@ -392,11 +393,21 @@ function zoomForBounds(west: number, south: number, east: number, north: number)
392393 return Math . max ( 0 , Math . min ( 20 , Math . log2 ( 360 / span ) - 0.75 ) ) ;
393394}
394395
395- function parseLayerGroups ( document : Document ) : {
396+ /**
397+ * The project's own layer tree: the `<layer-tree-group>` that is a direct child
398+ * of `<qgis>`. A Print Layout legend with `autoUpdateModel="0"` stores its own
399+ * frozen `<layer-tree-group>` snapshot under `<Layouts>`, so a document-wide
400+ * query would treat every layer those legends reference as another layer to
401+ * import and duplicate it once per legend (GeoLibre#1964).
402+ */
403+ function layerTreeRoot ( document : Document ) : Element | null {
404+ return document . documentElement . querySelector ( ":scope > layer-tree-group" ) ;
405+ }
406+
407+ function parseLayerGroups ( root : Element | null ) : {
396408 groups : LayerGroup [ ] ;
397409 ids : Map < Element , string > ;
398410} {
399- const root = document . querySelector ( "layer-tree-group" ) ;
400411 if ( ! root ) return { groups : [ ] , ids : new Map ( ) } ;
401412 const ids = new Map < Element , string > ( ) ;
402413 const groups = Array . from ( root . querySelectorAll ( "layer-tree-group" ) ) . map ( ( element , index ) => {
@@ -433,9 +444,12 @@ function groupDisplayName(element: Element, root: Element): string {
433444 return element . getAttribute ( "name" ) ?. trim ( ) || "Group" ;
434445}
435446
436- function layerGroupAssignments ( document : Document , ids : Map < Element , string > ) : Map < string , string > {
447+ function layerGroupAssignments (
448+ root : Element | null ,
449+ ids : Map < Element , string > ,
450+ ) : Map < string , string > {
437451 const assignments = new Map < string , string > ( ) ;
438- document . querySelectorAll ( "layer-tree-layer[id]" ) . forEach ( ( layer ) => {
452+ root ? .querySelectorAll ( "layer-tree-layer[id]" ) . forEach ( ( layer ) => {
439453 const layerId = layer . getAttribute ( "id" ) ;
440454 const parentGroup = layer . parentElement ?. closest ( "layer-tree-group" ) ;
441455 const groupId = parentGroup ? ids . get ( parentGroup ) : undefined ;
@@ -444,20 +458,21 @@ function layerGroupAssignments(document: Document, ids: Map<Element, string>): M
444458 return assignments ;
445459}
446460
447- function layerVisibility ( document : Document ) : Map < string , boolean > {
461+ function layerVisibility ( root : Element | null ) : Map < string , boolean > {
448462 const result = new Map < string , boolean > ( ) ;
449- document . querySelectorAll ( "layer-tree-layer[id]" ) . forEach ( ( element ) => {
463+ root ? .querySelectorAll ( "layer-tree-layer[id]" ) . forEach ( ( element ) => {
450464 const id = element . getAttribute ( "id" ) ;
451465 if ( id ) result . set ( id , element . getAttribute ( "checked" ) !== "Qt::Unchecked" ) ;
452466 } ) ;
453467 return result ;
454468}
455469
456- function layerOrder ( document : Document , mapLayers : Element [ ] ) : string [ ] {
457- const ids = Array . from ( document . querySelectorAll ( "layer-tree-layer[id]" ) )
470+ function layerOrder ( root : Element | null , mapLayers : Element [ ] ) : string [ ] {
471+ const ids = Array . from ( root ? .querySelectorAll ( "layer-tree-layer[id]" ) ?? [ ] )
458472 . map ( ( element ) => element . getAttribute ( "id" ) ?? "" )
459473 . filter ( Boolean ) ;
460- if ( ids . length > 0 ) return ids . reverse ( ) ;
474+ const unique = Array . from ( new Set ( ids ) ) ;
475+ if ( unique . length > 0 ) return unique . reverse ( ) ;
461476 return mapLayers
462477 . map ( ( element ) => text ( element . querySelector ( ":scope > id" ) ) )
463478 . filter ( Boolean )
0 commit comments