Skip to content

Commit

Permalink
Port Convex Hull alg to new API
Browse files Browse the repository at this point in the history
Improvements:
- slight optimisation to feature requests - don't request attributes
which are not used
- Remove "method" param. Now the decision to group by field or
not is made only on whether a class field was selected or not
  • Loading branch information
nyalldawson committed Jul 28, 2017
1 parent c5cb3df commit e53a14a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 66 deletions.
79 changes: 41 additions & 38 deletions python/plugins/processing/algs/qgis/ConvexHull.py
Expand Up @@ -36,16 +36,16 @@
QgsFeatureSink,
QgsGeometry,
QgsWkbTypes,
QgsProcessingUtils,
QgsFeatureRequest,
QgsFields,
NULL)
NULL,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterField,
QgsProcessingParameterFeatureSink,
QgsProcessing,
QgsProcessingException)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterTableField
from processing.core.parameters import ParameterSelection
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
Expand All @@ -56,7 +56,6 @@ class ConvexHull(QgisAlgorithm):
INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
FIELD = 'FIELD'
METHOD = 'METHOD'

def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'convex_hull.png'))
Expand All @@ -68,17 +67,12 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.methods = [self.tr('Create single minimum convex hull'),
self.tr('Create convex hulls based on field')]

self.addParameter(ParameterVector(self.INPUT,
self.tr('Input layer')))
self.addParameter(ParameterTableField(self.FIELD,
self.tr('Field (optional, only used if creating convex hulls by classes)'),
self.INPUT, optional=True))
self.addParameter(ParameterSelection(self.METHOD,
self.tr('Method'), self.methods))
self.addOutput(OutputVector(self.OUTPUT, self.tr('Convex hull'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer')))
self.addParameter(QgsProcessingParameterField(self.FIELD,
self.tr('Field (optional, set if creating convex hulls by classes)'),
parentLayerParameterName=self.INPUT, optional=True))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Convex hull'), QgsProcessing.TypeVectorPolygon))

def name(self):
return 'convexhull'
Expand All @@ -87,14 +81,15 @@ def displayName(self):
return self.tr('Convex hull')

def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context)
useField = self.getParameterValue(self.METHOD) == 1
fieldName = self.getParameterValue(self.FIELD)
source = self.parameterAsSource(parameters, self.INPUT, context)
fieldName = self.parameterAsString(parameters, self.FIELD, context)
useField = bool(fieldName)

field_index = None
f = QgsField('value', QVariant.String, '', 255)
if useField:
index = layer.fields().lookupField(fieldName)
fType = layer.fields()[index].type()
field_index = source.fields().lookupField(fieldName)
fType = source.fields()[field_index].type()
if fType in [QVariant.Int, QVariant.UInt, QVariant.LongLong, QVariant.ULongLong]:
f.setType(fType)
f.setLength(20)
Expand All @@ -112,25 +107,30 @@ def processAlgorithm(self, parameters, context, feedback):
fields.append(QgsField('area', QVariant.Double, '', 20, 6))
fields.append(QgsField('perim', QVariant.Double, '', 20, 6))

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Polygon, source.sourceCrs())

outFeat = QgsFeature()
inGeom = QgsGeometry()
outGeom = QgsGeometry()

fid = 0
val = None
features = QgsProcessingUtils.getFeatures(layer, context)
if useField:
unique = layer.uniqueValues(index)
unique = source.uniqueValues(field_index)
current = 0
total = 100.0 / (layer.featureCount() * len(unique)) if layer.featureCount() else 1
total = 100.0 / (source.featureCount() * len(unique)) if source.featureCount() else 1
for i in unique:
if feedback.isCanceled():
break

first = True
hull = []
features = QgsProcessingUtils.getFeatures(layer, context)
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([field_index]))
for f in features:
idVar = f[fieldName]
if feedback.isCanceled():
break

idVar = f.attributes()[field_index]
if str(idVar).strip() == str(i).strip():
if first:
val = idVar
Expand All @@ -154,16 +154,19 @@ def processAlgorithm(self, parameters, context, feedback):
perim = NULL
outFeat.setGeometry(outGeom)
outFeat.setAttributes([fid, val, area, perim])
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('Exception while computing convex hull'))
fid += 1
else:
hull = []
total = 100.0 / layer.featureCount() if layer.featureCount() else 1
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / source.featureCount() if source.featureCount() else 1
features = source.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]))
for current, f in enumerate(features):
if feedback.isCanceled():
break

inGeom = f.geometry()
points = vector.extractPoints(inGeom)
hull.extend(points)
Expand All @@ -180,9 +183,9 @@ def processAlgorithm(self, parameters, context, feedback):
perim = NULL
outFeat.setGeometry(outGeom)
outFeat.setAttributes([0, 'all', area, perim])
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
raise GeoAlgorithmExecutionException(
raise QgsProcessingException(
self.tr('Exception while computing convex hull'))

del writer
return {self.OUTPUT: dest_id}
5 changes: 3 additions & 2 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -48,6 +48,7 @@
from .BoundingBox import BoundingBox
from .CheckValidity import CheckValidity
from .ConcaveHull import ConcaveHull
from .ConvexHull import ConvexHull
from .CreateAttributeIndex import CreateAttributeIndex
from .CreateConstantRaster import CreateConstantRaster
from .Delaunay import Delaunay
Expand Down Expand Up @@ -131,7 +132,6 @@
from .ZonalStatistics import ZonalStatistics

# from .ExtractByLocation import ExtractByLocation
# from .ConvexHull import ConvexHull
# from .FixedDistanceBuffer import FixedDistanceBuffer
# from .VariableDistanceBuffer import VariableDistanceBuffer
# from .RandomSelection import RandomSelection
Expand Down Expand Up @@ -185,7 +185,7 @@ def __init__(self):

def getAlgs(self):
# algs = [
# ConvexHull(), FixedDistanceBuffer(),
# FixedDistanceBuffer(),
# VariableDistanceBuffer(),
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(),
Expand Down Expand Up @@ -221,6 +221,7 @@ def getAlgs(self):
BoundingBox(),
CheckValidity(),
ConcaveHull(),
ConvexHull(),
CreateAttributeIndex(),
CreateConstantRaster(),
Delaunay(),
Expand Down
50 changes: 24 additions & 26 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -2164,32 +2164,30 @@ tests:
# OUTPUT_LAYER:
# name: expected/join_attribute_table.gml
# type: vector
#
# - algorithm: qgis:convexhull
# name: Simple convex hull
# params:
# INPUT:
# name: custom/points_hull.gml
# type: vector
# METHOD: '0'
# results:
# OUTPUT:
# name: expected/convex_hull.gml
# type: vector
#
# - algorithm: qgis:convexhull
# name: Convex hull based on field attribute
# params:
# FIELD: hull
# INPUT:
# name: custom/points_hull.gml
# type: vector
# METHOD: '1'
# results:
# OUTPUT:
# name: expected/convex_hull_fields.gml
# type: vector
#

- algorithm: qgis:convexhull
name: Simple convex hull
params:
INPUT:
name: custom/points_hull.gml
type: vector
results:
OUTPUT:
name: expected/convex_hull.gml
type: vector

- algorithm: qgis:convexhull
name: Convex hull based on field attribute
params:
FIELD: hull
INPUT:
name: custom/points_hull.gml
type: vector
results:
OUTPUT:
name: expected/convex_hull_fields.gml
type: vector

# # These tests dissabled because algs require access to iface which
# # is not available in the test suite.
# #- algorithm: qgis:shortestpathpointtopoint
Expand Down

0 comments on commit e53a14a

Please sign in to comment.