Resurrect some processing algs, port multipart to singlepart to c++

This commit is contained in:
Nyall Dawson 2017-06-21 22:11:31 +10:00
parent 2bf92f34f3
commit 31167718cf
8 changed files with 269 additions and 162 deletions

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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}

View File

@ -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(),

View File

@ -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:

View File

@ -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 &paramete
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 &parameters, 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

View File

@ -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 &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const override;
};
///@endcond PRIVATE
#endif // QGSNATIVEALGORITHMS_H