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.

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.
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:

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:

Come sfruttare tutto questo
Supponiamo che un amico ci passi uno shapefile con tabella attributi cosi fatta:
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:

- incollo, in un nuovo file di testo (per esempio usando NotePad++), il contenuto della simbologia appena copiata e salvo come tema.qml;
- tramite la tecnica del precedente blog post, estrapolo il file TSV (out_regioni_xpath.tsv) dal file tema.qml precedentemente salvato:
- esporto il layer (lo shapefile dell’amico) in formato CSV (layer.csv);
- 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;
- 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:
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:
- T’ansignari e T’appeddiri spazio creato da Open Data Sicilia;
- QGIS.org;
- Estrarre dati da un file XML;
- Estrarre dati da un file XML con Google sheet (XPATH);
- Script pyQGIS;
- Miller;
- XmlStarlet;
- XPATH.
- Sotto sistema Bash su Win 10
Ringraziamenti:
- Ivano Giuliano – per lo spunto sul tema
- Andrea Borruso – by default
- Giovanni Pirrotta – per aver partecipato a T’ansignari e T’appeddiri
- Giovan Battista Vitrano – by default