QGIS (ri)creare la legenda a partire dal colore rgba presente nella tabella attributi

Nel precedente articolo (qui) abbiamo affrontato il caso “come estrarre i codici rgba dello stile categorizzato di un layer e popolare la relativa tabella attributi“; in questo blog post ci occuperemo del processo inverso: creare la legenda a partire dai codici rgba presenti nella tabella attributi.

Screenshot QGIS 3.4 Madeira

Attualmente (QGIS 3.4) non è possibile pilotare i colori dei simboli della legenda usando espressioni o altri trucchi rimanendo all’interno di QGIS (e senza usare pyQGIS), cioè i colori visibili nella map canvas (quelli della geometria) sono pilotabili ma non la relativa legenda quindi si rischia di avere una map canvas non allineata con i colori della legenda. Il motivo è legato al fatto che i colori vengono scelti – da QGIS – in modo random e non esiste (attualmente) modo per disattivare questo.

QGIS – stile layer

Unico modo per poter ricreare la legenda, partendo da valori rgba noti, è attraverso l’uso del file QML (è un file XML utilizzato da QGIS per la tematizzazione).

Come modificare un file XML/QML

In informaticaXML (sigla di eXtensible Markup Language) è un metalinguaggio per la definizione di linguaggi di markup, ovvero un linguaggio marcatore basato su un meccanismo sintattico che consente di definire e controllare il significato degli elementi contenuti in un documento o in un testo. [WikiPedia]

In pratica è qualcosa che non è a mia portata e quindi ho aperto issue nello spazio (che curo anche io) T’ansignari e T’appeddiri di #OpenDataSicilia.

Le soluzioni proposte sono due, una tramite Python e l’altra tramite utility da riga di comando XMLSTARLET :

in entrambi i casi (per maggiori dettagli leggeri qui) il concetto di base è quello di intercettare il valore per cui si è tematizzato (name) e sostituire il nuovo valore del codice rgba corrispondente:

file XML/QML

NB: la struttura del file QML è complessa e occorre sottolineare che gli attributi, secondo cui si tematizza il layer, vengono memorizzati in due modi diversi: come valore dell’attributo e come valori interi da 0 a n; questo valore intero viene memorizzato nel tag name:

file QML

Come sfruttare tutto questo

Supponiamo che un amico ci passi uno shapefile con tabella attributi cosi fatta:

Tabella attributi con codici rgba nel campo ‘COLOR’

e ci comunica che ha tematizzato (metodo categorizzato) utilizzando il campo “DEN_REG” e i colori usati si trovano nella colonna “COLOR” (io penso, poteva allegarmi anche il file qml e facevo prima; ma l’amico non ha creato oppure ha cancellato il file QML e non sa più come ricrearlo).

Importo lo shapefile in QGIS e tematizzo (metodo categorizzato) usando il campo “DEN_REG” (lo stesso dell’amico sbadato) ottenendo, come QGIS vuole, una legenda diversa dall’originale in quanto i colori sono generati in modo random.

Dopo aver tematizzato, copio la sola simbologia:

copio la simbologia
  1. incollo, in un nuovo file di testo (per esempio usando NotePad++), il contenuto della simbologia appena copiata e salvo come tema.qml;
  2. tramite la tecnica del precedente blog post, estrapolo il file TSV (out_regioni_xpath.tsv) dal file tema.qml precedentemente salvato:
  3. esporto il layer (lo shapefile dell’amico) in formato CSV (layer.csv);
  4. metto in JOIN il file CSV precedente (layer.csv) con quello del punto 2 (out_regioni_xpath.tsv) per ottenere la corrispondenza tra nome regione (“DEN_REG”) e valori rgba, ottenendo il file out.tsv;
  5. utilizzo il file (out.tsv ), risultante dal punto precedente, come input per uno dei due metodi precedentemente illustrati per modificare un file XML.

