Skip to content
Permalink
Browse files

Merge pull request #5050 from nyalldawson/algs

Port more QGIS algs to new API
  • Loading branch information
nyalldawson committed Aug 21, 2017
2 parents 2530d9e + bcc6627 commit 4c5260771907e301403ea0216e69a36f9afdc2ec
Showing with 995 additions and 756 deletions.
  1. +13 −18 python/plugins/processing/algs/qgis/DefineProjection.py
  2. +44 −56 python/plugins/processing/algs/qgis/ExecuteSQL.py
  3. +50 −50 python/plugins/processing/algs/qgis/FieldPyculator.py
  4. +71 −83 python/plugins/processing/algs/qgis/FieldsCalculator.py
  5. +50 −43 python/plugins/processing/algs/qgis/FindProjection.py
  6. +154 −148 python/plugins/processing/algs/qgis/GeometryConvert.py
  7. +15 −13 python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
  8. +55 −44 python/plugins/processing/algs/qgis/RasterCalculator.py
  9. +23 −11 python/plugins/processing/algs/qgis/ui/FieldsCalculatorDialog.py
  10. +8 −2 python/plugins/processing/gui/TestTools.py
  11. +1 −1 python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gfs
  12. +6 −0 python/plugins/processing/tests/testdata/expected/convert_poly_centroid.gml
  13. +1 −1 python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gfs
  14. +6 −0 python/plugins/processing/tests/testdata/expected/convert_poly_multiline.gml
  15. +3 −3 python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs
  16. +8 −205 python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gml
  17. +26 −0 python/plugins/processing/tests/testdata/expected/execute_sql.gfs
  18. +28 −0 python/plugins/processing/tests/testdata/expected/execute_sql.gml
  19. +31 −0 python/plugins/processing/tests/testdata/expected/field_calculator_points.gfs
  20. +86 −0 python/plugins/processing/tests/testdata/expected/field_calculator_points.gml
  21. +16 −0 python/plugins/processing/tests/testdata/expected/projection_candidates.gfs
  22. +49 −0 python/plugins/processing/tests/testdata/expected/projection_candidates.gml
  23. +31 −0 python/plugins/processing/tests/testdata/expected/pycalculator_points.gfs
  24. +86 −0 python/plugins/processing/tests/testdata/expected/pycalculator_points.gml
  25. +129 −78 python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
  26. +4 −0 src/core/processing/qgsprocessingparameters.cpp
  27. +1 −0 tests/src/core/testqgsprocessing.cpp
@@ -28,15 +28,12 @@
import os
import re

from qgis.core import (QgsCoordinateReferenceSystem,
QgsApplication,
QgsProcessingUtils)
from qgis.utils import iface
from qgis.core import (QgsProcessing,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterCrs,
QgsProcessingOutputVectorLayer)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterCrs
from processing.core.outputs import OutputVector

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]

@@ -45,7 +42,6 @@ class DefineProjection(QgisAlgorithm):

INPUT = 'INPUT'
CRS = 'CRS'
OUTPUT = 'OUTPUT'

def group(self):
return self.tr('Vector general tools')
@@ -54,11 +50,11 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT,
self.tr('Input Layer')))
self.addParameter(ParameterCrs(self.CRS, 'Output CRS'))
self.addOutput(OutputVector(self.OUTPUT,
self.tr('Layer with projection'), True))
self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT,
self.tr('Input Layer'), types=[QgsProcessing.TypeVectorAnyGeometry]))
self.addParameter(QgsProcessingParameterCrs(self.CRS, 'Output CRS'))
self.addOutput(QgsProcessingOutputVectorLayer(self.INPUT,
self.tr('Layer with projection')))

def name(self):
return 'definecurrentprojection'
@@ -67,9 +63,8 @@ def displayName(self):
return self.tr('Define current projection')

def processAlgorithm(self, parameters, context, feedback):
fileName = self.getParameterValue(self.INPUT)
layer = QgsProcessingUtils.mapLayerFromString(fileName, context)
crs = QgsCoordinateReferenceSystem(self.getParameterValue(self.CRS))
layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
crs = self.parameterAsCrs(parameters, self.CRS, context)

provider = layer.dataProvider()
ds = provider.dataSourceUri()
@@ -89,6 +84,6 @@ def processAlgorithm(self, parameters, context, feedback):
f.write(wkt)

layer.setCrs(crs)
iface.mapCanvas().refresh()
layer.triggerRepaint()

