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

View File

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