Port a multi-step algorithm to new API (concave hull)

This commit is contained in:
Nyall Dawson 2017-07-01 18:00:57 +10:00
parent a15d283cd6
commit db816ec3fe
2 changed files with 50 additions and 34 deletions

View File

@ -32,14 +32,16 @@ from qgis.core import (QgsFeatureRequest,
QgsFeatureSink,
QgsWkbTypes,
QgsApplication,
QgsProcessingUtils)
QgsProcessingUtils,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterDefinition,
QgsProcessingParameterNumber,
QgsProcessingParameterBoolean,
QgsProcessingParameterFeatureSink,
QgsProcessingOutputVectorLayer)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterNumber
from processing.core.parameters import ParameterBoolean
from processing.core.outputs import OutputVector
from processing.tools import dataobjects
import processing
from math import sqrt
@ -57,17 +59,19 @@ class ConcaveHull(QgisAlgorithm):
def __init__(self):
super().__init__()
self.addParameter(ParameterVector(ConcaveHull.INPUT,
self.tr('Input point layer'), [dataobjects.TYPE_VECTOR_POINT]))
self.addParameter(ParameterNumber(self.ALPHA,
self.tr('Threshold (0-1, where 1 is equivalent with Convex Hull)'),
0, 1, 0.3))
self.addParameter(ParameterBoolean(self.HOLES,
self.tr('Allow holes'), True))
self.addParameter(ParameterBoolean(self.NO_MULTIGEOMETRY,
self.tr('Split multipart geometry into singleparts geometries'), False))
self.addOutput(
OutputVector(ConcaveHull.OUTPUT, self.tr('Concave hull'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, self.tr('Input point layer'), [QgsProcessingParameterDefinition.TypeVectorPoint]))
self.addParameter(QgsProcessingParameterNumber(self.ALPHA,
self.tr('Threshold (0-1, where 1 is equivalent with Convex Hull)'),
minValue=0, maxValue=1, defaultValue=0.3, type=QgsProcessingParameterNumber.Double))
self.addParameter(QgsProcessingParameterBoolean(self.HOLES,
self.tr('Allow holes'), defaultValue=True))
self.addParameter(QgsProcessingParameterBoolean(self.NO_MULTIGEOMETRY,
self.tr('Split multipart geometry into singleparts geometries'), defaultValue=False))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Concave hull'), type=QgsProcessingParameterDefinition.TypeVectorPolygon))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Concave hull"), type=QgsProcessingParameterDefinition.TypeVectorPolygon))
def name(self):
return 'concavehull'
@ -76,21 +80,21 @@ class ConcaveHull(QgisAlgorithm):
return self.tr('Concave hull')
def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(ConcaveHull.INPUT), context)
alpha = self.getParameterValue(self.ALPHA)
holes = self.getParameterValue(self.HOLES)
no_multigeom = self.getParameterValue(self.NO_MULTIGEOMETRY)
layer = self.parameterAsSource(parameters, ConcaveHull.INPUT, context)
alpha = self.parameterAsDouble(parameters, self.ALPHA, context)
holes = self.parameterAsBool(parameters, self.HOLES, context)
no_multigeom = self.parameterAsBool(parameters, self.NO_MULTIGEOMETRY, context)
# Delaunay triangulation from input point layer
feedback.setProgressText(self.tr('Creating Delaunay triangles...'))
delone_triangles = processing.run("qgis:delaunaytriangulation", layer, None, context=context)['OUTPUT']
delone_triangles = processing.run("qgis:delaunaytriangulation", {'INPUT': parameters[ConcaveHull.INPUT], 'OUTPUT': 'memory:'}, feedback=feedback, context=context)['OUTPUT']
delaunay_layer = QgsProcessingUtils.mapLayerFromString(delone_triangles, context)
# Get max edge length from Delaunay triangles
feedback.setProgressText(self.tr('Computing edges max length...'))
features = QgsProcessingUtils.getFeatures(delaunay_layer, context)
count = QgsProcessingUtils.featureCount(delaunay_layer, context)
features = delaunay_layer.getFeatures()
count = delaunay_layer.featureCount()
if count == 0:
raise GeoAlgorithmExecutionException(self.tr('No Delaunay triangles created.'))
@ -98,6 +102,9 @@ class ConcaveHull(QgisAlgorithm):
lengths = []
edges = {}
for feat in features:
if feedback.isCanceled():
break
line = feat.geometry().asPolygon()[0]
for i in range(len(line) - 1):
lengths.append(sqrt(line[i].sqrDist(line[i + 1])))
@ -111,6 +118,9 @@ class ConcaveHull(QgisAlgorithm):
i = 0
ids = []
for id, max_len in list(edges.items()):
if feedback.isCanceled():
break
if max_len > alpha * max_length:
ids.append(id)
feedback.setProgress(50 + i * counter)
@ -124,21 +134,25 @@ class ConcaveHull(QgisAlgorithm):
# Dissolve all Delaunay triangles
feedback.setProgressText(self.tr('Dissolving Delaunay triangles...'))
dissolved = processing.run("qgis:dissolve", delaunay_layer.id(),
True, None, None, context=context)['OUTPUT']
dissolved = processing.run("native:dissolve", {'INPUT': delaunay_layer.id(), 'OUTPUT': 'memory:'}, feedback=feedback, context=context)['OUTPUT']
dissolved_layer = QgsProcessingUtils.mapLayerFromString(dissolved, context)
# Save result
feedback.setProgressText(self.tr('Saving data...'))
feat = QgsFeature()
QgsProcessingUtils.getFeatures(dissolved_layer, context).nextFeature(feat)
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), QgsWkbTypes.Polygon,
layer.crs(), context)
dissolved_layer.getFeatures().nextFeature(feat)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
layer.fields(), QgsWkbTypes.Polygon, layer.sourceCrs())
geom = feat.geometry()
if no_multigeom and geom.isMultipart():
# Only singlepart geometries are allowed
geom_list = geom.asMultiPolygon()
for single_geom_list in geom_list:
if feedback.isCanceled():
break
single_feature = QgsFeature()
single_geom = QgsGeometry.fromPolygon(single_geom_list)
if not holes:
@ -147,7 +161,7 @@ class ConcaveHull(QgisAlgorithm):
while deleted:
deleted = single_geom.deleteRing(1)
single_feature.setGeometry(single_geom)
writer.addFeature(single_feature, QgsFeatureSink.FastInsert)
sink.addFeature(single_feature, QgsFeatureSink.FastInsert)
else:
# Multipart geometries are allowed
if not holes:
@ -155,5 +169,6 @@ class ConcaveHull(QgisAlgorithm):
deleted = True
while deleted:
deleted = geom.deleteRing(1)
writer.addFeature(feat, QgsFeatureSink.FastInsert)
del writer
sink.addFeature(feat, QgsFeatureSink.FastInsert)
return {self.OUTPUT: dest_id}

