Ist es möglich, Legenden-Symbole in der QGIS Print Composer-Legende zu ändern?


9

Ich habe einige Ebenen mit sehr komplexen datendefinierten Symbolen. Wenn ich im Print Composer eine Legende verwende, werden diese Symbole nicht richtig gezeichnet.

Gibt es eine Möglichkeit, die Legenden-Symbole mit Pyqgis zu ändern, sodass ich benutzerdefinierte PNG- oder SVG-Bilder anstelle des Standard-Legenden-Symbols für diese Ebenen verwenden kann?

Ich weiß, wie man dem Druckkomponisten einen Druckknopf hinzufügt und wie man ihn mit einer Funktion verbindet. Ich möchte den Legendeneinstellungen eine Schaltfläche hinzufügen, damit ich das automatisch generierte Legenden-Symbol durch ein benutzerdefiniertes Bild ersetzen kann. Was ich also noch brauche, ist die Information, wie ich mit pyqgis / pyqt auf die Legenden-Symbole zugreifen kann und wie ich sie durch ein QImage auf einem QLabel oder so etwas ersetzen kann?

Sehr einfaches Modell der Schaltfläche zum Ändern des Symbols:

Geben Sie hier die Bildbeschreibung ein

Automatisch generierte Legende:

Geben Sie hier die Bildbeschreibung ein

Legende mit benutzerdefiniertem Legenden-Symbol:

Geben Sie hier die Bildbeschreibung ein

Ich habe bereits herausgefunden, wie ich auf die Elemente der Legende im Druckkomponisten zugreifen kann, aber noch nicht, wie ich auf das Symbol selbst zugreifen kann:

import qgis
from PyQt4.QtCore import *
from PyQt4.QtGui import *

activeComposer = iface.activeComposers()

for item in activeComposer:
    if item.composerWindow().windowTitle()=='test':
        for i in item.items():
            if isinstance(i,QgsComposerLegend):
                #print i
                #print i.model()
                legend = i
                for i in xrange(legend.modelV2().rowCount()):
                    posteleg=legend.modelV2().index(i, 0)
                    print posteleg.data()

Bearbeiten 1:

Ich habe auch herausgefunden, wie man auf QIcon-Objekte des Legendenbaums zugreift, kann sie aber noch nicht austauschen:

def run(self):

        activeComposer = self.iface.activeComposers()
        #print(self.resolve('icon.png'))
        for item in activeComposer:
            if item.composerWindow().windowTitle()=='test':
                for i in item.items():
                    if isinstance(i,QgsComposerLegend):
                        legend = i

                        layerIcon = QIcon(os.path.join(os.path.dirname(__file__), "icon.png"))

                        for i in xrange(legend.modelV2().rowCount()):
                            posteleg=legend.modelV2().index(i, 0)
                            posteleg.model().iconGroup().swap(layerIcon)
                            print posteleg.data()

Hier ist ein Beispiel aus dem wirklichen Leben, in dem Sie eine Symbologie sehen können, die aus vielen Symbolebenen kombiniert ist: Geben Sie hier die Bildbeschreibung ein Dies endet in der Legende wie folgt: Geben Sie hier die Bildbeschreibung ein

Da ich das richtige Symbol in der Legende benötige, möchte ich einen Screenshot meines Symbols erstellen, es zuschneiden und als Bild in meiner Legende verwenden.

Ich weiß, dass ich einfach ein separates Bild über meine Legende legen könnte, das das automatisch generierte Symbol abdeckt, aber ich hätte gerne eine "sauberere" Lösung, mit der ich die Symbole in der Legende durch benutzerdefinierte Bilder ersetzen kann.

Bearbeiten 2:

In der Zwischenzeit habe ich einen anderen Weg gefunden, um auf die Legendeneinträge zuzugreifen:

from qgis.core import QgsLegendRenderer, QgsComposerLegendStyle

