QGIS: selezionare geometrie e visualizzarle su mappa, da una tabella di attributi correlata

Come farlo con una “Azione” (e con una “Macro”) in Python

Chi lavora con i GIS gestisce set di dati. È comune che alcuni di questi non contengano dati cartografici, ma che siano comunque in relazione con layer spaziali.

Ad esempio il layer dei punti degli idranti di una città, in relazione (1:1, 1:N, ecc.) con un tabella che ne contiene alcune informazioni anagrafiche. In QGIS posso impostare il tutto definendo tra i due layer un JOIN, sfruttando la classica colonna id presente in entrambi. Fatto questo però non è possibile selezionare le geometrie a partire dalla tabella anagrafica, e avere evidenza della selezione in mappa.

In termini di SQL, se volessi ad esempio selezionare il punto corrispondente all'idrante che nella tabella dell’anagrafica, per una certa colonna, è caratterizzato da un determinato valore, dovrei scrivere:

SELECT * FROM idranti
JOIN (SELECT * FROM anagraficaIdranti WHERE attributo IN ('253a')) AS aI
ON idranti.id=aI.id

In QGIS posso lanciare questa query tramite il DB Manager, ma non c’è modo nativamente di usarla per vedere il mio idrante selezionato in mappa. Per questo ho cercato una strada alternativa, di cui condivido qui gli esiti; è poco più di un esempio “Hello world”, ma penso che potrà essere molto utile come punto di partenza.

QGIS Action

Le azioni sono una caratteristica molto comoda di QGIS, che consente di associare un trigger (lo scatenarsi di un evento) alle feature di un layer: ad esempio un’azione classica è quella di aprire un’immagine a partire da un determinato record.

L’azione creata per questo caso d’uso è da associare al layer/tabella che si vuole usare e correlare al layer geometrico. Nello script Python sarà necessario inserire:

  • il nome del layer geometrico su cui si vuole applicare la selezione per attributo;
  • i riferimenti dei campi usati per fare il JOIN tra la tabella correlata e il layer geometrico.
from qgis.utils import iface
# inserire qui il nome del layer geometrico
vl = QgsMapLayerRegistry.instance().mapLayersByName('nomeLayerGeom')[0]
iface.setActiveLayer(vl)
cLayer = iface.mapCanvas().currentLayer()
# qui sotto i nomi dei due campi da correlare
expr = QgsExpression("\"refGeom\"=[% "refAttr" %]")
it = cLayer.getFeatures( QgsFeatureRequest( expr ) )
ids = [i.id() for i in it]
cLayer.setSelectedFeatures( ids )
# zoom alla selezione
box = cLayer.boundingBoxOfSelected()
iface.mapCanvas().setExtent(box)
iface.mapCanvas().refresh()

Nel codice di sopra si inizia dal rendere attivo il layer geometrico, poi si definisce l’espressione da usare per fare la query per attributo (con il JOIN tra il campo refGeom per il layer geometrico, e il campo refAttr per il layer correlato), si lancia la query e infine si fa lo zoom alla selezione.

Ho preparato un gist con tutto l’occorrente per fare autonomamente un test, con all'interno:

L’Azione in azione ;)

Totò Fiandaca, che è stato tra i primi a cui ho mostrato il tutto, ha voluto testare la cosa e mi ha regalato il video sottostante, in cui è possibile vedere applicato quanto descritto sopra. 
Totò (che ringrazio) fa vedere inoltre come la stessa azione sia utilizzabile anche a partire da un form di QGIS.

NdR: Totò, dopo la pubblicazione del post, ha aggiunto un video molto più ricco con un bel caso d’uso (grazie).

La macro di Salvatore Larosa

Dopo la pubblicazione di questo post, Salvatore Larosa ha pubblicato un’altra modalità (molto bella) per risolvere la questione.

Il tutto si imposta a livello di progetto, con una macro in Python:

  • si aprono le proprietà di progetto;
  • si fa click su Macros e si incolla il codice sottostante;
  • si attiva, sempre a livello di progetto, la relazione tra layer padre e figlio;
  • fine.

Fatto questo, alla selezione di 1 o più record della tabella correlata, verranno automaticamente selezionate le geometrie correlate.

Prima che Salvatore rendesse pubblico questa macro, avevo scritto che vorrei essere bravo come lui. E questo suo contributo spiega le ragioni di questo mio pensiero.

Totò Fiandaca ha creato un altro video che mostra come attivare il tutto e goderne :)

Il plugin di Luca Mandolesi

Luca Mandolesi, dopo la pubblicazione del post e successivamente al lavoro di Salvatore Larosa, ha creato un plugin basato sulla macro di quest’ultimo.

Lo trovate qui: https://github.com/pyarchinit/selectFromRelations

Nel video seguente Salvatore Fiandaca ve lo mostra in azione:

Una nota finale

I due GIS Desktop su cui mi sono formato e che non uso più (a molti saranno sconosciuti) — TNTmips e Manifold — prevedevano nativamente la possibilità di usare una query SQL su un layer geometrico, in JOIN anche complessi con altre tabelle e visualizzare su mappa il risultato della selezione.
Attivare la cosa su QGIS sarebbe una gran cosa, che a mio avviso meriterebbe una raccolta fondi dedicata e a cui darei il mio contributo.

Lo stesso dicasi per l’estensione delle opzioni delle proprietà di JOIN di un layer, in modo da abilitare anche comportamenti come quelli attivati da questa azione. O creare un plugin che dia un’interfaccia per l’impostazione dei parametri necessari (certe volte vorrei essere bravo come Salvatore La Rosa o Giovanni Allegri).

Non voglio banalizzare la cosa, ma sicuramente sottolineare quanto si tratti di feature preziose e molto abilitanti per un applicativo GIS Desktop.