self.setOutputValue(self.OUTPUT, fileName)
return {self.INPUT: layer}
@@ -25,21 +25,18 @@

__revision__ = '$Format:%H$'

from qgis.core import (QgsFeature,
QgsVirtualLayerDefinition,
from qgis.core import (QgsVirtualLayerDefinition,
QgsVectorLayer,
QgsCoordinateReferenceSystem,
QgsWkbTypes,
QgsApplication,
QgsProcessingUtils)
QgsProcessingParameterMultipleLayers,
QgsProcessingParameterString,
QgsProcessingParameterEnum,
QgsProcessingParameterCrs,
QgsProcessingParameterFeatureSink,
QgsFeatureSink,
QgsProcessingException)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterString
from processing.core.parameters import ParameterMultipleInput
from processing.core.parameters import ParameterCrs
from processing.core.parameters import ParameterSelection
from processing.core.outputs import OutputVector


class ExecuteSQL(QgisAlgorithm):
@@ -54,7 +51,7 @@ class ExecuteSQL(QgisAlgorithm):
INPUT_GEOMETRY_FIELD = 'INPUT_GEOMETRY_FIELD'
INPUT_GEOMETRY_TYPE = 'INPUT_GEOMETRY_TYPE'
INPUT_GEOMETRY_CRS = 'INPUT_GEOMETRY_CRS'
OUTPUT_LAYER = 'OUTPUT_LAYER'
OUTPUT = 'OUTPUT'

def group(self):
return self.tr('Vector general tools')
@@ -63,19 +60,19 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterMultipleInput(name=self.INPUT_DATASOURCES,
description=self.tr('Additional input datasources (called input1, .., inputN in the query)'),
optional=True))
self.addParameter(QgsProcessingParameterMultipleLayers(name=self.INPUT_DATASOURCES,
description=self.tr('Additional input datasources (called input1, .., inputN in the query)'),
optional=True))

self.addParameter(ParameterString(name=self.INPUT_QUERY,
description=self.tr('SQL query'),
multiline=True))
self.addParameter(QgsProcessingParameterString(name=self.INPUT_QUERY,
description=self.tr('SQL query'),
multiLine=True))

self.addParameter(ParameterString(name=self.INPUT_UID_FIELD,
description=self.tr('Unique identifier field'), optional=True))
self.addParameter(QgsProcessingParameterString(name=self.INPUT_UID_FIELD,
description=self.tr('Unique identifier field'), optional=True))

self.addParameter(ParameterString(name=self.INPUT_GEOMETRY_FIELD,
description=self.tr('Geometry field'), optional=True))
self.addParameter(QgsProcessingParameterString(name=self.INPUT_GEOMETRY_FIELD,
description=self.tr('Geometry field'), optional=True))

self.geometryTypes = [
self.tr('Autodetect'),
@@ -86,13 +83,13 @@ def initAlgorithm(self, config=None):
'MultiPoint',
'MultiLineString',
'MultiPolygon']
self.addParameter(ParameterSelection(self.INPUT_GEOMETRY_TYPE,
self.tr('Geometry type'), self.geometryTypes, optional=True))
self.addParameter(QgsProcessingParameterEnum(self.INPUT_GEOMETRY_TYPE,
self.tr('Geometry type'), options=self.geometryTypes, optional=True))

self.addParameter(ParameterCrs(self.INPUT_GEOMETRY_CRS,
self.tr('CRS'), optional=True))
self.addParameter(QgsProcessingParameterCrs(self.INPUT_GEOMETRY_CRS,
self.tr('CRS'), optional=True))

self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('SQL Output')))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('SQL Output')))

def name(self):
return 'executesql'
@@ -101,24 +98,19 @@ def displayName(self):
return self.tr('Execute SQL')

def processAlgorithm(self, parameters, context, feedback):
layers = self.getParameterValue(self.INPUT_DATASOURCES)
query = self.getParameterValue(self.INPUT_QUERY)
uid_field = self.getParameterValue(self.INPUT_UID_FIELD)
geometry_field = self.getParameterValue(self.INPUT_GEOMETRY_FIELD)
geometry_type = self.getParameterValue(self.INPUT_GEOMETRY_TYPE)
geometry_crs = self.getParameterValue(self.INPUT_GEOMETRY_CRS)
layers = self.parameterAsLayerList(parameters, self.INPUT_DATASOURCES, context)
query = self.parameterAsString(parameters, self.INPUT_QUERY, context)
uid_field = self.parameterAsString(parameters, self.INPUT_UID_FIELD, context)
geometry_field = self.parameterAsString(parameters, self.INPUT_GEOMETRY_FIELD, context)
geometry_type = self.parameterAsEnum(parameters, self.INPUT_GEOMETRY_TYPE, context)
geometry_crs = self.parameterAsCrs(parameters, self.INPUT_GEOMETRY_CRS, context)

