Skip to content
Permalink
Browse files

Port Convex Hull alg to new API

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 e53a14a23eeb0da7b66b0346bfd4b33854b3b25b
@@ -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]
@@ -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'))
@@ -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'
@@ -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)
@@ -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
@@ -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)
@@ -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}
@@ -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
@@ -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
@@ -185,7 +185,7 @@ def __init__(self):

def getAlgs(self):
# algs = [
# ConvexHull(), FixedDistanceBuffer(),
# FixedDistanceBuffer(),
# VariableDistanceBuffer(),
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(),
@@ -221,6 +221,7 @@ def getAlgs(self):
BoundingBox(),
CheckValidity(),
ConcaveHull(),
ConvexHull(),
CreateAttributeIndex(),
CreateConstantRaster(),
Delaunay(),
@@ -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

0 comments on commit e53a14a

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