Software • 19. Januar 2023

Verknüpfen und automatisches Ausklappen von Layergruppen

In QGIS-Projekten mit vielen Layern kann schnell die Übersichtlichkeit verloren gehen: Wenn dann etwa zwei an unterschiedlichen Positionen im Ebenenbaum liegende Layer miteinander verglichen werden sollen, kann es bis zur Auswahl per Mausklick länger dauern. Typische Fehler wie die Auswahl eines falschen Layers schleichen sich in diesem Prozess besonders gerne ein.

Im Projekt für die Berliner Verkehrsbetriebe (BVG) werden regelmäßig Änderungen im Streckennetz abgerufen, welche dann mit den Ursprungsdaten verglichen und dort geändert werden müssen. Um dieses Vergleichen zu vereinfachen, wurde ein Python-Makro entwickelt, welches Paare von Layergruppen automatisch miteinander verknüpft. Dabei wird beim Einschalten des einen Partners der andere automatisch ausgeklappt sowie alle übrigen Gruppen eingeklappt.

Beispielsweise wird so beim Aktivieren der Gruppe Änderungen > Änderungen Bus-Linien der restliche Ebenenbaum zugeklappt und anschließend ausschließlich die passende Kartenlayergruppe Linien ÖPNV > Bus-Linien ausgeklappt. Das erspart dem/der Sachbearbeiter*in das manuelle Suchen des entsprechenden Kartenlayers und soll dem Editieren in falschen Layern vorbeugen.

Umgesetzt wird dies mittels Python-Code, welcher automatisch beim Start des Projekts ausgeführt wird. Diese automatische Ausführung gelingt über Makros, die sich im Projekt einstellen lassen. Dazu wird die geschriebene Python-Datei importiert und die in ihr definierte Funktion (hier "main") aufgerufen, wenn das Projekt geöffnet wird.

Aufbau und Funktion des Python-Codes "expandLayer"

Zunächst müssen die Gruppen definiert werden, welche automatisch geschlossen werden sollen (im Beispiel: alle außer der Änderungs-Layergruppe). Das funktioniert über den Namen der Gruppe und die Methode findGroup der layerTreeRoot. Außerdem müssen die zu verknüpfenden Layergruppen als Paare definiert werden. Dies geschieht in Form eines Dictionaries.

In der main - Funktion wird durch den Dictionary iteriert. Für jedes Paar wird jeweils ein Knoten-Objekt (layerTreeNode) definiert, welches die entsprechende Gruppe im Ebenenbaum darstellt. Mit dem Objekt aus der Änderungs-Layergruppe wird nun über das Signal VisibilityChanged, also der Aus- oder Abwahl der Gruppe, eine Funktion verknüpft, welche automatisch den definierten Partner aus der Kartenebene sichtbar macht und alle anderen Gruppen einklappt.

def main():
    for change, map in pairs.items():
        mNode = root.findGroup(map)
        chNode = root.findGroup(change)
        chNode.visibilityChanged.connect(partial(expandTree, mNode))

Die zweite notwendige Funktion heißt expandTree und bekommt als Parameter entsprechend den Layer-Partner als layerTreeNode übergeben. In dieser werden zunächst alle Layergruppen (inklusive aller Untergruppen) bis auf den Änderungslayer zugeklappt. Dazu wird jeweils über alle Kinder der Hauptgruppe iteriert, die folglich mit der Methode setExpanded(False)) eingeklappt werden.

def expandTree (mapNode):
    for child in mainGroup1.findGroups(recursive = True):
        child.setExpanded(False)
    mainGroup1.setExpanded(False)

Anschließend muss nun der übergebene layerTreeNode via (setExpanded(True)) ausgeklappt werden. Ebenso müssen aber auch alle zugehörigen Übergruppen (Eltern, engl. „parents“) wieder ausgeklappt werden. Da das Finden aller parents bislang nicht als eigenständige Funktion implementiert ist, wurde diese daraufhin als findParentsRecursive erstellt. Diese Funktion bekommt einen layerTreeNode übergeben und bildet solange die zugehörigen parents (Methode parent), bis das entsprechende Objekt vom Typ QgsLayerTree erreicht ist und somit die oberste Ebene gefunden wurde.

def findParentsRecursive(node):
    while type(node) != QgsLayerTree:
        node = node.parent()
        yield node

In der expandTree-Funktion kann diese nun aufgerufen werden. Durch eine Iteration über alle Einträge können somit alle übergeordneten Gruppen ausgeklappt werden.

   # expand map node group
    mapNode.setExpanded(True)
    # expand all parents as well
    for parent in findParentsRecursive(mapNode):
        parent.setExpanded(True)

Damit ist das Skript fertig und kann in das Projekt eingebunden werden, wobei darauf zu achten ist, dass dieses im gleichen Ordner wie die QGIS-Projektdatei liegen muss. Weiterhin können in den Einstellungen Python-Makros automatisch aktiviert werden, damit dem/der Bearbeiter*in nicht jedes Mal eine Warnmeldung angezeigt wird.

Abschließend sollte noch darauf geachtet werden, dass alle Gruppenpaare, die im Dictionary definiert werden, einen eindeutigen Namen haben (da dieser nicht doppelt vorkommen darf). Zudem sollten alle im Dictionary definierten Gruppenpaare im Projekt anschließend nicht mehr umbenannt werden, da eine Umbenennung sonst wieder eine entsprechende Anpassung im Python-Skript notwendig macht.

Wir hoffen, dass mit diesem Python-Makro viel Arbeitszeit eingespart werden kann und Fehlerquellen vermieden werden können. Herzlichen Dank an das BVG-Team für die tolle Zusammenarbeit!

Weitere Beiträge, die Dich interessieren könnten:

Annika Schmidt

Während ihres Studiums der Geophysik und Geoinformatik an der TU Bergakademie Freiberg lernte Annika Schmidt den Umgang mit vielfältigen Geodaten kennen. Seit 2022 ist sie bei der WhereGroup am Standort Berlin als GIS-Expertin angestellt, wo sie an umfangreichen GIS-Projekten für verschiedene Kunden arbeitet.

Artikel teilen: