#1 Tue 28 May 2019 10:55
- zack
- Participant occasionnel
- Date d'inscription: 16 Jul 2014
- Messages: 33
QGIS 3.6: Boucle sur entités avec PyQGIS
Bonjour,
J'ai exporté un script produit avec le modeleur graphique de QGIS, et j'essaye de modifier le code pour automatiser des boucles.
J'ai simplifié le code en ne mettant qu'un traitement pour l'instant (le buffer), et j'ai essayé de faire une boucle for en récupérant les entités de ma couche :
Code:
from qgis.core import QgsProcessing from qgis.core import QgsProcessingAlgorithm from qgis.core import QgsProcessingMultiStepFeedback from qgis.core import QgsProcessingParameterVectorLayer from qgis.core import QgsProcessingParameterFeatureSource from qgis.core import QgsProcessingParameterFeatureSink import processing class Modele_iteration(QgsProcessingAlgorithm): def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterVectorLayer('total', 'Sortie "total" ', types=[QgsProcessing.TypeVectorPolygon], defaultValue='I:/user/DONNEES/temp/test_iteration_pyqgis/total.shp')) self.addParameter(QgsProcessingParameterFeatureSource('nonbati', 'Sortie "non bâti" ', types=[QgsProcessing.TypeVectorPolygon], defaultValue='I:/user/DONNEES/temp/test_iteration_pyqgis/nonbati.shp')) self.addParameter(QgsProcessingParameterFeatureSink('Sortie', 'sortie', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue='I:/user/DONNEES/temp/test_iteration_pyqgis/test.shp')) def processAlgorithm(self, parameters, context, model_feedback): # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the # overall progress through the model feedback = QgsProcessingMultiStepFeedback(1, model_feedback) results = {} outputs = {} features = parameters['nonbati'].getFeatures() for feature in features : # Tampon alg_params = { 'DISSOLVE': False, 'DISTANCE': 0.5, 'END_CAP_STYLE': 0, 'INPUT': f, 'JOIN_STYLE': 0, 'MITER_LIMIT': 2, 'SEGMENTS': 5, 'OUTPUT': parameters['Sortie'] } outputs['Tampon'] = processing.run('native:buffer', alg_params, context=context, feedback=feedback, is_child_algorithm=True) results['Sortie'] = outputs['Tampon']['OUTPUT'] return results def name(self): return 'modele_iteration' def displayName(self): return 'modele_iteration' def group(self): return 'modele' def groupId(self): return 'modele' def createInstance(self): return Modele_iteration()
Mais ça ne marche pas comme ça apparemment, puisqu'il me dit "AttributeError: 'str' object has no attribute 'getFeatures'".
Est-ce que quelqu'un sait comment construire une boucle pyqgis ?
D'autant qu'après coup, j'aimerai mettre cette boucle for dans une boucle while, afin qu'il répète l'action tant que des changements sont effectués. En gros mon modèle vise à changer un champs "affectation" en fonction des valeurs des polygones voisins, et il doit se répéter jusqu'à ce qu'il n'y ait plus de polygones qui changent d'affectation.
Merci de votre aide !
Hors ligne
#2 Tue 28 May 2019 11:41
- JD
- Moderateur
- Date d'inscription: 8 Aug 2013
- Messages: 726
Re: QGIS 3.6: Boucle sur entités avec PyQGIS
Bonjour,
pour récupérer un objet de type QgsVectorLayer il faut utiliser une méthode particulière de la classe QgsProcessingAlgorithm
qui se nomme parameterAsLayer.
Il faut donc rajouter ceci dans votre code :
Code:
... nonbati_layer = self.parameterAsLayer(parameters, 'nonbati', context) features = nonbati_layer.getFeatures() ...
Hors ligne
#3 Tue 28 May 2019 12:06
- zack
- Participant occasionnel
- Date d'inscription: 16 Jul 2014
- Messages: 33
Re: QGIS 3.6: Boucle sur entités avec PyQGIS
Ok merci lejedi76, effectivement ça ne bloque plus sur le getFeatures.
Mais ça bloque ensuite au niveau du lancement du process buffer.
Est-ce qu'un feature suffit en input?
J'ai mis QgsProcessing.TEMPORARY_OUTPUT en output, mais lors de mon dernier traitement, comment faire pour avoir un nom relatif à mon entité (par exemple son id) ?
Je suis novice en python, mais après avoir compris le fonctionnement de pyqgis je devrais m'en sortir, au moins c'est un bon exercice.
Merci de votre aide !
Hors ligne
#4 Tue 28 May 2019 22:09
- Mathieu CHAILLOUX
- Juste Inscrit !
- Date d'inscription: 10 Jul 2017
- Messages: 4
Re: QGIS 3.6: Boucle sur entités avec PyQGIS
Est-ce qu'un feature suffit en input?
Non, il faut une couche.
J'ai déjà eu à faire ce genre d'opération et ma solution était de sélectionner chaque feature comme une nouvelle couche temporaire puis d'appeler l'algorithme à appliquer (pour vous le buffer) sur cette nouvelle couche.
Voici un exemple de code qu'il vous faudra adapter :
Code:
for count, report_feat in enumerate(reporting.getFeatures()): multi_feedback.setCurrentStep(count) report_id = report_feat.id() reporting.selectByIds([report_id]) select_path = params.mkTmpLayerPath("reportingSelection" + str(report_feat.id()) + ".gpkg") qgsTreatments.saveSelectedFeatures(reporting,select_path,context,feedback) report_computed_path = params.mkTmpLayerPath("reportingComputed" + str(report_feat.id()) + ".gpkg") parameters = { EffectiveMeshSizeGlobalAlgorithm.INPUT : source, EffectiveMeshSizeGlobalAlgorithm.SELECT_EXPR : select_expr, EffectiveMeshSizeGlobalAlgorithm.BOUNDARY : select_path, EffectiveMeshSizeGlobalAlgorithm.CRS : crs, EffectiveMeshSizeGlobalAlgorithm.CUT_MODE : cut_mode, EffectiveMeshSizeGlobalAlgorithm.UNIT : unit, EffectiveMeshSizeGlobalAlgorithm.OUTPUT : report_computed_path } qgsTreatments.applyProcessingAlg(FragScapeAlgorithmsProvider.NAME, EffectiveMeshSizeGlobalAlgorithm.ALG_NAME, parameters,context,multi_feedback)
Pour créer un chemin correspondant à une couche temporaire : QgsProcessingUtils.generateTempFilename
Pour sauvegarder les entités sélectionnées dans une couche : native:saveselectedfeatures
Hors ligne
#5 Thu 06 June 2019 09:44
- zack
- Participant occasionnel
- Date d'inscription: 16 Jul 2014
- Messages: 33
Re: QGIS 3.6: Boucle sur entités avec PyQGIS
Bonjour, et avec un peu de retard merci pour votre réponse !
Depuis j'essaye de trouver la solution par moi-même, j'ai lu "The PyQGIS Programmer's Guide" de Gary Sherman, sans que ça ne m'éclaire particulièrement, et j'ai parcouru une foultitude de forums à la recherche de ma réponse.
J'en suis arrivé au code suivant, dans le but de récupérer l'entité au sein d'une couche :
Code:
def processAlgorithm(self, parameters, context, model_feedback): # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the # overall progress through the model feedback = QgsProcessingMultiStepFeedback(1, model_feedback) results = {} outputs = {} nonbati_layer = self.parameterAsLayer(parameters, 'nonbati', context) features = nonbati_layer.getFeatures() for feature in features : feature_id = feature.id() nonbati_layer.selectByIds([feature_id]) temp_layer = QgsVectorLayer("Polygon?crs=epsg=2154", "temporary_polygons", "memory") selection = nonbati_layer.selectedFeatures() temp_data = temp_layer.dataProvider() attr = nonbati_layer.dataProvider().fields().toList() temp_data.addAttributes(attr) temp_layer.updateFields() temp_data.addFeatures(selection) # Tampon alg_params = { 'DISSOLVE': False, 'DISTANCE': 0.5, 'END_CAP_STYLE': 0, 'INPUT': temp_layer, 'JOIN_STYLE': 0, 'MITER_LIMIT': 2, 'SEGMENTS': 5, 'OUTPUT': 'I:/user/test' + str(feature_id) + '.shp' #QgsProcessing.TEMPORARY_OUTPUT } #outputs['Tampon'] = processing.run('native:buffer', alg_params, context=context, is_child_algorithm=True) #results['Sortie'] = outputs['Tampon']['OUTPUT'] # return results feedback.setCurrentStep(1) if feedback.isCanceled(): return {}
Je pense que du coup ça marche pour l'INPUT, mais j'ai un message d'erreur de "invalid result, NoneType cannot be converted to a C/C++ QVariantMap in this context" ...
J'essaye dans un premier temps de sortir une série de shapes dans un dossier. Mais ça ne marche pas. Je voudrais ensuite enchainer plusieurs traitements et avoir un fichier unique qui regroupe les sorties de boucle en sortie.
Et je me doute bien qu'il y a quelque chose à faire pour grouper toutes mes sorties en un seul fichier shape de sortie, mais je ne vois pas comment faire.
Je reviens donc sur mon poste, voir si vous avez une solution, tout seul je n'y arrive malheureusement pas ... merci d'avance !
Dernière modification par zack (Thu 06 June 2019 10:08)
Hors ligne
#6 Thu 06 June 2019 10:40
- zack
- Participant occasionnel
- Date d'inscription: 16 Jul 2014
- Messages: 33
Re: QGIS 3.6: Boucle sur entités avec PyQGIS
Je me suis un peu précipité pour poster le précédent message. En fait même s'il me met le message d'erreur "invalid result", il me sort bien tous les fichiers de l'itération.
Si je met return {} je n'ai plus de message d'erreur, mais il ne fait le travail que sur la première entité.
De plus, mes résultats en sortie sont en epsg 4326 alors que ma couche originale est en 2154 et que ma couche temporaire je lui ai demandé 2154... de plus ils ne sont pas du tout au bon endroit...
Bref je ne comprend pas tout mais j'avance ! Et pour un seul fichier unique j'imagine que je dois le créer en amont de la boucle et faire un ajout d'entités dessus à chaque fin de boucle, mais avec quelle fonction ?
Hors ligne