Created
May 5, 2025 12:41
-
-
Save pigreco/dd056b50afc6b19d2d80368df19eac6f to your computer and use it in GitHub Desktop.
Create Many-to-Many Relationship Table
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
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