4

I have a problem with some python code that I'm trying to interface into qgis. This script works via python console but not when I try to implement it as a tool.

The goal of the code is to get data of volume in function of different heights of a DEM raster.

I get errors at the lines:

line 149, in processAlgorithm stats = input_featuresource.dataProvider().bandStatistics(1)
AttributeError: 'NoneType' object has no attribute 'dataProvider' 

and

line 177, in processAlgorithm processing.run("native:rastersurfacevolume", {'INPUT':parameters['Inputlayer'],'BAND':1,'LEVEL':level,'METHOD':1,'OUTPUT_HTML_FILE':'TEMPORARY_OUTPUT','OUTPUT_TABLE':outTable + ".shp"}) 
NameError: name 'processing' is not defined 

Processing script:

from qgis.PyQt.QtCore import QVariant
from qgis.core import QgsRasterLayer
from processing.core.Processing import Processing
from qgis.core import QgsField
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink,
                       QgsProject,
                       QgsVectorLayer)


# Python can not iterate with floats, therefore we define this function

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

    def createInstance(self):
        return ExampleProcessingAlgorithm()

    def name(self):
        """
        Returns the algorithm name, used for identifying the algorithm. This
        string should be fixed for the algorithm, and must not be localised.
        The name should be unique within each provider. Names should contain
        lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'HSV'

    def displayName(self):
        """
        Returns the translated algorithm name, which should be used for any
        user-visible display of the algorithm name.
        """
        return self.tr('HSV')

    def group(self):
        """
        Returns the name of the group this algorithm belongs to. This string
        should be localised.
        """
        return self.tr('SHER tools')

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs to. This
        string should be fixed for the algorithm, and must not be localised.
        The group id should be unique within each provider. Group id should
        contain lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'examplescripts'

    def shortHelpString(self):
        """
        Returns a localised short helper string for the algorithm. This string
        should provide a basic description about what the algorithm does and the
        parameters and outputs associated with it..
        """
        return self.tr("Example algorithm short description")
        
    def initAlgorithm(self, config=None):
        """
        Here we define the inputs and output of the algorithm, along
        with some other properties.
        """
        
                                                     
        self.addParameter(QgsProcessingParameterRasterLayer('Inputlayer',
                'Inputlayer', 
                defaultValue=None))
        
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                'INPUT',
                self.tr('Input raster layer'),
                types=[QgsProcessing.TypeRaster]
            )
        )
        
        self.addParameter(QgsProcessingParameterFile('Dossier', 'Dossier', 
            behavior=QgsProcessingParameterFile.Folder, 
            fileFilter='Tous les fichiers (*.*)', 
            defaultValue=None))
        
        self.addParameter(QgsProcessingParameterFeatureSink('OUTPUT', 'Output Layer'))

        # We add a feature sink in which to store our processed features (this
        # usually takes the form of a newly created vector layer when the
        # algorithm is run in QGIS).
        
        #self.addParameter(
        #    QgsProcessingParameterFeatureSink(
        #        self.OUTPUT,
        #        self.tr('Output layer')
        #    )
        #)
        
    def processAlgorithm(self, parameters, context, feedback):
    
        """
        Here is where the processing itself takes place.
        """
        
        # Retrieve the feature source and sink. The 'dest_id' variable is used
        # to uniquely identify the feature sink, and must be included in the
        # dictionary returned by the processAlgorithm function.
        
        
        # Send some information to the user
        #feedback.pushInfo('CRS is {}'.format(source.sourceCrs().authid()))
        results = {}
        outputs = {}
        
        # Set the path to your folder and the DTM file
        #projectPath = 'C:/Users/maxime.chavet/Documents/BKF76_Formations/SIG/'
        #inputRasterDEM = 'C:/Users/maxime.chavet/Documents/BKF76_Formations/Smart-Map/1_Krig_prof_Grid_Map.tiff'
        #vector_output='OUTPUT_VECTOR'

        #demLayer = iface.addRasterLayer(inputRasterDEM,"DEM","gdal")
        #demLayer = QgsRasterLayer(inputRasterDEM, "DEM", "gdal")
        # We add the input vector features source. It can have any kind of
        # geometry.
        input_featuresource = self.parameterAsSource(parameters,
                                                     'Inputlayer',
                                                     context)

        demLayer=input_featuresource
        
        def frange(start, stop, step):
            i = start
            while i < stop:
                yield i
                i += step 

        # Calculate the statistics (min/max) of the DTM
        stats = input_featuresource.dataProvider().bandStatistics(1)
        demMinimum = stats.minimumValue
        #demMinimum = 2
        #demMaximum=4
        demMaximum = stats.maximumValue
        #print("min:",demMinimum,"m")
        #print("max:",demMaximum,"m")

        # Determine the range
        demRange = demMaximum - demMinimum
        print("Elevation Difference:",demRange,"m")


        # Set the increment for the iteration at 10% of the range
        increment = demRange / 10.0
        print("Increment:",increment)
        i = 0

        # Create an empty list for the dbf files
        dbfList = []

        # Loop over the elevation range from the minimum to the maximum with the increment
        for level in frange(demMinimum,demMaximum + 1,increment):
            # Define the output table name
            outTable = 'C:/Users/maxime.chavet/Documents/BKF76_Formations/SIG/' +"volume" + str(round(level*100.0))
            outTableDbf = outTable + ".dbf"
            
            # Run the raster surface volume tool with the variables
            processing.run("native:rastersurfacevolume", {'INPUT':parameters['Inputlayer'],'BAND':1,'LEVEL':level,'METHOD':1,'OUTPUT_HTML_FILE':'TEMPORARY_OUTPUT','OUTPUT_TABLE':outTable + ".shp"})
            
            # Read the table
            #dbfTable = QgsVectorLayer(outTableDbf, outTable, "ogr")
            dbfTable = QgsVectorLayer(outTable + ".shp", outTable, "ogr")
            
            # Convert the volumes from m3 to km3
            for feature in dbfTable.getFeatures():
                VolumeKm3 = abs(feature["Volume"])/1000000000.0
            
            # Add the km3 field to the table
            pr = dbfTable.dataProvider()
            pr.addAttributes([QgsField("Level", QVariant.Double),QgsField("VolAbsKm3", QVariant.Double)])
            dbfTable.updateFields()
            dbfTable.startEditing()
            for f in dbfTable.getFeatures():
                f["Level"] = level
                f["VolAbsKm3"] = VolumeKm3
                dbfTable.updateFeature(f)
            dbfTable.commitChanges()
         
            dbfList.append(outTableDbf)

        # Merge all dbf files into one
        #path_output=parameters['Dossier'] + 'stagevolume.shp'
        outputs['OUTPUT']= processing.run("native:mergevectorlayers", {'LAYERS':dbfList,'CRS':None,'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT})
        #outputs['OUTPUT']=path_output
        #QgsVectorLayer(parameters['Dossier'] + 'stagevolume.dbf', "StageVolume", "ogr")

        # Add the result to the project projectPath + 'stagevolume.shp'
        #QgsProject.instance().addMapLayer(vector_output)
        return {'OUTPUT': outputs['OUTPUT']}

1 Answer 1

5
  • Add import processing to import section to get rid of NameError.
  • Since you use self.parameterAsSource, you get NoneType object .. error. Instead, use self.parameterAsRasterLayer.

Use the script below. I removed unused imports, docstrings, unused lines.

import processing
from qgis.PyQt.QtCore import QVariant
from qgis.core import QgsField
from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSink,
                       QgsVectorLayer,
                       QgsProcessingParameterRasterLayer,
                       QgsProcessingParameterFile)


class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def createInstance(self):
        return ExampleProcessingAlgorithm()

    def name(self):
        return 'HSV'

    def displayName(self):
        return self.tr('HSV')

    def group(self):
        return self.tr('SHER tools')

    def groupId(self):
        return 'examplescripts'

    def shortHelpString(self):
        return self.tr("Example algorithm short description")

    def initAlgorithm(self, config=None):

        self.addParameter(
            QgsProcessingParameterRasterLayer(
                'INPUT',
                self.tr('Input raster layer')
            )
        )

        self.addParameter(
            QgsProcessingParameterFile(
                'Dossier', 'Dossier',
                behavior=QgsProcessingParameterFile.Folder,
                fileFilter='Tous les fichiers (*.*)',
                defaultValue=None
        )
    )

        self.addParameter(
            QgsProcessingParameterFeatureSink(
                'OUTPUT',
                'Output Layer'
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        outputs = {}

        input_raster = self.parameterAsRasterLayer(parameters, 'INPUT', context)
        
        def frange(start, stop, step):
            i = start
            while i < stop:
                yield i
                i += step

        # Calculate the statistics (min/max) of the DTM
        stats = input_raster.dataProvider().bandStatistics(1)
        demMinimum = stats.minimumValue
        demMaximum = stats.maximumValue

        # Determine the range
        demRange = demMaximum - demMinimum
        print("Elevation Difference:", demRange, "m")


        # Set the increment for the iteration at 10% of the range
        increment = demRange / 10.0
        print("Increment:", increment)
        i = 0

        # Create an empty list for the dbf files
        dbfList = []

        # Loop over the elevation range from the minimum to the maximum with the increment
        for level in frange(demMinimum,demMaximum + 1,increment):
            # Define the output table name
            outTable = 'C:/Users/maxime.chavet/Documents/BKF76_Formations/SIG/' + "volume" + str(round(level*100.0))
            outTableDbf = outTable + ".dbf"

            # Run the raster surface volume tool with the variables
            processing.run("native:rastersurfacevolume",
                           {'INPUT':parameters['Inputlayer'],
                            'BAND':1,
                            'LEVEL':level,
                            'METHOD':1,
                            'OUTPUT_HTML_FILE':'TEMPORARY_OUTPUT',
                            'OUTPUT_TABLE':outTable + ".shp"})

            # Read the table
            dbfTable = QgsVectorLayer(outTable + ".shp", outTable, "ogr")

            # Convert the volumes from m3 to km3
            for feature in dbfTable.getFeatures():
                VolumeKm3 = abs(feature["Volume"])/1000000000.0

            # Add the km3 field to the table
            pr = dbfTable.dataProvider()
            pr.addAttributes([QgsField("Level", QVariant.Double),QgsField("VolAbsKm3", QVariant.Double)])
            dbfTable.updateFields()
            dbfTable.startEditing()
            for f in dbfTable.getFeatures():
                f["Level"] = level
                f["VolAbsKm3"] = VolumeKm3
                dbfTable.updateFeature(f)
            dbfTable.commitChanges()

            dbfList.append(outTableDbf)

        # Merge all dbf files into one
        outputs['OUTPUT']= processing.run("native:mergevectorlayers",
                                          {'LAYERS':dbfList,
                                           'CRS':None,
                                           'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT})

        return {'OUTPUT': outputs['OUTPUT']}
1
  • I didn't test all lines. If you get different errors, please open a new post to ask for it. This comment area is not suitable for additional questions. Nov 27, 2023 at 11:12

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.