Skip to content

Instantly share code, notes, and snippets.

@pigreco
Created May 5, 2025 12:41
Show Gist options
  • Save pigreco/dd056b50afc6b19d2d80368df19eac6f to your computer and use it in GitHub Desktop.
Save pigreco/dd056b50afc6b19d2d80368df19eac6f to your computer and use it in GitHub Desktop.
Create Many-to-Many Relationship Table
"""
Nome=Crea Tabella di Collegamento
Group=Scripts
"""
from qgis.PyQt.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterField,
QgsProcessingParameterVectorDestination,
QgsFeature,
QgsField,
QgsFields,
QgsFeatureRequest,
QgsWkbTypes,
QgsSpatialIndex)
import processing
class CreaTabellaDiCollegamento(QgsProcessingAlgorithm):
"""
Questo algoritmo crea una tabella di collegamento per una relazione
molti a molti (n:m) basata sull'intersezione spaziale di due layer.
"""
# Definizione dei parametri
LAYER_A = 'LAYER_A'
LAYER_B = 'LAYER_B'
ID_CAMPO_A = 'ID_CAMPO_A'
ID_CAMPO_B = 'ID_CAMPO_B'
OUTPUT = 'OUTPUT'
def tr(self, string):
"""
Gestisce le traduzioni.
"""
return QCoreApplication.translate('Processing', string)
def createInstance(self):
return CreaTabellaDiCollegamento()
def name(self):
return 'creatabellacoll'
def displayName(self):
return self.tr('Crea Tabella di Collegamento')
def group(self):
return self.tr('Scripts')
def groupId(self):
return 'scripts'
def shortHelpString(self):
return self.tr('Questo algoritmo crea una tabella di collegamento per una relazione molti a molti (n:m) tra due layer basata sulle loro intersezioni spaziali. <br><br> by Totò Fiandaca')
def initAlgorithm(self, config=None):
# Aggiungiamo i parametri di input
self.addParameter(
QgsProcessingParameterVectorLayer(
self.LAYER_A,
self.tr('Layer A'),
[QgsProcessing.TypeVectorAnyGeometry]
)
)
self.addParameter(
QgsProcessingParameterVectorLayer(
self.LAYER_B,
self.tr('Layer B'),
[QgsProcessing.TypeVectorAnyGeometry]
)
)
self.addParameter(
QgsProcessingParameterField(
self.ID_CAMPO_A,
self.tr('Campo ID del Layer A'),
None,
self.LAYER_A
)
)
self.addParameter(
QgsProcessingParameterField(
self.ID_CAMPO_B,
self.tr('Campo ID del Layer B'),
None,
self.LAYER_B
)
)
# Aggiungiamo il parametro di output
self.addParameter(
QgsProcessingParameterVectorDestination(
self.OUTPUT,
self.tr('Tabella di collegamento')
)
)
def processAlgorithm(self, parameters, context, feedback):
# Recuperiamo i layer di input
layer_a = self.parameterAsVectorLayer(parameters, self.LAYER_A, context)
layer_b = self.parameterAsVectorLayer(parameters, self.LAYER_B, context)
# Recuperiamo i nomi dei campi ID
id_campo_a = self.parameterAsString(parameters, self.ID_CAMPO_A, context)
id_campo_b = self.parameterAsString(parameters, self.ID_CAMPO_B, context)
# Controlliamo se i layer sono validi
if not layer_a or not layer_b:
raise QgsProcessingException(self.tr('Layer di input non validi'))
# Creiamo i campi per la tabella di output
fields = QgsFields()
fields.append(QgsField('id', QVariant.Int))
fields.append(QgsField('id_a', QVariant.Int))
fields.append(QgsField('id_b', QVariant.Int))
# Creiamo la tabella di output (sink)
# Modifica: rimuoviamo il parametro problematico None
(sink, dest_id) = self.parameterAsSink(
parameters,
self.OUTPUT,
context,
fields,
QgsWkbTypes.NoGeometry
)
# Controlliamo se il sink è stato creato correttamente
if sink is None:
raise QgsProcessingException(self.tr('Impossibile creare il layer di output'))
# Creiamo un indice spaziale per il layer B per migliorare le prestazioni
feedback.pushInfo(self.tr('Creazione dell\'indice spaziale per il layer B...'))
index = QgsSpatialIndex(layer_b.getFeatures())
# Inizializziamo contatore e set per le relazioni
count = 1
relations = set()
# Calcoliamo il totale per la barra di avanzamento
total = 100.0 / layer_a.featureCount() if layer_a.featureCount() else 0
# Elaboriamo ogni feature del layer A
feedback.pushInfo(self.tr('Analisi delle intersezioni in corso...'))
for current, feature_a in enumerate(layer_a.getFeatures()):
# Controlliamo se l'utente ha cancellato l'operazione
if feedback.isCanceled():
break
# Otteniamo geometria e ID della feature A
geom_a = feature_a.geometry()
id_a = feature_a[id_campo_a]
# Troviamo le potenziali intersezioni usando l'indice spaziale
bbox = geom_a.boundingBox()
candidate_ids = index.intersects(bbox)
# Per ogni candidato, controlliamo l'intersezione reale
request = QgsFeatureRequest().setFilterFids(candidate_ids)
for feature_b in layer_b.getFeatures(request):
geom_b = feature_b.geometry()
# Verifichiamo l'intersezione reale
if geom_a.intersects(geom_b):
id_b = feature_b[id_campo_b]
# Creiamo una chiave per questa relazione
relation_key = (id_a, id_b)
# Aggiungiamo la relazione solo se non esiste già
if relation_key not in relations:
# Creiamo una nuova feature per la tabella di output
f = QgsFeature(fields)
f.setAttributes([count, id_a, id_b])
# Aggiungiamo la feature alla tabella di output
sink.addFeature(f)
# Registriamo la relazione e incrementiamo il contatore
relations.add(relation_key)
count += 1
# Aggiorniamo la barra di avanzamento
feedback.setProgress(int(current * total))
# Informazioni finali
feedback.pushInfo(self.tr('Processo completato! Relazioni trovate: {}'.format(count - 1)))
# Restituiamo l'ID della tabella di output
return {self.OUTPUT: dest_id}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment