10 votos

¿Es posible cambiar los símbolos de la leyenda en la leyenda del compositor de la impresión QGIS

Tengo algunas capas con símbolos muy complejos definidos por los datos. Si utilizo una leyenda en el compositor de la impresión estos símbolos no se dibujan correctamente.

¿Hay alguna manera de cambiar los símbolos de la leyenda con pyqgis, para poder usar imágenes PNG o SVG personalizadas en lugar del símbolo de la leyenda por defecto para estas capas?

Sé cómo añadir un Pulsador al compositor de la impresión y cómo conectarlo con una función. Quiero añadir un botón a la Leyenda-Configuración que me permita reemplazar el icono de la leyenda generado automáticamente con una imagen personalizada. Así que lo que todavía necesito es la información de cómo puedo acceder a los símbolos de leyenda con pyqgis/pyqt, y cómo reemplazarlos con una imagen QImage en un QLabel o algo así?

Una maqueta muy básica del botón utilizado para cambiar el símbolo:

enter image description here

Leyenda generada automáticamente:

enter image description here

Leyenda con el símbolo de la leyenda personalizada:

enter image description here

Ya he averiguado cómo acceder a los elementos de la leyenda en el compositor de la impresión, pero aún no cómo acceder al símbolo en sí:

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()

Edición 1:

También he descubierto cómo acceder a los objetos QIcon del árbol de la leyenda, pero aún no puedo intercambiarlos:

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()

Aquí hay un ejemplo de la vida real en el que se puede ver una simbología combinada de muchas capas de símbolos: enter image description here Esto terminará en la leyenda de esta manera: enter image description here

Como necesito el símbolo adecuado en la leyenda me gustaría hacer una captura de pantalla de mi símbolo, recortarlo y usarlo como imagen en mi leyenda.

Sé que podría superponer una imagen separada sobre mi leyenda que cubra el símbolo generado automáticamente, pero me gustaría tener una solución "más limpia" que me permita reemplazar los símbolos de la leyenda con imágenes personalizadas.

Edición 2:

Mientras tanto, he encontrado otra forma de acceder a las entradas de la leyenda:

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)

Esto me permite acceder a los objetos de QgsLayerTreeLayer y puedo cambiar la leyenda Estilo (Grupo,Subgrupo,Oculto). Pero todavía no tengo idea de cómo acceder al símbolo de la leyenda. ¿Alguna idea?

2 votos

Cuando me encuentro con esto con QGIS, suelo crear capas adicionales que tienen la simbología que quiero mostrar en la leyenda (normalmente simples duplicados de capas existentes - no nuevas fuentes de datos). Luego, en el compositor configuro la ventana del mapa y bloqueo las capas. Una vez bloqueadas las capas, activo las capas "falsas" y puedo añadirlas a la leyenda. Es puramente una solución, y no en PyQGIS, pero tal vez hay alguna manera de emular las capas "falsas" para lo que usted necesita?

10voto

Geoffrey Puntos 228

Como este tema abarca muchos argumentos, sólo me centraré en las capas de símbolos SVG, esperando haber entendido bien lo que buscas (no me di cuenta de la longitud de la respuesta mientras la escribía, así que lo siento pero espero que aporte más claridad, eso sí).


Contexto

1) Clases de capas de símbolos

Las siguientes clases de capas de símbolos están disponibles para el formato SVG:

  • QgsSvgMarkerSymbolLayerV2 que muestra una geometría de puntos utilizando una imagen SVG especificada (funciona para punto capas);
  • QgsSVGFillSymbolLayer que dibuja el interior de una geometría poligonal utilizando una imagen SVG repetida (funciona para polígono capas).

Un enfoque común para crear una capa de símbolos es inicializarla con un diccionario de propiedades.

Puede inicializar una nueva capa de símbolos y ver sus propiedades por defecto de esta manera:

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

Obtendrá todas las propiedades que se almacenan en él:

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

Si desea editar las propiedades, puede utilizar métodos, que son invocables desde la ayuda de la clase (por ejemplo, ejecutar help(QgsSvgMarkerSymbolLayerV2) en la consola de Python). Más adelante verás un ejemplo de cómo usar los métodos.

En aras de la exhaustividad, también puede inicializar una capa de símbolos con un diccionario de propiedades (por ejemplo, véase aquí ), pero sinceramente prefiero el primer enfoque y lo utilizaré.

2) Creación de un renderizador

Para utilizar la capa de símbolos una vez creada (y eventualmente editada), es necesario crear un renderizador apropiado y luego asignar ese renderizador a su capa de mapa.

Para acceder al renderizador existente de una capa:

renderer = layer.rendererV2()

