É possível alterar os símbolos das legendas na legenda do compositor de impressão QGIS?

9

Eu tenho algumas camadas com símbolos definidos por dados muito complexos. Se eu usar uma legenda no compositor de impressão, esses símbolos não serão desenhados corretamente.

Existe uma maneira de alterar os símbolos de legenda com pyqgis, para que eu possa usar imagens PNG ou SVG personalizadas em vez do símbolo de legenda padrão para essas camadas?

Sei como adicionar um botão ao compositor de impressão e como conectá-lo a uma função. Desejo adicionar um botão às Configurações da legenda para permitir a substituição do ícone da legenda gerada automaticamente por uma imagem personalizada. Então, o que eu ainda preciso são as informações de como posso acessar os símbolos de legenda com pyqgis / pyqt e como substituí-los por uma QImage em um QLabel ou algo assim?

Maquete muito básica do botão usado para alterar o símbolo:

insira a descrição da imagem aqui

Legenda gerada automaticamente:

insira a descrição da imagem aqui

Legenda com o símbolo de legenda personalizada:

insira a descrição da imagem aqui

Já descobri como acessar os itens da legenda no compositor de impressão, mas ainda não como acessar o próprio símbolo:

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

Editar 1:

Eu também descobri como acessar objetos QIcon da árvore de legendas, mas ainda não posso trocá-los:

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

Aqui está um exemplo da vida real em que você pode ver uma simbologia combinada de várias camadas de símbolos: insira a descrição da imagem aqui Isso terminará na legenda assim: insira a descrição da imagem aqui

Como preciso do símbolo apropriado na legenda, gostaria de fazer uma captura de tela do meu símbolo, recortá-lo e usá-lo como uma imagem na legenda.

Sei que poderia sobrepor uma imagem separada em cima da minha legenda, que cubra o símbolo gerado automaticamente, mas gostaria de ter uma solução "mais limpa" que me permita substituir os símbolos na legenda por imagens personalizadas.

Edição 2:

Enquanto isso, eu encontrei outra maneira de obter acesso às entradas da legenda:

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)

Isso me permite acessar os objetos QgsLayerTreeLayer e posso alternar a legenda Estilo (Grupo, Subgrupo, Oculto). Mas ainda não tenho ideia de como acessar o símbolo da legenda. Alguma ideia?

markgraeflerland
fonte
1
Quando me deparei com isso com o QGIS, normalmente crio camadas adicionais que possuem a simbologia que quero mostrar na legenda (geralmente duplicatas simples de camadas existentes - não novas fontes de dados). Depois, no compositor, montei a janela do mapa e bloqueio as camadas. Depois que as camadas são bloqueadas, ligo as camadas "falsas" e posso adicioná-las a uma legenda. É puramente uma solução alternativa, e não no PyQGIS, mas talvez haja alguma maneira de emular as camadas "falsas" para o que você precisa?
Nate Wanner

Respostas:

10

Como este tópico aborda muitos argumentos, vou me concentrar apenas nas camadas de símbolos SVG, na esperança de entender bem o que você está procurando (não percebi a duração da resposta enquanto escrevia, então, desculpe-me por mas espero que isso adicione mais clareza).


Contexto

1) Classes da camada de símbolo

As seguintes classes de camada de símbolo estão disponíveis para o formato SVG:

  • QgsSvgMarkerSymbolLayerV2 , que exibe uma geometria de ponto usando uma imagem SVG especificada (funciona para camadas de ponto );
  • QgsSVGFillSymbolLayer , que desenha o interior de uma geometria de polígono usando uma imagem SVG repetida (funciona para camadas de polígono ).

Uma abordagem comum para criar uma camada de símbolo é inicializá-la com um dicionário de propriedades.

Você pode inicializar uma nova camada de símbolo e ver suas propriedades padrão desta maneira:

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

Você obterá todas as propriedades armazenadas nele:

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

Se você deseja editar as propriedades, poderá usar métodos que podem ser chamados da ajuda da classe (por exemplo, executados help(QgsSvgMarkerSymbolLayerV2)no Python Console). Você verá mais adiante um exemplo de como usar métodos.

