1

I'd like to have a Python script that loads a raster layer contained in a PostGIS database into my QGIS project.

I found this very useful script that works for loading a vector layer : Processing script to load vector layer from PostGIS - connection string error

and I wanted to adapt it for raster layers, but my coding knowledge is very limited and I can't manage to make it work.

What I tried to do is change all the "geom" occurencies by "rast" and to adapt the functions (basically I mostly changed "vector" by "raster" everywhere).

The script is now the following :

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProcessingAlgorithm,
                       QgsProcessingException,
                       QgsProcessingContext,
                       QgsProcessingParameterString,
                       QgsProcessingOutputVectorLayer,
                       QgsProcessingOutputRasterLayer,
                       QgsProcessingOutputString,
                       QgsProcessingParameterNumber,
                       QgsVectorLayer,
                       QgsRasterLayer,
                       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('Load PostGIS merged')

    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(
            QgsProcessingOutputRasterLayer(
                'LAYER',
                self.tr('Raster layer'),
                QgsProcessing.TypeRaster
            )
        )

    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, "rast")

        layer = QgsRasterLayer(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()}

But when I run it I get this error :

          File "/Applications/QGIS.app/Contents/MacOS/../Resources/python/plugins/processing/script/ScriptEditorDialog.py", line 253, in runAlgorithm
          alg.initAlgorithm()
          File "", line 122, in initAlgorithm
         TypeError: QgsProcessingOutputRasterLayer(): arguments did not match any overloaded call:
          overload 1: too many arguments
          overload 2: argument 1 has unexpected type 'str'

And I don't really know what to do about it.

2 Answers 2

1

The constructor to QgsProcessingOutputRasterLayer takes only two arguments. See https://qgis.org/pyqgis/3.26/core/QgsProcessingOutputRasterLayer.html .

Remove the QgsProcessing.TypeRaster argument from the algorithm output definition (last part of your initAlgorithm function).

edit:

And add an extra argument to the QgsProcessingContext.LayerDetails call, which should read

QgsProcessingContext.LayerDetails(
   'Raster Layer',context.project()
   ,'LAYER'
   ,QgsProcessingUtils.LayerHint.Raster
)
5
  • Thanks, I changed that. There is still another error though: Erroneous query: SELECT 0, array_agg(DISTINCT st_srid("rast")::text || ':' || UPPER(geometrytype("rast")) || ':' || ST_Zmflag("rast")) FROM (SELECT "rast" FROM "public"."merge") AS _unused returned 7 [ERROR: function geometrytype(raster) does not exist. LINE 1: ...gg(DISTINCT st_srid("rast")::text || ':' || UPPER(geometryty... Sep 9, 2022 at 12:20
  • with a HINT that I would translate as "No function corresponds to the given name and argument types. You need to add explicite type conversions" Sep 9, 2022 at 12:21
  • Some part of the script still tries to operate on a vector layer, but I cannot really see which part.
    – til_b
    Sep 9, 2022 at 15:51
  • I hope it was the LayerDetails LayerHint, but I think it is (see edited answer)
    – til_b
    Sep 9, 2022 at 16:29
  • 1
    Thanks for your help, but this didn't change the error. I posted the solution I finally found. Sep 9, 2022 at 19:55
1

Thank you @til_b for your help.

I tried to add the argument to the QgsProcessingContext.LayerDetails call as you suggested, but it didn't change the error. I tried several other things, and I finally managed to find the last missing detail.

I had :

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

But it had to be :

layer = QgsRasterLayer(uri.uri(), layername, "postgresraster")

So here is the final script that is working perfectly:


from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProcessingAlgorithm,
                       QgsProcessingException,
                       QgsProcessingContext,
                       QgsProcessingParameterString,
                       QgsProcessingOutputVectorLayer,
                       QgsProcessingOutputRasterLayer,
                       QgsProcessingOutputString,
                       QgsProcessingParameterNumber,
                       QgsVectorLayer,
                       QgsRaster,
                       QgsRasterLayer,
                       QgsDataSourceUri,
                       QgsApplication,
                       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('Load PostGIS merged')

    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'),
            )
        )

        self.addOutput(
            QgsProcessingOutputRasterLayer(
                'LAYER',
                self.tr('Raster layer')
            )
        )

    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, "rast")

        layer = QgsRasterLayer(uri.uri(), layername, "postgresraster")

        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()}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.