compDict = {}
for comp in iface.activeComposers():
    # workaround to get name: read it from window title
    compDict[comp.composerWindow().windowTitle()] = comp.composition()
if "mycomposername" in compDict:
    itemLlegend = compDict["mycomposername"].getComposerItemById("mylegend_id")
    if itemLlegend:
        print itemLlegend

tree_layer_layer =  itemLlegend.modelV2().rootGroup().children()
for item in tree_layer_layer:
        if item.layerName()=="MyLayername":
            print "match"
            QgsLegendRenderer.setNodeLegendStyle(item, QgsComposerLegendStyle.Group)

Dadurch kann ich auf die QgsLayerTreeLayer-Objekte zugreifen und den Legendenstil (Gruppe, Untergruppe, Versteckt) wechseln. Aber ich habe immer noch keine Ahnung, wie ich auf das Legenden-Symbol zugreifen soll. Irgendwelche Ideen?


1
Wenn ich mit QGIS darauf stoße, erstelle ich normalerweise zusätzliche Ebenen mit der Symbologie, die ich in der Legende anzeigen möchte (normalerweise einfache Duplikate vorhandener Ebenen - keine neuen Datenquellen). Dann habe ich im Komponisten das Kartenfenster eingerichtet und die Ebenen gesperrt. Nachdem die Ebenen gesperrt sind, schalte ich die "falschen" Ebenen ein und kann sie einer Legende hinzufügen. Es ist nur eine Problemumgehung und nicht in PyQGIS, aber vielleicht gibt es eine Möglichkeit, die "gefälschten" Ebenen für das zu emulieren, was Sie benötigen?
Nate Wanner

Antworten:


10

Da dieses Thema viele Argumente abdeckt, werde ich mich nur auf die SVG-Symbolebenen konzentrieren, in der Hoffnung, dass ich gut verstanden habe, wonach Sie suchen (ich habe die Länge der Antwort beim Schreiben nicht erkannt, daher tut es mir leid das aber ich hoffe es wird mehr Klarheit hinzufügen).


Kontext

1) Symbolebenenklassen

Die folgenden Symbolebenenklassen sind für das SVG-Format verfügbar:

Ein gängiger Ansatz zum Erstellen einer Symbolebene besteht darin, sie mit einem Wörterbuch mit Eigenschaften zu initialisieren.

Sie können eine neue Symbolebene initialisieren und ihre Standardeigenschaften auf folgende Weise anzeigen:

symbol_layer = QgsSvgMarkerSymbolLayerV2()
for k,v in symbol_layer.properties().iteritems():
    print k, ':', v

Sie erhalten alle darin gespeicherten Eigenschaften:

outline_width : 0.2
outline_color : 0,0,0,255
angle : 0
name : crosses/Star1.svg
scale_method : diameter
color : 0,0,0,255
size_unit : MM
horizontal_anchor_point : 1
size_map_unit_scale : 0,0,0,0,0,0
outline_width_unit : MM
offset : 0,0
offset_map_unit_scale : 0,0,0,0,0,0
outline_width_map_unit_scale : 0,0,0,0,0,0
size : 4
vertical_anchor_point : 1
offset_unit : MM

Wenn Sie die Eigenschaften bearbeiten möchten, können Sie Methoden verwenden, die über die Hilfe der Klasse aufgerufen werden können (z. B. help(QgsSvgMarkerSymbolLayerV2)in der Python-Konsole ausführen ). Sie werden später ein Beispiel für die Verwendung von Methoden sehen.

Der Vollständigkeit halber können Sie auch eine Symbolebene mit einem Wörterbuch von Eigenschaften initialisieren (siehe hier ), aber ich bevorzuge aufrichtig den ersten Ansatz und werde ihn verwenden.

2) Erstellen eines Renderers

Um die Symbolebene zu verwenden, nachdem Sie sie erstellt (und schließlich bearbeitet) haben, müssen Sie einen geeigneten Renderer erstellen und diesen Renderer dann Ihrer Kartenebene zuweisen.

