#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
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