From 31167718cf05434f7b347b68924bfe69bc64a34f Mon Sep 17 00:00:00 2001 From: Nyall Dawson Date: Wed, 21 Jun 2017 22:11:31 +1000 Subject: [PATCH] Resurrect some processing algs, port multipart to singlepart to c++ --- .../processing/algs/qgis/DeleteHoles.py | 52 ++++---- .../processing/algs/qgis/DensifyGeometries.py | 50 ++++---- .../algs/qgis/DensifyGeometriesInterval.py | 48 ++++---- .../processing/algs/qgis/DropGeometry.py | 36 +++--- .../algs/qgis/QGISAlgorithmProvider.py | 17 +-- .../tests/testdata/qgis_algorithm_tests.yaml | 116 +++++++++--------- src/core/processing/qgsnativealgorithms.cpp | 89 +++++++++++++- src/core/processing/qgsnativealgorithms.h | 23 ++++ 8 files changed, 269 insertions(+), 162 deletions(-) diff --git a/python/plugins/processing/algs/qgis/DeleteHoles.py b/python/plugins/processing/algs/qgis/DeleteHoles.py index 1d994851d3a..1aecf4f5324 100644 --- a/python/plugins/processing/algs/qgis/DeleteHoles.py +++ b/python/plugins/processing/algs/qgis/DeleteHoles.py @@ -25,12 +25,13 @@ __copyright__ = '(C) 2015, Etienne Trimaille' __revision__ = '$Format:%H$' from qgis.core import (QgsApplication, - QgsProcessingUtils) + QgsProcessingUtils, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterNumber, + QgsProcessingParameterFeatureSink, + QgsProcessingOutputVectorLayer, + QgsProcessingParameterDefinition) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import (ParameterVector, - ParameterNumber) -from processing.core.outputs import OutputVector -from processing.tools import dataobjects class DeleteHoles(QgisAlgorithm): @@ -53,12 +54,14 @@ class DeleteHoles(QgisAlgorithm): def __init__(self): super().__init__() - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON])) - self.addParameter(ParameterNumber(self.MIN_AREA, - self.tr('Remove holes with area less than'), 0, 10000000.0, default=0.0, optional=True)) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), [QgsProcessingParameterDefinition.TypeVectorPolygon])) + self.addParameter(QgsProcessingParameterNumber(self.MIN_AREA, + self.tr('Remove holes with area less than'), QgsProcessingParameterNumber.Double, + 0, True, 0.0, 10000000.0)) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Cleaned'), datatype=[dataobjects.TYPE_VECTOR_POLYGON])) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Cleaned'), QgsProcessingParameterDefinition.TypeVectorPolygon)) + self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Cleaned'), QgsProcessingParameterDefinition.TypeVectorPolygon)) def name(self): return 'deleteholes' @@ -67,29 +70,24 @@ class DeleteHoles(QgisAlgorithm): return self.tr('Delete holes') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - min_area = self.getParameterValue(self.MIN_AREA) - if min_area is not None: - try: - min_area = float(min_area) - except: - pass + source = self.parameterAsSource(parameters, self.INPUT, context) + min_area = self.parameterAsDouble(parameters, self.MIN_AREA, context) if min_area == 0.0: min_area = -1.0 - writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), - context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + source.fields(), source.wkbType(), source.sourceCrs()) - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / QgsProcessingUtils.featureCount(layer, context) + features = source.getFeatures() + total = 100.0 / source.featureCount() for current, f in enumerate(features): + if feedback.isCanceled(): + break + if f.hasGeometry(): - if min_area is not None: - f.setGeometry(f.geometry().removeInteriorRings(min_area)) - else: - f.setGeometry(f.geometry().removeInteriorRings()) - writer.addFeature(f) + f.setGeometry(f.geometry().removeInteriorRings(min_area)) + sink.addFeature(f) feedback.setProgress(int(current * total)) - del writer + return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/DensifyGeometries.py b/python/plugins/processing/algs/qgis/DensifyGeometries.py index 3f2122cb8d6..223ede5510c 100644 --- a/python/plugins/processing/algs/qgis/DensifyGeometries.py +++ b/python/plugins/processing/algs/qgis/DensifyGeometries.py @@ -30,15 +30,12 @@ import os from qgis.core import (QgsWkbTypes, QgsApplication, - QgsProcessingUtils) - + QgsProcessingParameterFeatureSource, + QgsProcessingParameterNumber, + QgsProcessingParameterFeatureSink, + QgsProcessingOutputVectorLayer, + QgsProcessingParameterDefinition) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputVector -from processing.tools import dataobjects - -pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] class DensifyGeometries(QgisAlgorithm): @@ -61,14 +58,15 @@ class DensifyGeometries(QgisAlgorithm): def __init__(self): super().__init__() - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'), - [dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_LINE])) - self.addParameter(ParameterNumber(self.VERTICES, - self.tr('Vertices to add'), 1, 10000000, 1)) - self.addOutput(OutputVector(self.OUTPUT, - self.tr('Densified'))) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), [QgsProcessingParameterDefinition.TypeVectorPolygon, QgsProcessingParameterDefinition.TypeVectorLine])) + self.addParameter(QgsProcessingParameterNumber(self.VERTICES, + self.tr('Vertices to add'), QgsProcessingParameterNumber.Integer, + 1, False, 1, 10000000)) + + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Densified'))) + self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Densified'))) def name(self): return 'densifygeometries' @@ -77,22 +75,24 @@ class DensifyGeometries(QgisAlgorithm): return self.tr('Densify geometries') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - vertices = self.getParameterValue(self.VERTICES) + source = self.parameterAsSource(parameters, self.INPUT, context) + vertices = self.parameterAsInt(parameters, self.VERTICES, context) - isPolygon = layer.geometryType() == QgsWkbTypes.PolygonGeometry + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + source.fields(), source.wkbType(), source.sourceCrs()) - writer = self.getOutputFromName( - self.OUTPUT).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context) + features = source.getFeatures() + total = 100.0 / source.featureCount() - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / QgsProcessingUtils.featureCount(layer, context) for current, f in enumerate(features): + if feedback.isCanceled(): + break + feature = f if feature.hasGeometry(): - new_geometry = feature.geometry().densifyByCount(int(vertices)) + new_geometry = feature.geometry().densifyByCount(vertices) feature.setGeometry(new_geometry) - writer.addFeature(feature) + sink.addFeature(feature) feedback.setProgress(int(current * total)) - del writer + return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py b/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py index 6e860c023dd..f328c8eb6e6 100644 --- a/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py +++ b/python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py @@ -31,13 +31,12 @@ from math import sqrt from qgis.core import (QgsWkbTypes, QgsApplication, - QgsProcessingUtils) - + QgsProcessingParameterFeatureSource, + QgsProcessingParameterNumber, + QgsProcessingParameterFeatureSink, + QgsProcessingOutputVectorLayer, + QgsProcessingParameterDefinition) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.parameters import ParameterNumber -from processing.core.outputs import OutputVector -from processing.tools import dataobjects class DensifyGeometriesInterval(QgisAlgorithm): @@ -57,13 +56,15 @@ class DensifyGeometriesInterval(QgisAlgorithm): def __init__(self): super().__init__() - self.addParameter(ParameterVector(self.INPUT, - self.tr('Input layer'), - [dataobjects.TYPE_VECTOR_POLYGON, dataobjects.TYPE_VECTOR_LINE])) - self.addParameter(ParameterNumber(self.INTERVAL, - self.tr('Interval between vertices to add'), 0.0, 10000000.0, 1.0)) - self.addOutput(OutputVector(self.OUTPUT, self.tr('Densified'))) + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT, + self.tr('Input layer'), [QgsProcessingParameterDefinition.TypeVectorPolygon, QgsProcessingParameterDefinition.TypeVectorLine])) + self.addParameter(QgsProcessingParameterNumber(self.INTERVAL, + self.tr('Interval between vertices to add'), QgsProcessingParameterNumber.Double, + 1, False, 0, 10000000)) + + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Densified'))) + self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Densified'))) def name(self): return 'densifygeometriesgivenaninterval' @@ -72,23 +73,24 @@ class DensifyGeometriesInterval(QgisAlgorithm): return self.tr('Densify geometries given an interval') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context) - interval = self.getParameterValue(self.INTERVAL) + source = self.parameterAsSource(parameters, self.INPUT, context) + interval = self.parameterAsDouble(parameters, self.INTERVAL, context) - isPolygon = layer.geometryType() == QgsWkbTypes.PolygonGeometry + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context, + source.fields(), source.wkbType(), source.sourceCrs()) - writer = self.getOutputFromName( - self.OUTPUT).getVectorWriter(layer.fields(), layer.wkbType(), layer.crs(), context) - - features = QgsProcessingUtils.getFeatures(layer, context) - total = 100.0 / QgsProcessingUtils.featureCount(layer, context) + features = source.getFeatures() + total = 100.0 / source.featureCount() for current, f in enumerate(features): + if feedback.isCanceled(): + break + feature = f if feature.hasGeometry(): - new_geometry = feature.geometry().densifyByCount(float(interval)) + new_geometry = feature.geometry().densifyByDistance(float(interval)) feature.setGeometry(new_geometry) - writer.addFeature(feature) + sink.addFeature(feature) feedback.setProgress(int(current * total)) - del writer + return {self.OUTPUT: dest_id} diff --git a/python/plugins/processing/algs/qgis/DropGeometry.py b/python/plugins/processing/algs/qgis/DropGeometry.py index 1c68e076350..46171d9d4de 100644 --- a/python/plugins/processing/algs/qgis/DropGeometry.py +++ b/python/plugins/processing/algs/qgis/DropGeometry.py @@ -29,11 +29,11 @@ from qgis.core import (QgsFeatureRequest, QgsWkbTypes, QgsCoordinateReferenceSystem, QgsApplication, - QgsProcessingUtils) + QgsProcessingParameterDefinition, + QgsProcessingParameterFeatureSource, + QgsProcessingParameterFeatureSink, + QgsProcessingOutputVectorLayer) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm -from processing.core.parameters import ParameterVector -from processing.core.outputs import OutputVector -from processing.tools import dataobjects class DropGeometry(QgisAlgorithm): @@ -55,11 +55,10 @@ class DropGeometry(QgisAlgorithm): def __init__(self): super().__init__() - self.addParameter(ParameterVector(self.INPUT_LAYER, - self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POINT, - dataobjects.TYPE_VECTOR_LINE, - dataobjects.TYPE_VECTOR_POLYGON])) - self.addOutput(OutputVector(self.OUTPUT_TABLE, self.tr('Dropped geometry'))) + + self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER, self.tr('Input layer'), [QgsProcessingParameterDefinition.TypeVectorPoint, QgsProcessingParameterDefinition.TypeVectorLine, QgsProcessingParameterDefinition.TypeVectorPolygon])) + self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_TABLE, self.tr('Dropped geometry'))) + self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT_TABLE, self.tr("Dropped geometry"))) def name(self): return 'dropgeometries' @@ -68,17 +67,20 @@ class DropGeometry(QgisAlgorithm): return self.tr('Drop geometries') def processAlgorithm(self, parameters, context, feedback): - layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context) - writer = self.getOutputFromName( - self.OUTPUT_TABLE).getVectorWriter(layer.fields(), QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem(), - context) + source = self.parameterAsSource(parameters, self.INPUT_LAYER, context) + (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_TABLE, context, + source.fields(), QgsWkbTypes.NoGeometry, QgsCoordinateReferenceSystem()) request = QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry) - features = QgsProcessingUtils.getFeatures(layer, context, request) - total = 100.0 / QgsProcessingUtils.featureCount(layer, context) + features = source.getFeatures(request) + total = 100.0 / source.featureCount() for current, input_feature in enumerate(features): - writer.addFeature(input_feature) + if feedback.isCanceled(): + break + + input_feature.clearGeometry() + sink.addFeature(input_feature) feedback.setProgress(int(current * total)) - del writer + return {self.OUTPUT_TABLE: dest_id} diff --git a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py index abd5a94d0f0..8e2a97b0e7c 100755 --- a/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py +++ b/python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py @@ -65,7 +65,8 @@ from .ExtractByExpression import ExtractByExpression # from .Centroids import Centroids # from .Delaunay import Delaunay # from .VoronoiPolygons import VoronoiPolygons -# from .DensifyGeometries import DensifyGeometries +from .DensifyGeometries import DensifyGeometries +from .DensifyGeometriesInterval import DensifyGeometriesInterval from .MultipartToSingleparts import MultipartToSingleparts # from .SimplifyGeometries import SimplifyGeometries # from .LinesToPolygons import LinesToPolygons @@ -84,10 +85,9 @@ from .ExtentFromLayer import ExtentFromLayer # from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets # from .SelectByLocation import SelectByLocation # from .Union import Union -# from .DensifyGeometriesInterval import DensifyGeometriesInterval # from .SpatialJoin import SpatialJoin from .DeleteColumn import DeleteColumn -# from .DeleteHoles import DeleteHoles +from .DeleteHoles import DeleteHoles # from .DeleteDuplicateGeometries import DeleteDuplicateGeometries # from .TextToFloat import TextToFloat # from .ExtractByAttribute import ExtractByAttribute @@ -169,7 +169,7 @@ from .Aspect import Aspect # from .PoleOfInaccessibility import PoleOfInaccessibility # from .RasterCalculator import RasterCalculator # from .CreateAttributeIndex import CreateAttributeIndex -# from .DropGeometry import DropGeometry +from .DropGeometry import DropGeometry from .BasicStatistics import BasicStatisticsForField # from .Heatmap import Heatmap # from .Orthogonalize import Orthogonalize @@ -205,14 +205,13 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # LinesIntersection(), UniqueValues(), PointDistance(), # ReprojectLayer(), ExportGeometryInfo(), Centroids(), # Delaunay(), VoronoiPolygons(), SimplifyGeometries(), - # DensifyGeometries(), DensifyGeometriesInterval(), # , SinglePartsToMultiparts(), # PolygonsToLines(), LinesToPolygons(), ExtractNodes(), # ConvexHull(), FixedDistanceBuffer(), # VariableDistanceBuffer(), Dissolve(), Difference(), # Intersection(), Union(), # RandomSelection(), RandomSelectionWithinSubsets(), - # SelectByLocation(), RandomExtract(), DeleteHoles(), + # SelectByLocation(), RandomExtract(), # RandomExtractWithinSubsets(), ExtractByLocation(), # SpatialJoin(), RegularPoints(), SymmetricalDifference(), # VectorSplit(), VectorGridLines(), VectorGridPolygons(), @@ -251,7 +250,7 @@ class QGISAlgorithmProvider(QgsProcessingProvider): # ExtendLines(), ExtractSpecificNodes(), # GeometryByExpression(), SnapGeometriesToLayer(), # PoleOfInaccessibility(), CreateAttributeIndex(), - # DropGeometry(), + # # RasterCalculator(), Heatmap(), Orthogonalize(), # ShortestPathPointToPoint(), ShortestPathPointToLayer(), # ShortestPathLayerToPoint(), ServiceAreaFromPoint(), @@ -268,6 +267,10 @@ class QGISAlgorithmProvider(QgsProcessingProvider): CheckValidity(), Clip(), DeleteColumn(), + DeleteHoles(), + DensifyGeometries(), + DensifyGeometriesInterval(), + DropGeometry(), ExtentFromLayer(), ExtractByExpression(), GridPolygon(), diff --git a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml index deedd9fb0b3..08ef4a93ab0 100644 --- a/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml +++ b/python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml @@ -15,16 +15,16 @@ tests: # geometry: # precision: 7 # -# - name: Delete Holes -# algorithm: qgis:deleteholes -# params: -# - name: polys.gml -# type: vector -# results: -# OUTPUT: -# name: expected/polys_deleteholes.gml -# type: vector -# + - name: Delete Holes + algorithm: native:deleteholes + params: + - name: polys.gml + type: vector + results: + OUTPUT: + name: expected/polys_deleteholes.gml + type: vector + - algorithm: qgis:clip name: Clip lines by polygons params: @@ -128,18 +128,18 @@ tests: # name: expected/intersection_collection_fallback.shp # type: vector # -# - name: Densify geometries -# algorithm: qgis:densifygeometries -# params: -# INPUT: -# name: multipolys.gml -# type: vector -# VERTICES: 4 -# results: -# OUTPUT: -# name: expected/multipolys_densify.gml -# type: vector -# + - name: Densify geometries + algorithm: qgis:densifygeometries + params: + INPUT: + name: multipolys.gml + type: vector + VERTICES: 4 + results: + OUTPUT: + name: expected/multipolys_densify.gml + type: vector + # - name: Polygons to Lines # algorithm: qgis:polygonstolines # params: @@ -1665,18 +1665,18 @@ tests: # compare: # geometry: # precision: 7 -# -# - algorithm: qgis:dropgeometries -# name: Drop geometries -# params: -# INPUT_LAYER: -# name: polys.gml -# type: vector -# results: -# OUTPUT_TABLE: -# name: expected/dropped_geometry.csv -# type: vector -# + + - algorithm: qgis:dropgeometries + name: Drop geometries + params: + INPUT_LAYER: + name: polys.gml + type: vector + results: + OUTPUT_TABLE: + name: expected/dropped_geometry.csv + type: vector + # - algorithm: qgis:creategridlines # name: Create grid (lines) # params: @@ -1808,30 +1808,30 @@ tests: geometry: precision: 7 -# - algorithm: qgis:deleteholes -# name: Delete holes (no min) -# params: -# INPUT: -# name: custom/remove_holes.gml -# type: vector -# MIN_AREA: 0.0 -# results: -# OUTPUT: -# name: expected/removed_holes.gml -# type: vector -# -# - algorithm: qgis:deleteholes -# name: Delete holes (with min) -# params: -# INPUT: -# name: custom/remove_holes.gml -# type: vector -# MIN_AREA: 5.0 -# results: -# OUTPUT: -# name: expected/removed_holes_min_area.gml -# type: vector -# + - algorithm: qgis:deleteholes + name: Delete holes (no min) + params: + INPUT: + name: custom/remove_holes.gml + type: vector + MIN_AREA: 0.0 + results: + OUTPUT: + name: expected/removed_holes.gml + type: vector + + - algorithm: qgis:deleteholes + name: Delete holes (with min) + params: + INPUT: + name: custom/remove_holes.gml + type: vector + MIN_AREA: 5.0 + results: + OUTPUT: + name: expected/removed_holes_min_area.gml + type: vector + - algorithm: qgis:basicstatisticsforfields name: Basic stats datetime params: diff --git a/src/core/processing/qgsnativealgorithms.cpp b/src/core/processing/qgsnativealgorithms.cpp index 2a6d6a479cc..9dd2f74cd62 100644 --- a/src/core/processing/qgsnativealgorithms.cpp +++ b/src/core/processing/qgsnativealgorithms.cpp @@ -58,12 +58,13 @@ bool QgsNativeAlgorithms::supportsNonFileBasedOutput() const void QgsNativeAlgorithms::loadAlgorithms() { - addAlgorithm( new QgsCentroidAlgorithm() ); addAlgorithm( new QgsBufferAlgorithm() ); - addAlgorithm( new QgsDissolveAlgorithm() ); + addAlgorithm( new QgsCentroidAlgorithm() ); addAlgorithm( new QgsClipAlgorithm() ); - addAlgorithm( new QgsTransformAlgorithm() ); + addAlgorithm( new QgsDissolveAlgorithm() ); + addAlgorithm( new QgsMultipartToSinglepartAlgorithm() ); addAlgorithm( new QgsSubdivideAlgorithm() ); + addAlgorithm( new QgsTransformAlgorithm() ); } @@ -664,6 +665,84 @@ QVariantMap QgsSubdivideAlgorithm::processAlgorithm( const QVariantMap ¶mete return outputs; } + + +QgsMultipartToSinglepartAlgorithm::QgsMultipartToSinglepartAlgorithm() +{ + addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) ); + + addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Single parts" ) ) ); + addOutput( new QgsProcessingOutputVectorLayer( QStringLiteral( "OUTPUT" ), QObject::tr( "Single parts" ) ) ); +} + +QString QgsMultipartToSinglepartAlgorithm::shortHelpString() const +{ + return QObject::tr( "This algorithm takes a vector layer with multipart geometries and generates a new one in which all geometries contain " + "a single part. Features with multipart geometries are divided in as many different features as parts the geometry " + "contain, and the same attributes are used for each of them." ); +} + +QVariantMap QgsMultipartToSinglepartAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const +{ + std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) ); + if ( !source ) + return QVariantMap(); + + QgsWkbTypes::Type sinkType = QgsWkbTypes::singleType( source->wkbType() ); + + QString dest; + std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, source->fields(), + sinkType, source->sourceCrs(), dest ) ); + if ( !sink ) + return QVariantMap(); + + long count = source->featureCount(); + if ( count <= 0 ) + return QVariantMap(); + + QgsFeature f; + QgsFeatureIterator it = source->getFeatures(); + + double step = 100.0 / count; + int current = 0; + while ( it.nextFeature( f ) ) + { + if ( feedback->isCanceled() ) + { + break; + } + + QgsFeature out = f; + if ( out.hasGeometry() ) + { + QgsGeometry inputGeometry = f.geometry(); + if ( inputGeometry.isMultipart() ) + { + Q_FOREACH ( const QgsGeometry &g, inputGeometry.asGeometryCollection() ) + { + out.setGeometry( g ); + sink->addFeature( out ); + } + } + else + { + sink->addFeature( out ); + } + } + else + { + // feature with null geometry + sink->addFeature( out ); + } + + feedback->setProgress( current * step ); + current++; + } + + QVariantMap outputs; + outputs.insert( QStringLiteral( "OUTPUT" ), dest ); + return outputs; +} + + ///@endcond - - diff --git a/src/core/processing/qgsnativealgorithms.h b/src/core/processing/qgsnativealgorithms.h index 0b9de30f4f6..85eaee5e0ef 100644 --- a/src/core/processing/qgsnativealgorithms.h +++ b/src/core/processing/qgsnativealgorithms.h @@ -182,6 +182,29 @@ class QgsSubdivideAlgorithm : public QgsProcessingAlgorithm }; +/** + * Native multipart to singlepart algorithm. + */ +class QgsMultipartToSinglepartAlgorithm : public QgsProcessingAlgorithm +{ + + public: + + QgsMultipartToSinglepartAlgorithm(); + + QString name() const override { return QStringLiteral( "multiparttosingleparts" ); } + QString displayName() const override { return QObject::tr( "Multipart to singleparts" ); } + virtual QStringList tags() const override { return QObject::tr( "multi,single,multiple,split,dump" ).split( ',' ); } + QString group() const override { return QObject::tr( "Vector geometry tools" ); } + QString shortHelpString() const override; + + protected: + + virtual QVariantMap processAlgorithm( const QVariantMap ¶meters, + QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const override; + +}; + ///@endcond PRIVATE #endif // QGSNATIVEALGORITHMS_H