df = QgsVirtualLayerDefinition()
layerIdx = 1
if layers:
for layerSource in layers.split(';'):
layer = QgsProcessingUtils.mapLayerFromString(layerSource, context)
if layer:
df.addSource('input{}'.format(layerIdx), layer.id())
layerIdx += 1
for layerIdx, layer in enumerate(layers):
df.addSource('input{}'.format(layerIdx + 1), layer.id())

if query == '':
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('Empty SQL. Please enter valid SQL expression and try again.'))
else:
df.setQuery(query)
@@ -133,26 +125,22 @@ def processAlgorithm(self, parameters, context, feedback):
df.setGeometryField(geometry_field)
if geometry_type > 1:
df.setGeometryWkbType(geometry_type - 1)
if geometry_crs:
crs = QgsCoordinateReferenceSystem(geometry_crs)
if crs.isValid():
df.setGeometrySrid(crs.postgisSrid())
if geometry_crs.isValid():
df.setGeometrySrid(geometry_crs.postgisSrid())

vLayer = QgsVectorLayer(df.toString(), "temp_vlayer", "virtual")
if not vLayer.isValid():
raise GeoAlgorithmExecutionException(vLayer.dataProvider().error().message())
raise QgsProcessingException(vLayer.dataProvider().error().message())

writer = self.getOutputFromName(self.OUTPUT_LAYER).getVectorWriter(vLayer.fields(),
vLayer.wkbType() if geometry_type != 1 else 1,
vLayer.crs(), context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
vLayer.fields(), vLayer.wkbType() if geometry_type != 1 else 1, vLayer.crs())

features = QgsProcessingUtils.getFeatures(vLayer, context)
features = vLayer.getFeatures()
total = 100.0 / vLayer.featureCount() if vLayer.featureCount() else 0
outFeat = QgsFeature()
for current, inFeat in enumerate(features):
outFeat.setAttributes(inFeat.attributes())
if geometry_type != 1:
outFeat.setGeometry(inFeat.geometry())
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
if feedback.isCanceled():
break

sink.addFeature(inFeat, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
del writer
return {self.OUTPUT: dest_id}
@@ -29,30 +29,27 @@
import sys

from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsFeature,
from qgis.core import (QgsProcessingException,
QgsField,
QgsFeatureSink,
QgsApplication,
QgsProcessingUtils)
QgsProcessingParameterFeatureSource,
QgsProcessingParameterString,
QgsProcessingParameterEnum,
QgsProcessingParameterNumber,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterString
from processing.core.parameters import ParameterNumber
from processing.core.parameters import ParameterSelection
from processing.core.outputs import OutputVector


class FieldsPyculator(QgisAlgorithm):

INPUT_LAYER = 'INPUT_LAYER'
INPUT = 'INPUT'
FIELD_NAME = 'FIELD_NAME'
FIELD_TYPE = 'FIELD_TYPE'
FIELD_LENGTH = 'FIELD_LENGTH'
FIELD_PRECISION = 'FIELD_PRECISION'
GLOBAL = 'GLOBAL'
FORMULA = 'FORMULA'
OUTPUT_LAYER = 'OUTPUT_LAYER'
OUTPUT = 'OUTPUT'
RESULT_VAR_NAME = 'value'

TYPES = [QVariant.Int, QVariant.Double, QVariant.String]
@@ -68,21 +65,21 @@ def initAlgorithm(self, config=None):
self.tr('Float'),
self.tr('String')]

