mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-16 00:03:12 -04:00
Port a multi-step algorithm to new API (concave hull)
This commit is contained in:
parent
a15d283cd6
commit
db816ec3fe
@ -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}
|
||||||
|
@ -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(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user