View File

@ -47,6 +47,7 @@ from .BasicStatistics import BasicStatisticsForField
from .Boundary import Boundary
from .BoundingBox import BoundingBox
from .CheckValidity import CheckValidity
from .ConcaveHull import ConcaveHull
from .CreateAttributeIndex import CreateAttributeIndex
from .Delaunay import Delaunay
from .DeleteColumn import DeleteColumn
@ -110,7 +111,6 @@ from .ZonalStatistics import ZonalStatistics
# from .HubDistanceLines import HubDistanceLines
# from .HubLines import HubLines
# from .GeometryConvert import GeometryConvert
# from .ConcaveHull import ConcaveHull
# from .RasterLayerStatistics import RasterLayerStatistics
# from .StatisticsByCategories import StatisticsByCategories
# from .EquivalentNumField import EquivalentNumField
@ -206,7 +206,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
# JoinAttributes(),
# Explode(), FieldsPyculator(),
# EquivalentNumField(),
# StatisticsByCategories(), ConcaveHull(),
# StatisticsByCategories(),
# RasterLayerStatistics(), PointsDisplacement(),
# PointsFromPolygons(),
# PointsFromLines(), RandomPointsExtent(),
@ -246,6 +246,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
Boundary(),
BoundingBox(),
CheckValidity(),
ConcaveHull(),
CreateAttributeIndex(),
Delaunay(),
DeleteColumn(),