self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer')))
self.addParameter(ParameterString(self.FIELD_NAME,
self.tr('Result field name'), 'NewField'))
self.addParameter(ParameterSelection(self.FIELD_TYPE,
self.tr('Field type'), self.type_names))
self.addParameter(ParameterNumber(self.FIELD_LENGTH,
self.tr('Field length'), 1, 255, 10))
self.addParameter(ParameterNumber(self.FIELD_PRECISION,
self.tr('Field precision'), 0, 10, 0))
self.addParameter(ParameterString(self.GLOBAL,
self.tr('Global expression'), multiline=True, optional=True))
self.addParameter(ParameterString(self.FORMULA,
self.tr('Formula'), 'value = ', multiline=True))
self.addOutput(OutputVector(self.OUTPUT_LAYER, self.tr('Calculated')))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input layer')))
self.addParameter(QgsProcessingParameterString(self.FIELD_NAME,
self.tr('Result field name'), defaultValue='NewField'))
self.addParameter(QgsProcessingParameterEnum(self.FIELD_TYPE,
self.tr('Field type'), options=self.type_names))
self.addParameter(QgsProcessingParameterNumber(self.FIELD_LENGTH,
self.tr('Field length'), minValue=1, maxValue=255, defaultValue=10))
self.addParameter(QgsProcessingParameterNumber(self.FIELD_PRECISION,
self.tr('Field precision'), minValue=0, maxValue=15, defaultValue=3))
self.addParameter(QgsProcessingParameterString(self.GLOBAL,
self.tr('Global expression'), multiLine=True, optional=True))
self.addParameter(QgsProcessingParameterString(self.FORMULA,
self.tr('Formula'), defaultValue='value = ', multiLine=True))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
self.tr('Calculated')))

def name(self):
return 'advancedpythonfieldcalculator'
@@ -91,33 +88,33 @@ def displayName(self):
return self.tr('Advanced Python field calculator')

def processAlgorithm(self, parameters, context, feedback):
fieldName = self.getParameterValue(self.FIELD_NAME)
fieldType = self.getParameterValue(self.FIELD_TYPE)
fieldLength = self.getParameterValue(self.FIELD_LENGTH)
fieldPrecision = self.getParameterValue(self.FIELD_PRECISION)
code = self.getParameterValue(self.FORMULA)
globalExpression = self.getParameterValue(self.GLOBAL)
output = self.getOutputFromName(self.OUTPUT_LAYER)

layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
fields = layer.fields()
fields.append(QgsField(fieldName, self.TYPES[fieldType], '',
fieldLength, fieldPrecision))
writer = output.getVectorWriter(fields, layer.wkbType(), layer.crs(), context)
outFeat = QgsFeature()
source = self.parameterAsSource(parameters, self.INPUT, context)
field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
field_type = self.TYPES[self.parameterAsEnum(parameters, self.FIELD_TYPE, context)]
width = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)
code = self.parameterAsString(parameters, self.FORMULA, context)
globalExpression = self.parameterAsString(parameters, self.GLOBAL, context)

fields = source.fields()
fields.append(QgsField(field_name, self.TYPES[field_type], '',
width, precision))
new_ns = {}

(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, source.wkbType(), source.sourceCrs())

# Run global code
if globalExpression.strip() != '':
try:
bytecode = compile(globalExpression, '<string>', 'exec')
exec(bytecode, new_ns)
except:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr("FieldPyculator code execute error.Global code block can't be executed!\n{0}\n{1}").format(str(sys.exc_info()[0].__name__), str(sys.exc_info()[1])))

# Replace all fields tags
fields = layer.fields()
fields = source.fields()
num = 0
for field in fields:
field_name = str(field.name())
@@ -136,13 +133,17 @@ def processAlgorithm(self, parameters, context, feedback):
try:
bytecode = compile(code, '<string>', 'exec')
except:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr("FieldPyculator code execute error. Field code block can't be executed!\n{0}\n{1}").format(str(sys.exc_info()[0].__name__), str(sys.exc_info()[1])))

# Run
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0

for current, feat in enumerate(features):
if feedback.isCanceled():
break

feedback.setProgress(int(current * total))
attrs = feat.attributes()
feat_id = feat.id()
@@ -168,18 +169,17 @@ def processAlgorithm(self, parameters, context, feedback):

# Check result
if self.RESULT_VAR_NAME not in new_ns:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr("FieldPyculator code execute error\n"
"Field code block does not return '{0}' variable! "
"Please declare this variable in your code!").format(self.RESULT_VAR_NAME))

# Write feature
outFeat.setGeometry(feat.geometry())
attrs.append(new_ns[self.RESULT_VAR_NAME])
outFeat.setAttributes(attrs)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
feat.setAttributes(attrs)
sink.addFeature(feat, QgsFeatureSink.FastInsert)

del writer
return {self.OUTPUT: dest_id}

def checkParameterValues(self, parameters, context):
# TODO check that formula is correct and fields exist

0 comments on commit 4c52607

Please sign in to comment.
You can’t perform that action at this time.