6

I am trying run an exported QGIS model as Python script from Anaconda.

I followed Executing a standalone QGIS python script on Windows 10, and I get no error of modules, when running this I get no error nor output but some warnings of deprecation

import os, sys

Append QGIS Python library to python search path

sys.path.append(r'C:\Program Files\QGIS 3.6\apps\Python37')

Append location of DLLs to current system PATH envrionment variable

os.environ['PATH'] += r";C:\Program Files\QGIS 3.6\apps\qgis\python;" print (os.environ['PATH']) from qgis.core import QgsProcessing from qgis.core import QgsProcessingAlgorithm from qgis.core import QgsProcessingMultiStepFeedback from qgis.core import QgsProcessingParameterRasterLayer from qgis.core import QgsProcessingParameterFeatureSink sys.path.append(r'C:\Program Files\QGIS 3.6\apps\qgis\python\plugins')

import processing

class Trees(QgsProcessingAlgorithm):

def initAlgorithm(self, config=None):
    self.addParameter(QgsProcessingParameterRasterLayer('dsm', 'DSM', defaultValue="Path/dsm.tif"))
    self.addParameter(QgsProcessingParameterRasterLayer('dtm', 'DTM', defaultValue="Path/dtm.tif"))
    self.addParameter(QgsProcessingParameterRasterLayer('zone', 'Zone', defaultValue="Path/odm_orthophoto.tif"))
    self.addParameter(QgsProcessingParameterFeatureSink('Finaltrees', 'FinalTrees', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue='Path/finaltrees.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(20, model_feedback)
    results = {}
    outputs = {}

    # Heights calculator
    alg_params = {
        'CELLSIZE': None,
        'CRS': None,
        'EXPRESSION': '\"DSM@1\" - \"DTM@1\"',
        'EXTENT': None,
        'LAYERS': parameters['dtm'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['HeightsCalculator'] = processing.run('qgis:rastercalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(1)
    if feedback.isCanceled():
        return {}

    # NDVI calculator
    alg_params = {
        'CELLSIZE': 0,
        'CRS': None,
        'EXPRESSION': 'sqrt(256*  \"Zone@2\"- 256 * \"Zone@3\")',
        'EXTENT': None,
        'LAYERS': parameters['dtm'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['NdviCalculator'] = processing.run('qgis:rastercalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(2)
    if feedback.isCanceled():
        return {}

    # NDVI Selector
    alg_params = {
        'CELLSIZE': 0,
        'CRS': None,
        'EXPRESSION': '\"\'Output\' from algorithm \'NDVI calculator\'@1\"> 100',
        'EXTENT': None,
        'LAYERS': outputs['NdviCalculator']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['NdviSelector'] = processing.run('qgis:rastercalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(3)
    if feedback.isCanceled():
        return {}

    # Only NDVI
    alg_params = {
        'CELLSIZE': 0,
        'CRS': None,
        'EXPRESSION': '(\"\'Output\' from algorithm \'NDVI Selector\'@1\" =1)*\"\'Output\' from algorithm \'NDVI Selector\'@1\"',
        'EXTENT': None,
        'LAYERS': outputs['NdviSelector']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['OnlyNdvi'] = processing.run('qgis:rastercalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(4)
    if feedback.isCanceled():
        return {}

    # Build virtual raster
    alg_params = {
        'ADD_ALPHA': False,
        'ASSIGN_CRS': None,
        'INPUT': outputs['OnlyNdvi']['OUTPUT'],
        'PROJ_DIFFERENCE': False,
        'RESAMPLING': 0,
        'RESOLUTION': 1,
        'SEPARATE': False,
        'SRC_NODATA': '0',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['BuildVirtualRaster'] = processing.run('gdal:buildvirtualraster', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(5)
    if feedback.isCanceled():
        return {}

    # True NDVI
    alg_params = {
        'CELLSIZE': 0,
        'CRS': None,
        'EXPRESSION': '(\"Zone@4\" -\"Zone@1\" ) /(\"Zone@4\" +\"Zone@1\" )',
        'EXTENT': None,
        'LAYERS': parameters['zone'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['TrueNdvi'] = processing.run('qgis:rastercalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(6)
    if feedback.isCanceled():
        return {}

    # Polygonize (raster to vector)
    alg_params = {
        'BAND': 1,
        'EIGHT_CONNECTEDNESS': False,
        'FIELD': 'Value',
        'INPUT': outputs['BuildVirtualRaster']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['PolygonizeRasterToVector'] = processing.run('gdal:polygonize', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(7)
    if feedback.isCanceled():
        return {}

    # Extract by attribute
    alg_params = {
        'FIELD': 'Value',
        'INPUT': outputs['PolygonizeRasterToVector']['OUTPUT'],
        'OPERATOR': 0,
        'VALUE': '1',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['ExtractByAttribute'] = processing.run('native:extractbyattribute', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(8)
    if feedback.isCanceled():
        return {}

    # Fix geometries
    alg_params = {
        'INPUT': outputs['ExtractByAttribute']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['FixGeometries'] = processing.run('native:fixgeometries', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(9)
    if feedback.isCanceled():
        return {}

    # JUSTNDVI
    alg_params = {
        'ALPHA_BAND': False,
        'CROP_TO_CUTLINE': True,
        'DATA_TYPE': 0,
        'INPUT': outputs['TrueNdvi']['OUTPUT'],
        'KEEP_RESOLUTION': False,
        'MASK': outputs['FixGeometries']['OUTPUT'],
        'MULTITHREADING': False,
        'NODATA': None,
        'OPTIONS': '',
        'SET_RESOLUTION': False,
        'SOURCE_CRS': None,
        'TARGET_CRS': None,
        'X_RESOLUTION': None,
        'Y_RESOLUTION': None,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['Justndvi'] = processing.run('gdal:cliprasterbymasklayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(10)
    if feedback.isCanceled():
        return {}

    # NDVIValues
    alg_params = {
        'FIELD_NAME': 'NDVI',
        'INPUT_RASTER': outputs['Justndvi']['OUTPUT'],
        'RASTER_BAND': 1,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['Ndvivalues'] = processing.run('native:pixelstopoints', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(11)
    if feedback.isCanceled():
        return {}

    # Clip raster by mask layer
    alg_params = {
        'ALPHA_BAND': False,
        'CROP_TO_CUTLINE': True,
        'DATA_TYPE': 0,
        'INPUT': outputs['HeightsCalculator']['OUTPUT'],
        'KEEP_RESOLUTION': False,
        'MASK': outputs['FixGeometries']['OUTPUT'],
        'MULTITHREADING': False,
        'NODATA': None,
        'OPTIONS': '',
        'SET_RESOLUTION': False,
        'SOURCE_CRS': None,
        'TARGET_CRS': None,
        'X_RESOLUTION': None,
        'Y_RESOLUTION': None,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['ClipRasterByMaskLayer'] = processing.run('gdal:cliprasterbymasklayer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(12)
    if feedback.isCanceled():
        return {}

    # Mean NDVI
    alg_params = {
        'DISCARD_NONMATCHING': False,
        'INPUT': outputs['FixGeometries']['OUTPUT'],
        'JOIN': outputs['Ndvivalues']['OUTPUT'],
        'JOIN_FIELDS': None,
        'PREDICATE': 1,
        'SUMMARIES': 6,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['MeanNdvi'] = processing.run('qgis:joinbylocationsummary', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(13)
    if feedback.isCanceled():
        return {}

    # Heights
    alg_params = {
        'FIELD_NAME': 'Height',
        'INPUT_RASTER': outputs['ClipRasterByMaskLayer']['OUTPUT'],
        'RASTER_BAND': 1,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['Heights'] = processing.run('native:pixelstopoints', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(14)
    if feedback.isCanceled():
        return {}

    # Peaks
    alg_params = {
        'DISCARD_NONMATCHING': True,
        'INPUT': outputs['FixGeometries']['OUTPUT'],
        'JOIN': outputs['Heights']['OUTPUT'],
        'JOIN_FIELDS': None,
        'PREDICATE': 1,
        'SUMMARIES': 3,
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['Peaks'] = processing.run('qgis:joinbylocationsummary', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(15)
    if feedback.isCanceled():
        return {}

    # PeaksnNDVI
    alg_params = {
        'DISCARD_NONMATCHING': True,
        'INPUT': outputs['Peaks']['OUTPUT'],
        'JOIN': outputs['MeanNdvi']['OUTPUT'],
        'JOIN_FIELDS': 'NDVI_mean',
        'METHOD': 1,
        'PREDICATE': 2,
        'PREFIX': '',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['Peaksnndvi'] = processing.run('qgis:joinattributesbylocation', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(16)
    if feedback.isCanceled():
        return {}

    # Add geometry attributes
    alg_params = {
        'CALC_METHOD': 0,
        'INPUT': outputs['Peaksnndvi']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['AddGeometryAttributes'] = processing.run('qgis:exportaddgeometrycolumns', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(17)
    if feedback.isCanceled():
        return {}

    # Extract by expression
    alg_params = {
        'EXPRESSION': 'NDVI_mean > 0.2 AND NDVI_mean IS NOT NULL AND Height_max > 1 AND Height_max IS NOT NULL AND area > 1',
        'INPUT': outputs['AddGeometryAttributes']['OUTPUT'],
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['ExtractByExpression'] = processing.run('native:extractbyexpression', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(18)
    if feedback.isCanceled():
        return {}

    # Join attributes by field value
    alg_params = {
        'DISCARD_NONMATCHING': True,
        'FIELD': 'Height',
        'FIELDS_TO_COPY': None,
        'FIELD_2': 'Height_max',
        'INPUT': outputs['Heights']['OUTPUT'],
        'INPUT_2': outputs['ExtractByExpression']['OUTPUT'],
        'METHOD': 1,
        'PREFIX': '',
        'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
    }
    outputs['JoinAttributesByFieldValue'] = processing.run('native:joinattributestable', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

    feedback.setCurrentStep(19)
    if feedback.isCanceled():
        return {}

    # Delete duplicates by attribute
    alg_params = {
        'FIELDS': 'NDVI_mean',
        'INPUT': outputs['JoinAttributesByFieldValue']['OUTPUT'],
        'OUTPUT': parameters['Finaltrees']
    }
    outputs['DeleteDuplicatesByAttribute'] = processing.run('native:removeduplicatesbyattribute', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
    results['Finaltrees'] = outputs['DeleteDuplicatesByAttribute']['OUTPUT']
    return results

def name(self):
    return 'trees'

def displayName(self):
    return 'trees'

def group(self):
    return ''

def groupId(self):
    return ''

def createInstance(self):
    return Trees()

Is this possible?

Taras
  • 32,823
  • 4
  • 66
  • 137

2 Answers2

1

With QGIS 3.4 and Windows 10 I have these deprecation warnings when running my standalone application :

C:\Program Files\QGIS 3.4\apps\qgis-ltr\python\qgis\utils.py:685: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  mod = _builtin_import(name, globals, locals, fromlist, level)
C:\Program Files\QGIS 3.4\apps\qgis-ltr\python\plugins\db_manager\db_plugins\postgis\plugin.py:350: DeprecationWarning: invalid escape sequence \:
  uri = u"raster:gdal:%s:%s" % (self.name, re.sub(":", "\:", self.gdalUri()))
C:\PROGRA~1\QGIS3~1.4\apps\Python37\lib\site-packages\PyQt5\uic\objcreator.py:152: DeprecationWarning: 'U' mode is deprecated
  plugin = open(filename, 'rU')

And everything works fine.

I guess we can rely on QGis and Pyqt teams to keep their libs up to date before the warnings become errors.

Djedouas
  • 131
  • 3
0

Your script has the definition of the processing algorithm class but in order for it to do something you need to instantiate the Trees class and use it.

Add this at the end of the file and then run it:

trees = Trees()
feedback = QgsProcessingFeedback()
context = QgsProcessingContext()
parameters = {} #add your parameters here
trees.initAlgorithm()
trees.processAlgorithm(parameters, context, feedback)
Leon Powałka
  • 1,653
  • 5
  • 18