Para obtener una lista de los tipos de renderizadores disponibles, utilice:

renderer_types = QgsRendererV2Registry().renderersList()

Para su caso, deberíamos tratar con un Renderizador de símbolos categorizados . Como he dicho antes, hay que crear un renderizador y luego asignarlo a la capa:

# 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) Cambio de capa de símbolos

Los diversos símbolos de la Renderizador de símbolos categorizados se pueden llamar a través de symbols() (devuelve una lista):

for symb in renderer.symbols():
    print symb

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

Si desea reemplazar una capa de símbolos determinada dentro del symbol definida antes, sólo hay que conocer su índice y decírselo al renderizador de esta manera:

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

donde [0] indica el primer elemento del grupo categorizado.

Solución

Por último, ¡apliquemos lo que acabamos de aprender!

Suponiendo que se trabaje sobre esta capa de polígonos, que almacena los usos del suelo que hemos definido antes:

enter image description here

Si quiere cambiar el patrón por defecto de los usos del suelo agrícola (tienen la posición nº 1 en el grupo 'landuse') con una imagen SVG específica, puede ejecutar este código (leer aquí para saber cómo añadir una ruta SVG personalizada):

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()

Este será el resultado:

enter image description here

El código anterior es muy tosco pero, como no sabía si querías una solución específica para tu ejemplo o una explicación más general, he preferido centrar la atención en las herramientas disponibles en lugar de refinar el código en sí (¡seguro que puedes editarlo ligeramente según tus necesidades específicas!).

0 votos

Gracias por su extensa respuesta pero estoy buscando una solución para cambiar el símbolo (sólo) en la leyenda del compositor no para la capa en sí. Voy a actualizar mi pregunta anterior en un segundo con un ejemplo más

0 votos

@markgraeflerland Estoy seguro de que nadie pensó en la cuestión explicada en tu pregunta editada (prácticamente nueva). Nunca especificaste que la imagen de la leyenda no reprodujera lo que se ve en la capa (parecía que querías sustituir un elemento de la leyenda por una imagen, como he hecho yo). Sinceramente, en mi opinión, tu pregunta original era muy engañosa y mi post en realidad intentaba dar una respuesta a la misma. Siento la pérdida de tiempo, pero no voy a borrar la respuesta ya que podría ser de interés para alguien más cuando busque en Google. ¡Buena suerte con tu investigación!

2 votos

En la primera frase de mi pregunta escribí "Si utilizo una leyenda en el compositor de impresión estos símbolos no se dibujan correctamente" así que no creo que no haya especificado que la imagen de la leyenda no reproduzca lo que veo en la capa.

5voto

GuiOm Clair Puntos 58

Como alternativa, y sin necesidad de codificar en Python, lo he solucionado creando un nuevo grupo de capas dedicado a la creación de la leyenda, donde puedo poner lo que quiera, con los tamaños y colores que quiera. Así, en el compositor de la impresión, sólo he eliminado de los elementos de la leyenda las capas de datos reales y he mantenido sólo el grupo de capas de la leyenda.

Es especialmente práctico cuando se necesita hacer una leyenda que pueda representar casos que no se dan en el mapa real impreso.

EDIT : y sobre su segunda edición, con los símbolos compuestos no mostrar correctamente, ¿tiene algunas variables que definen sus símbolos, como la "C" o "G" es, de hecho, en relación con un campo, o la orientación? Si es el caso, QGIS no puede adivinar lo que usted quiere que se muestre, por lo que va a mostrar todo sin ningún valor para estos parámetros. Una solución puede ser guardar simbólicamente el símbolo con algunos valores fijos en lugar de las variables. Esta manera me permitió reemplazar este elemento de la leyenda que se muestra por defecto : enter image description here por esta que se ajusta a mi necesidad enter image description here

0voto

Mehul Mohan Puntos 166

Esta solución funciona cuando no tienes que componer muchos mapas porque cada vez tienes que configurar manualmente tu leyenda. Para mí, esto fue finalmente demasiado lento y propenso a errores.

Otra forma (bastante complicada pero más automatizada) de sustituir los iconos de la leyenda es ir a "propiedades de la capa / leyenda". Allí se marca "texto en los símbolos". A continuación, puede aplicar un SVG como imagen de fondo que se superpone al símbolo en la leyenda (cuando tiene el tamaño adecuado). Asegúrate de rellenar un espacio en "texto en símbolos", de lo contrario el SVG no se mostrará en absoluto. Es complicado pero a mí me funciona hasta que aparezca una solución adecuada en QGIS.

Lo que también es un gran inconveniente es que cuando el estilo de la capa se copia y se aplica a otra capa entonces las "propiedades de la leyenda" no se pasan por alguna razón.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X