So greifen Sie auf den vorhandenen Renderer einer Ebene zu:

renderer = layer.rendererV2()

Verwenden Sie Folgendes, um eine Liste der verfügbaren Renderertypen abzurufen:

renderer_types = QgsRendererV2Registry().renderersList()

Für Ihren Fall sollten wir uns mit einem Categorized Symbol Renderer befassen . Wie ich bereits sagte, müssen Sie einen Renderer erstellen und ihn dann der Ebene zuweisen:

# define the lookup: value -> (color, label)
landuses = {'Agriculture': ('#d3a151', 'Agriculture'), 'Natural': ('#175dcd', 'Natural'),}

# create a category for each item in landuses
categories = []
for landuse_name, (color, label) in landuses.items():
    symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
    symbol.setColor(QColor(color))
    category = QgsRendererCategoryV2(landuse_name, symbol, label)
    categories.append(category)

# create the renderer and assign it to the layer
expression = 'landuse' # field name
renderer = QgsCategorizedSymbolRendererV2(expression, categories) # categorized symbol renderer
layer.setRendererV2(renderer) # assign the renderer to the layer

3) Ändern der Symbolebene

Die verschiedenen Symbole des Categorized Symbol Renderers können über aufgerufen werden symbols()(es wird eine Liste zurückgegeben):

for symb in renderer.symbols():
    print symb

<qgis._core.QgsMarkerSymbolV2 object at 0x0E1FF760>
<qgis._core.QgsMarkerSymbolV2 object at 0x0E1FF7B0>

Wenn Sie eine bestimmte Symbolebene innerhalb der symbolzuvor definierten Ebene ersetzen möchten , müssen Sie nur ihren Index kennen und ihn dann dem Renderer auf folgende Weise mitteilen:

renderer.symbols()[0].changeSymbolLayer(0, new_symbol)

Dabei [0]gibt es das erste Element der kategorisierten Gruppe an.

Lösung

Lassen Sie uns zum Schluss das anwenden, was wir gerade gelernt haben!

Angenommen, Sie arbeiten an dieser Polygonebene, in der die zuvor definierten Landnutzungen gespeichert sind:

Geben Sie hier die Bildbeschreibung ein

Wenn Sie das Standardmuster für die Nutzung landwirtschaftlicher Flächen (sie haben die Position Nr. 1 in der Gruppe "Landnutzung") mit einem bestimmten SVG-Bild ändern möchten, können Sie diesen Code ausführen (lesen Sie hier, um zu erfahren, wie Sie eine benutzerdefinierte SVG hinzufügen Pfad):

import qgis
from PyQt4.QtCore import *
from PyQt4.QtGui import *

# define the lookup: value : (color, label)
landuses = {'Agriculture': ('#d3a151', 'Agriculture'), 'Natural': ('#175dcd', 'Natural'),}

# create a category for each item in landuses
categories = []
for landuse_name, (color, label) in landuses.items():
    symbol = QgsSymbolV2.defaultSymbol(layer.geometryType())
    symbol.setColor(QColor(color))
    category = QgsRendererCategoryV2(landuse_name, symbol, label)
    categories.append(category)

# create the renderer and assign it to the layer
expression = 'landuse' # field name
renderer = QgsCategorizedSymbolRendererV2(expression, categories)

activeComposer = iface.activeComposers()
for item in activeComposer:
    if item.composerWindow().windowTitle()=='test':
        for i in item.items():
            if isinstance(i,QgsComposerLegend):
                legend = i
                for k in xrange(legend.modelV2().rowCount()):
                    posteleg=legend.modelV2().index(k, 0)
                    layer = QgsMapLayerRegistry.instance().mapLayersByName( posteleg.data() )[0]
                    if k == 0: # k is the position of the layer of interest in Legend (NOT in the Layers Panel)
                        svg_location = 'C:/path_to_svg/smile.svg'
                        new_symbol = QgsSVGFillSymbolLayer()
                        new_symbol.setSvgFilePath(svg_location)
                        new_symbol.setPatternWidth(7.0)
                        #... If you want to set additional parameters, type help(QgsSVGFillSymbolLayer) in the Python Console
                        renderer.symbols()[1].changeSymbolLayer(0, new_symbol)
                    layer.setRendererV2(renderer)
                    layer.triggerRepaint()

Dies wird das Ergebnis sein:

Geben Sie hier die Bildbeschreibung ein

Der obige Code ist sehr grob, aber da ich nicht wusste, ob Sie eine bestimmte Lösung für Ihr Beispiel oder eine allgemeinere Erklärung wünschen, habe ich es vorgezogen, die Aufmerksamkeit auf die verfügbaren Tools zu richten, anstatt den Code selbst zu verfeinern (da bin ich mir sicher Sie können es leicht nach Ihren spezifischen Bedürfnissen bearbeiten!).


Vielen Dank für Ihre ausführliche Antwort, aber ich suche nach einer Lösung, um das Symbol (nur) in der Komponistenlegende nicht für die Ebene selbst zu ändern. Ich werde meine Frage oben in einer Sekunde mit einem weiteren Beispiel
aktualisieren

@markgraeflerland Ich bin sicher, niemand hat über das Problem nachgedacht, das in Ihrer (praktisch neuen) bearbeiteten Frage erklärt wurde. Sie haben nie angegeben, dass das Bild in der Legende nicht das wiedergibt, was Sie in der Ebene sehen (anscheinend wollten Sie ein Element aus der Legende durch ein Bild ersetzen, wie ich es getan habe). Mit freundlichen Grüßen, meiner Meinung nach war Ihre ursprüngliche Frage sehr irreführend und mein Beitrag hat tatsächlich versucht, eine Antwort darauf zu geben. Es tut mir leid für die verschwendete Zeit, aber ich werde die Antwort nicht löschen, da sie beim googeln für jemand anderen von Interesse sein könnte. Viel Glück bei Ihrer Recherche!
Mgri

2
Im ersten Satz meiner Frage schrieb ich: "Wenn ich im Druckkomponisten eine Legende verwende, werden diese Symbole nicht richtig gezeichnet." Ich glaube also nicht, dass ich nicht angegeben habe, dass das Bild in der Legende nicht das wiedergibt, was ich habe siehe in der Schicht.
Markgraeflerland

5

Alternativ und ohne Python-Codierung habe ich dieses Problem gelöst, indem ich eine neue Ebenengruppe für die Legendenerstellung erstellt habe, in der ich alles, was ich möchte, mit den gewünschten Größen und Farben platzieren kann. So habe ich im Print Composer gerade die tatsächlichen Datenebenen aus den Legendenelementen gelöscht und nur die Legendenebenengruppe beibehalten.

Es ist besonders praktisch, wenn eine Legende erstellt werden muss, die Fälle darstellen kann, die in der tatsächlich gedruckten Karte nicht vorkommen.

BEARBEITEN: und bei Ihrer zweiten Bearbeitung, bei der die zusammengesetzten Symbole nicht richtig angezeigt werden, haben Sie einige Variablen, die Ihre Symbole definieren, z. B. das "C" oder "G" ist tatsächlich relativ zu einem Feld oder der Ausrichtung? Wenn dies der Fall ist, kann QGIS nicht erraten, was angezeigt werden soll. Daher wird alles ohne Wert für diese Parameter angezeigt. Eine Problemumgehung kann darin bestehen, das Symbol mit einigen festen Werten anstelle der Variablen zu speichern. Auf diese Weise konnte ich dieses Standardelement für die Anzeige von Legenden ersetzen: Geben Sie hier die Bildbeschreibung eindurch dieses Element , das meinen Anforderungen entsprichtGeben Sie hier die Bildbeschreibung ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.