Nous utilisons des cookies pour vous garantir la meilleure expérience sur notre site. Si vous continuez à utiliser ce dernier, nous considèrerons que vous acceptez l'utilisation des cookies. J'ai compris ! ou En savoir plus !.
banniere

Le portail francophone de la géomatique


Toujours pas inscrit ? Mot de passe oublié ?
Nom d'utilisateur    Mot de passe              Toujours pas inscrit ?   Mot de passe oublié ?

Annonce

Rencontres QGIS 2025

L'appel à participation est ouvert jusqu'au 19 janvier 2025!

#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

 

Pied de page des forums

Powered by FluxBB