Por uma questão de integridade, você também pode inicializar uma camada de símbolo com um dicionário de propriedades (por exemplo, veja aqui ), mas eu sinceramente prefiro a primeira abordagem e a utilizarei.

2) Criando um renderizador

Para usar a camada de símbolo depois de criá-la (e eventualmente editá-la), você precisa criar um representante apropriado e depois atribuí-lo à sua camada de mapa.

Para acessar o renderizador existente de uma camada:

renderer = layer.rendererV2()

Para obter uma lista dos tipos de renderizadores disponíveis, use:

renderer_types = QgsRendererV2Registry().renderersList()

Para o seu caso, devemos lidar com um representante de símbolo categorizado . Como eu disse antes, você precisa criar um renderizador e atribuí-lo à camada:

# 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) Mudando a camada do símbolo

Os vários símbolos do representante de símbolos categorizados podem ser chamados symbols()(ele retorna uma lista):

for symb in renderer.symbols():
    print symb

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

Se você deseja substituir uma determinada camada de símbolo dentro da symboldefinida anteriormente, você só precisa conhecer seu índice e, em seguida, informar ao renderizador da seguinte maneira:

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

onde [0]indica o primeiro item do grupo categorizado.

Solução

Finalmente, vamos aplicar o que acabamos de aprender!

Assumindo trabalhar nesta camada de polígono, que armazena os usos da terra que definimos anteriormente:

insira a descrição da imagem aqui

Se você deseja alterar o padrão padrão para os usos da terra agrícola (eles têm a posição número 1 no grupo 'uso da terra') com uma imagem SVG específica, você pode executar este código (leia aqui para saber como adicionar um SVG personalizado caminho):

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á o resultado:

insira a descrição da imagem aqui

O código acima é muito difícil, mas como eu não sabia se você queria uma solução específica para o seu exemplo ou uma explicação mais geral, preferi concentrar a atenção nas ferramentas disponíveis em vez de refinar o próprio código (tenho certeza você pode editá-lo levemente de acordo com suas necessidades específicas!).

mgri
fonte
Obrigado pela sua resposta extensa, mas estou procurando uma solução para alterar o símbolo (somente) na legenda do compositor, não para a própria camada. Atualizarei minha pergunta acima em um segundo, com mais um exemplo
markgraeflerland 27/02
@markgraeflerland Tenho certeza de que ninguém pensou no problema explicado na sua pergunta (praticamente nova) editada. Você nunca especificou que a imagem na legenda não reproduzirá o que você vê na camada (parecia que você queria substituir um item da legenda por uma imagem, como eu fiz). Atenciosamente, na minha opinião, sua pergunta original foi muito enganosa e meu post realmente tentou responder. Sinto muito pelo tempo perdido, mas não excluirei a resposta, pois ela pode ser do interesse de outra pessoa ao pesquisar no Google. Boa sorte com sua pesquisa!
MGRI
2
Na primeira frase da minha pergunta, escrevi "Se eu usar uma legenda no compositor de impressão, esses símbolos não serão desenhados corretamente", então não acho que não tenha especificado que a imagem na legenda não reproduzirá o que veja na camada.
Markgraeflerland
5

Como alternativa, e sem a codificação Python, resolvi isso criando um novo grupo de camadas dedicado à criação da legenda, onde posso colocar o que quiser, com os tamanhos e cores que desejar. Assim no compositor de impressão, acabei de excluir dos itens de legenda as camadas de dados reais e mantive apenas o grupo de camadas de legenda.

É especificamente prático quando é necessário criar uma legenda que possa representar casos que não estão acontecendo no mapa impresso real.

EDIT: e na sua segunda edição, com os símbolos compostos não exibidos corretamente, você tem algumas variáveis ​​que definem seus símbolos, como o "C" ou "G" é de fato relativo a um campo ou à orientação? Se for esse o caso, o QGIS não pode adivinhar o que você deseja exibir, exibindo tudo sem nenhum valor para esses parâmetros. Uma solução alternativa pode ser salvar simplesmente o símbolo com alguns valores fixos no lugar das variáveis. Dessa maneira, eu substituí esse item de legenda padrão: insira a descrição da imagem aquipor este, atendendo minha necessidadeinsira a descrição da imagem aqui

GuiOm Clair
fonte