È più complicato spiegarlo che a realizzarlo: quello che in realtà serve sono solo i due file (tema.qml e layer.csv , poi basta lanciare lo script di sotto:

#!/bin/bash
set -x

<tema.qml xmlstarlet fo -D | xmlstarlet sel -T -t -m "//symbols/symbol" -v $'concat(@name,"\t",layer/prop[@k="color"]/@v)' -n >./idColori.tsv
<tema.qml xmlstarlet fo -D | xmlstarlet sel -T -t -m "//category" -v $'concat(@symbol,"\t",@value)' -n >./idRegioni.tsv
mlr --tsv --implicit-csv-header --headerless-csv-output  join  -j 1 --lp colori --rp regioni -f idColori.tsv idRegioni.tsv >./out_regioni_xpath.tsv
mlr --icsv --onidx --ofs "\t" cat layer.csv >layer_t.tsv
mlr --nidx --fs "\t" join -j 3 -r 1 --rp r -f out_regioni_xpath.tsv then cut -f 1,r2 layer_t.tsv >out.tsv

rm idColori.tsv
rm idRegioni.tsv
rm layer_t.tsv

il file di output dello script di sopra è out.tsv che sarà il file di input per il successivo script che sovrascriverà il file tema.qml:

soluzione tramite utility xmlstarlet:

#!/bin/bash
set -x

while IFS=$'\t' read -r col1 col2
do
    xmlstarlet ed --inplace -u '//symbols/symbol[@name='"$col1"']/layer/prop[@k="color"]/@v' -v "$col2" tema.qml
done < out.tsv

soluzione tramite Python (NB: occorre trasformare il file da out.tsv in out.csv):

import csv

myDict = {}

with open('out.csv', 'r') as dictfile:
    csvreader = csv.reader(dictfile, delimiter=',')
    for row in csvreader:
        myDict[row[0]] = row[1]

from bs4 import BeautifulSoup

infile = open('tema.qml','r')
contents = infile.read()
soup = BeautifulSoup(contents,'xml')
symbols = soup.select('symbol')
for symbol in symbols:
    if symbol['name'] in myDict:
        prop = symbol.find('prop', {'k' : 'color'})
        prop['v'] = myDict[symbol['name']]

xml = soup.prettify('utf-8')
with open('tema.qml', 'wb') as file:
    file.write(xml)

Riassumendo: creare il file tema.qml e il file layer.csv e lanciare il seguente script che effettuerà tutto quello descritto sopra e sovrascriverà il file tema.qml con i valori rgba da noi scelti, segue unico script:

#!/bin/bash
#set -x
# estraggo i dati dal file qml
<tema.qml xmlstarlet fo -D | xmlstarlet sel -T -t -m "//symbols/symbol" -v $'concat(@name,"\t",layer/prop[@k="color"]/@v)' -n >./idColori.tsv
<tema.qml xmlstarlet fo -D | xmlstarlet sel -T -t -m "//category" -v $'concat(@symbol,"\t",@value)' -n >./idRegioni.tsv
# metto in JOIN le due tabelle estratte
mlr –tsv –implicit-csv-header –headerless-csv-output join -j 1 –lp colori –rp regioni -f idColori.tsv idRegioni.tsv >./out_regioni_xpath.tsv
# trasformo il file da csv a tsv
mlr –icsv –onidx –ofs "\t" cat layer.csv >layer_t.tsv
# metto in JOIN la tabella precedente con il file tsv del layer
mlr –nidx –fs "\t" join -j 3 -r 1 –rp r -f out_regioni_xpath.tsv then cut -f 1,r2 layer_t.tsv >out.tsv
# sostituisco i valori rgba con quelli che mi interessano tramite file tsv del layer in JOIN
while IFS=$'\t' read -r col1 col2
do
xmlstarlet ed –inplace -u '//symbols/symbol[@name='"$col1"']/layer/prop[@k="color"]/@v' -v "$col2" tema.qml
done < out.tsv
# rimuovo i file che non mi servono piu'
rm idColori.tsv
rm idRegioni.tsv
rm out_regioni_xpath.tsv
rm layer_t.tsv
rm out.tsv
view raw script_unico.sh hosted with ❤ by GitHub

NOTE FINALI: Nonostante QGIS abbia la possibilità di modificare i colori dei simboli delle geometria attraverso delle espressioni e delle funzione colori, non è attualmente possibile intervenire sui colori della legenda, questi sono sempre generati in modo random.

Dati per chi volesse provare

Riferimenti:

Ringraziamenti:

Pubblicità

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo di WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione /  Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione /  Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione /  Modifica )

Connessione a %s...

Questo sito utilizza Akismet per ridurre lo spam. Scopri come vengono elaborati i dati derivati dai commenti.