Skip to content

Commit db816ec

Browse files
committedJul 6, 2017
Port a multi-step algorithm to new API (concave hull)
1 parent a15d283 commit db816ec

File tree

2 files changed

+50
-34
lines changed

2 files changed

+50
-34
lines changed
 

‎python/plugins/processing/algs/qgis/ConcaveHull.py

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,16 @@
3232
QgsFeatureSink,
3333
QgsWkbTypes,
3434
QgsApplication,
35-
QgsProcessingUtils)
35+
QgsProcessingUtils,
36+
QgsProcessingParameterFeatureSource,
37+
QgsProcessingParameterVectorLayer,
38+
QgsProcessingParameterDefinition,
39+
QgsProcessingParameterNumber,
40+
QgsProcessingParameterBoolean,
41+
QgsProcessingParameterFeatureSink,
42+
QgsProcessingOutputVectorLayer)
3643
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
3744
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
38-
from processing.core.parameters import ParameterVector
39-
from processing.core.parameters import ParameterNumber
40-
from processing.core.parameters import ParameterBoolean
41-
from processing.core.outputs import OutputVector
42-
from processing.tools import dataobjects
4345
import processing
4446
from math import sqrt
4547

@@ -57,17 +59,19 @@ def group(self):
5759

5860
def __init__(self):
5961
super().__init__()
60-
self.addParameter(ParameterVector(ConcaveHull.INPUT,
61-
self.tr('Input point layer'), [dataobjects.TYPE_VECTOR_POINT]))
62-
self.addParameter(ParameterNumber(self.ALPHA,
63-
self.tr('Threshold (0-1, where 1 is equivalent with Convex Hull)'),
64-
0, 1, 0.3))
65-
self.addParameter(ParameterBoolean(self.HOLES,
66-
self.tr('Allow holes'), True))
67-
self.addParameter(ParameterBoolean(self.NO_MULTIGEOMETRY,
68-
self.tr('Split multipart geometry into singleparts geometries'), False))
69-
self.addOutput(
70-
OutputVector(ConcaveHull.OUTPUT, self.tr('Concave hull'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
62+
63+
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input point layer'), [QgsProcessingParameterDefinition.TypeVectorPoint]))
64+
self.addParameter(QgsProcessingParameterNumber(self.ALPHA,
65+
self.tr('Threshold (0-1, where 1 is equivalent with Convex Hull)'),
66+
minValue=0, maxValue=1, defaultValue=0.3, type=QgsProcessingParameterNumber.Double))
67+
68+
self.addParameter(QgsProcessingParameterBoolean(self.HOLES,
69+
self.tr('Allow holes'), defaultValue=True))
70+
self.addParameter(QgsProcessingParameterBoolean(self.NO_MULTIGEOMETRY,
71+
self.tr('Split multipart geometry into singleparts geometries'), defaultValue=False))
72+
73+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Concave hull'), type=QgsProcessingParameterDefinition.TypeVectorPolygon))
74+
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Concave hull"), type=QgsProcessingParameterDefinition.TypeVectorPolygon))
7175

7276
def name(self):
7377
return 'concavehull'
@@ -76,28 +80,31 @@ def displayName(self):
7680
return self.tr('Concave hull')
7781

7882
def processAlgorithm(self, parameters, context, feedback):
79-
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(ConcaveHull.INPUT), context)
80-
alpha = self.getParameterValue(self.ALPHA)
81-
holes = self.getParameterValue(self.HOLES)
82-
no_multigeom = self.getParameterValue(self.NO_MULTIGEOMETRY)
83+
layer = self.parameterAsSource(parameters, ConcaveHull.INPUT, context)
84+
alpha = self.parameterAsDouble(parameters, self.ALPHA, context)
85+
holes = self.parameterAsBool(parameters, self.HOLES, context)
86+
no_multigeom = self.parameterAsBool(parameters, self.NO_MULTIGEOMETRY, context)
8387

8488
# Delaunay triangulation from input point layer
8589
feedback.setProgressText(self.tr('Creating Delaunay triangles...'))
86-
delone_triangles = processing.run("qgis:delaunaytriangulation", layer, None, context=context)['OUTPUT']
90+
delone_triangles = processing.run("qgis:delaunaytriangulation", {'INPUT': parameters[ConcaveHull.INPUT], 'OUTPUT': 'memory:'}, feedback=feedback, context=context)['OUTPUT']
8791
delaunay_layer = QgsProcessingUtils.mapLayerFromString(delone_triangles, context)
8892

8993
# Get max edge length from Delaunay triangles
9094
feedback.setProgressText(self.tr('Computing edges max length...'))
9195

92-
features = QgsProcessingUtils.getFeatures(delaunay_layer, context)
93-
count = QgsProcessingUtils.featureCount(delaunay_layer, context)
96+
features = delaunay_layer.getFeatures()
97+
count = delaunay_layer.featureCount()
9498
if count == 0:
9599
raise GeoAlgorithmExecutionException(self.tr('No Delaunay triangles created.'))
96100

97101
counter = 50. / count
98102
lengths = []
99103
edges = {}
100104
for feat in features:
105+
if feedback.isCanceled():
106+
break
107+
101108
line = feat.geometry().asPolygon()[0]
102109
for i in range(len(line) - 1):
103110
lengths.append(sqrt(line[i].sqrDist(line[i + 1])))
@@ -111,6 +118,9 @@ def processAlgorithm(self, parameters, context, feedback):
111118
i = 0
112119
ids = []
113120
for id, max_len in list(edges.items()):
121+
if feedback.isCanceled():
122+
break
123+
114124
if max_len > alpha * max_length:
115125
ids.append(id)
116126
feedback.setProgress(50 + i * counter)
@@ -124,21 +134,25 @@ def processAlgorithm(self, parameters, context, feedback):
124134

125135
# Dissolve all Delaunay triangles
126136
feedback.setProgressText(self.tr('Dissolving Delaunay triangles...'))
127-
dissolved = processing.run("qgis:dissolve", delaunay_layer.id(),
128-
True, None, None, context=context)['OUTPUT']
137+
dissolved = processing.run("native:dissolve", {'INPUT': delaunay_layer.id(), 'OUTPUT': 'memory:'}, feedback=feedback, context=context)['OUTPUT']
129138
dissolved_layer = QgsProcessingUtils.mapLayerFromString(dissolved, context)
130139

131140
# Save result
132141
feedback.setProgressText(self.tr('Saving data...'))
133142
feat = QgsFeature()
134-
QgsProcessingUtils.getFeatures(dissolved_layer, context).nextFeature(feat)
135-
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), QgsWkbTypes.Polygon,
136-
layer.crs(), context)
143+
dissolved_layer.getFeatures().nextFeature(feat)
144+
145+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
146+
layer.fields(), QgsWkbTypes.Polygon, layer.sourceCrs())
147+
137148
geom = feat.geometry()
138149
if no_multigeom and geom.isMultipart():
139150
# Only singlepart geometries are allowed
140151
geom_list = geom.asMultiPolygon()
141152
for single_geom_list in geom_list:
153+
if feedback.isCanceled():
154+
break
155+
142156
single_feature = QgsFeature()
143157
single_geom = QgsGeometry.fromPolygon(single_geom_list)
144158
if not holes:
@@ -147,13 +161,14 @@ def processAlgorithm(self, parameters, context, feedback):
147161
while deleted:
148162
deleted = single_geom.deleteRing(1)
149163
single_feature.setGeometry(single_geom)
150-
writer.addFeature(single_feature, QgsFeatureSink.FastInsert)
164+
sink.addFeature(single_feature, QgsFeatureSink.FastInsert)
151165
else:
152166
# Multipart geometries are allowed
153167
if not holes:
154168
# Delete holes
155169
deleted = True
156170
while deleted:
157171
deleted = geom.deleteRing(1)
158-
writer.addFeature(feat, QgsFeatureSink.FastInsert)
159-
del writer
172+
sink.addFeature(feat, QgsFeatureSink.FastInsert)
173+
174+
return {self.OUTPUT: dest_id}

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from .Boundary import Boundary
4848
from .BoundingBox import BoundingBox
4949
from .CheckValidity import CheckValidity
50+
from .ConcaveHull import ConcaveHull
5051
from .CreateAttributeIndex import CreateAttributeIndex
5152
from .Delaunay import Delaunay
5253
from .DeleteColumn import DeleteColumn
@@ -110,7 +111,6 @@
110111
# from .HubDistanceLines import HubDistanceLines
111112
# from .HubLines import HubLines
112113
# from .GeometryConvert import GeometryConvert
113-
# from .ConcaveHull import ConcaveHull
114114
# from .RasterLayerStatistics import RasterLayerStatistics
115115
# from .StatisticsByCategories import StatisticsByCategories
116116
# from .EquivalentNumField import EquivalentNumField
@@ -206,7 +206,7 @@ def getAlgs(self):
206206
# JoinAttributes(),
207207
# Explode(), FieldsPyculator(),
208208
# EquivalentNumField(),
209-
# StatisticsByCategories(), ConcaveHull(),
209+
# StatisticsByCategories(),
210210
# RasterLayerStatistics(), PointsDisplacement(),
211211
# PointsFromPolygons(),
212212
# PointsFromLines(), RandomPointsExtent(),
@@ -246,6 +246,7 @@ def getAlgs(self):
246246
Boundary(),
247247
BoundingBox(),
248248
CheckValidity(),
249+
ConcaveHull(),
249250
CreateAttributeIndex(),
250251
Delaunay(),
251252
DeleteColumn(),

0 commit comments

Comments
 (0)
Please sign in to comment.