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é ?

#1 Tue 25 May 2021 19:03

Caligraf
Juste Inscrit !
Date d'inscription: 15 Nov 2017
Messages: 4

QGIS: Processing script pour charger des couches PostGIS

Bonjour,

Je cherche à élaborer un script permettant de charger des couches issues d'une base de donnée PostGIS. Je n'ai pour l'instant pas réussi à intégrer la classe QgsDataSourceUri() comme je l'esperais. En effet, je tombe sur l'erreur suivante : TypeError: invalid result from LoadLayer.processAlgorithm(), NoneType cannot be converted to a C/C++ QVariantMap in this context

Voici le code en question :

Code:

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProcessingAlgorithm,
                       QgsProcessingException,
                       QgsProcessingParameterString,
                       QgsProcessingOutputString,
                       QgsProcessingParameterNumber,
                       QgsVectorLayer,
                       QgsDataSourceUri,
                       QgsProject)
from qgis import processing
from qgis.utils import *

class LoadLayer(QgsProcessingAlgorithm):
    """
    Charge une couche vectorielle depuis la base de donnée PostGIS
    """

    def tr(self, string):
        """
        Returns a translatable string with the self.tr() function.
        """
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        # Must return a new copy of your algorithm.
        return LoadLayer()

    def name(self):
        """
        Returns the unique algorithm name.
        """
        return 'load_post_gis_layer'

    def displayName(self):
        """
        Returns the translated algorithm name.
        """
        return self.tr('Load layers from PostGIS')

    def group(self):
        """
        Returns the name of the group this algorithm belongs to.
        """
        return self.tr('Load single vector layer')

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs
        to.
        """
        return 'load_single_vector_layer'

    def shortHelpString(self):
        """
        Returns a localised short help string for the algorithm.
        """
        return self.tr('Charge les couches depuis PostGIS')

    def initAlgorithm(self, config=None):
        """
        Here we define the inputs and outputs of the algorithm.
        """
        self.addParameter(
            QgsProcessingParameterString(
                'HOST',
                self.tr('Host'),
                defaultValue = '172.16.0.116'
            )
        )
        self.addParameter(
            QgsProcessingParameterNumber(
                'PORT',
                self.tr('Port'),
                defaultValue = 5432
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'USERNAME',
                self.tr('Username'),
                defaultValue = 'xyz'
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'PASSWORD',
                self.tr('Password'),
                defaultValue = '***'
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'DATABASE',
                self.tr('Database'),
                defaultValue = 'abc'
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'SCHEMA',
                self.tr('schema'),
                defaultValue = 'schema'
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'LAYERNAME',
                self.tr('Layer name'),
                defaultValue = 'Name of the layer'
            )
        )
        self.addOutput(
            QgsProcessingOutputString(
                'CONNEXIONSTR',
                self.tr('Imported layer')
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        
        host = self.parameterAsString (parameters,'HOST', context)

        port = self.parameterAsString (parameters, 'PORT', context)
        
        username = self.parameterAsString (parameters, 'USERNAME', context)

        password = self.parameterAsString (parameters, 'PASSWORD',context)

        database = self.parameterAsString (parameters, 'DATABASE', context)
        
        schema = self.parameterAsString (parameters, 'SCHEMA', context)
        
        layername = self.parameterAsString (parameters, 'LAYERNAME', context)
        
        
        uri = QgsDataSourceUri()
        uri.setConnection(host, port, database, username, password)
        uri.setDataSource(schema, layername, "geom")

        layer = QgsVectorLayer(uri.uri(), layername, "postgres")
        
        if feedback.isCanceled():
            return {}
        
        # Return the results
            return {'CONNEXIONSTR': layer}

En poussant mes recherches, j'ai également étudié la possibilité d'ajouter une ligne de connexion, récupérant les infos que l'on peut retrouver en cliquant sur une couche importée dans "propriétés", en m'inspirant de cette discussion :
https://stackoverflow.com/questions/358 … string-url

Code:

return {'CONNEXIONSTR' : f"postgresql://{user}:{password}@{host}:{port}/{database}"}

Toutefois ce code est incomplet, il manque des informations normalement délivrée par la méthode setDataSource ( nom de la table, la géométrie et "postgres" ) mais j'ignore le formatage nécessaire...

Bref, je n'ai pour l'instant pas de solution concrète, si ce n'est éventuellement de passer par un module tiers tel que sqlalchemy par exemple.

Hors ligne

 

#2 Tue 25 May 2021 23:13

ThomasG
Membre
Lieu: Nantes
Date d'inscription: 9 Sep 2005
Messages: 946
Site web

Re: QGIS: Processing script pour charger des couches PostGIS

Bonsoir,

Une solution testée ci-dessous.
La fin est "pompée" du fichier PostGISExecuteAndLoadSQL.py dans les scripts processing fournis avec QGIS

Code:

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProcessingAlgorithm,
                       QgsProcessingException,
                       QgsProcessingContext,
                       QgsProcessingParameterString,
                       QgsProcessingOutputVectorLayer,
                       QgsProcessingOutputString,
                       QgsProcessingParameterNumber,
                       QgsVectorLayer,
                       QgsDataSourceUri,
                       QgsProject)
from qgis import processing


class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
    """
    This is an example algorithm that takes string inputs,
    to provide to DB connexion
    """

    def tr(self, string):
        """
        Returns a translatable string with the self.tr() function.
        """
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        # Must return a new copy of your algorithm.
        return ExampleProcessingAlgorithm()

    def name(self):
        """
        Returns the unique algorithm name.
        """
        return 'postgresconnexion'

    def displayName(self):
        """
        Returns the translated algorithm name.
        """
        return self.tr('Return PostgreSQL layer')

    def group(self):
        """
        Returns the name of the group this algorithm belongs to.
        """
        return self.tr('Example scripts')

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs
        to.
        """
        return 'examplescripts'

    def shortHelpString(self):
        """
        Returns a localised short help string for the algorithm.
        """
        return self.tr('Example algorithm short description')

    def initAlgorithm(self, config=None):
        """
        Here we define the inputs and outputs of the algorithm.
        """
        # 'INPUT' is the recommended name for the main input
        # parameter.
        self.addParameter(
            QgsProcessingParameterString(
                'HOST',
                self.tr('Host'),
                defaultValue = 'localhost'
            )
        )
        self.addParameter(
            QgsProcessingParameterNumber(
                'PORT',
                self.tr('Port'),
                defaultValue = 5432
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'USERNAME',
                self.tr('Username'),
            )
        )
        # 'OUTPUT' is the recommended name for the main output
        # parameter.
        self.addParameter(
            QgsProcessingParameterString(
                'PASSWORD',
                self.tr('Password')
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'DATABASE',
                self.tr('Database')
            )
        )

        self.addParameter(
            QgsProcessingParameterString(
                'SCHEMA',
                self.tr('schema'),
                defaultValue = 'public'
            )
        )
        self.addParameter(
            QgsProcessingParameterString(
                'LAYERNAME',
                self.tr('Layer name'),
                defaultValue = 'Name of the layer'
            )
        )

        self.addOutput(
            QgsProcessingOutputVectorLayer(
                'LAYER',
                self.tr('Vector layer'),
                QgsProcessing.TypeVectorAnyGeometry
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        # First, we get the count of features from the INPUT layer.
        # This layer is defined as a QgsProcessingParameterFeatureSource
        # parameter, so it is retrieved by calling
        # self.parameterAsSource.
        host = self.parameterAsString (parameters,
                                                     'HOST',
                                                     context)

        # Retrieve the buffer distance and raster cell size numeric
        # values. Since these are numeric values, they are retrieved
        # using self.parameterAsDouble.
        port = self.parameterAsString (parameters, 'PORT',
                                            context)
        username = self.parameterAsString (parameters, 'USERNAME',
                                                context)

        password = self.parameterAsString (parameters, 'PASSWORD',
                                                context)

        database = self.parameterAsString (parameters, 'DATABASE',
                                                context)

        schema = self.parameterAsString (parameters, 'SCHEMA', context)
        
        layername = self.parameterAsString (parameters, 'LAYERNAME', context)

        uri = QgsDataSourceUri()
        uri.setConnection(host, port, database, username, password)
        uri.setDataSource(schema, layername, "geom")

        layer = QgsVectorLayer(uri.uri(), layername, "postgres")

        if feedback.isCanceled():
            return {}
        
        if not layer.isValid():
            raise QgsProcessingException(self.tr("""This layer is invalid!
                Please check the PostGIS log for error messages."""))

        context.temporaryLayerStore().addMapLayer(layer)
        context.addLayerToLoadOnCompletion(
            layer.id(),
            QgsProcessingContext.LayerDetails('SQL layer',
                                              context.project(),
                                              'LAYER'))

        # Return the results
        return {'LAYER': layer.id()}

Vous n'êtes pas obligé de passer par la définition de chaque paramètre de la base de données mais vous pouvez aussi réutiliser les connexions PostGIS existantes déjà définies en passant par QgsProcessingParameterProviderConnection (toujours en regardant le fichier PostGISExecuteAndLoadSQL.py)

Si vous souhaitez charger en une fois un ensemble de couches PostGIS en trouvant automatiquement leur nom, vous pouvez utiliser le code de https://gis.stackexchange.com/a/395994/638 (attention, pas dans le contexte d'un script processing et utilisant les connexions définies dans les connexions PostgreSQL définies dans QGIS)


Thomas

Hors ligne

 

#3 Wed 02 June 2021 09:37

Caligraf
Juste Inscrit !
Date d'inscription: 15 Nov 2017
Messages: 4

Re: QGIS: Processing script pour charger des couches PostGIS

Bonjour Thomas, merci pour cette formidable solution.
Les dernières lignes de codes manquantes m'ont bien aidé, et j'ai même adapté le code pour charger deux couches fréquemment utilisées ensemble.
Je relaye votre solution.

Hors ligne

 

Pied de page des forums

Powered by FluxBB