Skip to content
Permalink
Browse files

[processing] Tweak api for QgsProcessingFeatureBasedAlgorithm

Instead of returning a single QgsFeature from processFeature, we now
return a list of features.

This allows feature based algorithms which return multiple features
per input feature, e.g. "explode" type algorithms which split a
single input feature into multiple output features.
  • Loading branch information
nyalldawson committed Feb 20, 2018
1 parent 34553d3 commit 43cd62b62fef544b2a7bd5865520d9e7b0aebf81
Showing with 103 additions and 97 deletions.
  1. +8 −5 python/core/processing/qgsprocessingalgorithm.sip.in
  2. +1 −1 python/plugins/processing/algs/qgis/AddTableField.py
  3. +1 −1 python/plugins/processing/algs/qgis/DeleteColumn.py
  4. +1 −1 python/plugins/processing/algs/qgis/DeleteHoles.py
  5. +1 −1 python/plugins/processing/algs/qgis/DensifyGeometries.py
  6. +1 −1 python/plugins/processing/algs/qgis/DensifyGeometriesInterval.py
  7. +1 −1 python/plugins/processing/algs/qgis/ExtendLines.py
  8. +1 −1 python/plugins/processing/algs/qgis/FieldsMapper.py
  9. +1 −1 python/plugins/processing/algs/qgis/GeometryByExpression.py
  10. +1 −1 python/plugins/processing/algs/qgis/LinesToPolygons.py
  11. +1 −1 python/plugins/processing/algs/qgis/OffsetLine.py
  12. +1 −1 python/plugins/processing/algs/qgis/Orthogonalize.py
  13. +1 −1 python/plugins/processing/algs/qgis/PointOnSurface.py
  14. +1 −1 python/plugins/processing/algs/qgis/PolygonsToLines.py
  15. +1 −1 python/plugins/processing/algs/qgis/ReverseLineDirection.py
  16. +1 −1 python/plugins/processing/algs/qgis/SetMValue.py
  17. +1 −1 python/plugins/processing/algs/qgis/SetZValue.py
  18. +1 −1 python/plugins/processing/algs/qgis/SingleSidedBuffer.py
  19. +1 −1 python/plugins/processing/algs/qgis/TextToFloat.py
  20. +2 −2 src/3d/processing/qgsalgorithmtessellate.cpp
  21. +1 −1 src/3d/processing/qgsalgorithmtessellate.h
  22. +2 −2 src/analysis/processing/qgsalgorithmaddincrementalfield.cpp
  23. +1 −1 src/analysis/processing/qgsalgorithmaddincrementalfield.h
  24. +2 −2 src/analysis/processing/qgsalgorithmassignprojection.cpp
  25. +1 −1 src/analysis/processing/qgsalgorithmassignprojection.h
  26. +2 −2 src/analysis/processing/qgsalgorithmboundary.cpp
  27. +1 −1 src/analysis/processing/qgsalgorithmboundary.h
  28. +2 −2 src/analysis/processing/qgsalgorithmboundingbox.cpp
  29. +1 −1 src/analysis/processing/qgsalgorithmboundingbox.h
  30. +2 −2 src/analysis/processing/qgsalgorithmcentroid.cpp
  31. +1 −1 src/analysis/processing/qgsalgorithmcentroid.h
  32. +2 −2 src/analysis/processing/qgsalgorithmconvexhull.cpp
  33. +1 −1 src/analysis/processing/qgsalgorithmconvexhull.h
  34. +2 −2 src/analysis/processing/qgsalgorithmdropgeometry.cpp
  35. +1 −1 src/analysis/processing/qgsalgorithmdropgeometry.h
  36. +2 −2 src/analysis/processing/qgsalgorithmdropmzvalues.cpp
  37. +1 −1 src/analysis/processing/qgsalgorithmdropmzvalues.h
  38. +4 −4 src/analysis/processing/qgsalgorithmfixgeometries.cpp
  39. +1 −1 src/analysis/processing/qgsalgorithmfixgeometries.h
  40. +3 −3 src/analysis/processing/qgsalgorithmmergelines.cpp
  41. +1 −1 src/analysis/processing/qgsalgorithmmergelines.h
  42. +2 −2 src/analysis/processing/qgsalgorithmminimumenclosingcircle.cpp
  43. +1 −1 src/analysis/processing/qgsalgorithmminimumenclosingcircle.h
  44. +2 −2 src/analysis/processing/qgsalgorithmorientedminimumboundingbox.cpp
  45. +1 −1 src/analysis/processing/qgsalgorithmorientedminimumboundingbox.h
  46. +2 −2 src/analysis/processing/qgsalgorithmpromotetomultipart.cpp
  47. +1 −1 src/analysis/processing/qgsalgorithmpromotetomultipart.h
  48. +2 −2 src/analysis/processing/qgsalgorithmremoveduplicatevertices.cpp
  49. +1 −1 src/analysis/processing/qgsalgorithmremoveduplicatevertices.h
  50. +2 −2 src/analysis/processing/qgsalgorithmsimplify.cpp
  51. +1 −1 src/analysis/processing/qgsalgorithmsimplify.h
  52. +2 −2 src/analysis/processing/qgsalgorithmsmooth.cpp
  53. +1 −1 src/analysis/processing/qgsalgorithmsmooth.h
  54. +2 −2 src/analysis/processing/qgsalgorithmsnaptogrid.cpp
  55. +1 −1 src/analysis/processing/qgsalgorithmsnaptogrid.h
  56. +2 −2 src/analysis/processing/qgsalgorithmsubdivide.cpp
  57. +1 −1 src/analysis/processing/qgsalgorithmsubdivide.h
  58. +2 −2 src/analysis/processing/qgsalgorithmtransform.cpp
  59. +1 −1 src/analysis/processing/qgsalgorithmtransform.h
  60. +2 −2 src/analysis/processing/qgsalgorithmtranslate.cpp
  61. +1 −1 src/analysis/processing/qgsalgorithmtranslate.h
  62. +3 −3 src/core/processing/qgsprocessingalgorithm.cpp
  63. +8 −5 src/core/processing/qgsprocessingalgorithm.h
@@ -855,17 +855,20 @@ Returns the source's coordinate reference system. This will only return a valid
called from a subclasses' processFeature() implementation.
%End

virtual QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) = 0;
virtual QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) = 0;
%Docstring
Processes an individual input ``feature`` from the source. Algorithms should implement their
logic in this method for performing the algorithm's operation (e.g. replacing the feature's
geometry with the centroid of the original feature geometry for a 'centroid' type
algorithm).

Implementations should return the modified feature. Returning an invalid feature (e.g.
a default constructed :py:class:`QgsFeature`) will indicate that this feature should be 'skipped',
and will not be added to the algorithm's output. Subclasses can use this approach to
filter the incoming features as desired.
Implementations should return a list containing the modified feature. Returning an empty an list
will indicate that this feature should be 'skipped', and will not be added to the algorithm's output.
Subclasses can use this approach to filter the incoming features as desired.

Additionally, multiple features can be returned for a single input feature. Each returned feature
will be added to the algorithm's output. This allows for "explode" type algorithms where a single
input feature results in multiple output features.

The provided ``feedback`` object can be used to push messages to the log and for giving feedback
to users. Note that handling of progress reports and algorithm cancelation is handled by
@@ -97,4 +97,4 @@ def processFeature(self, feature, context, feedback):
attributes = feature.attributes()
attributes.append(None)
feature.setAttributes(attributes)
return feature
return [feature]
@@ -88,4 +88,4 @@ def processFeature(self, feature, context, feedback):
for index in self.field_indices:
del attributes[index]
feature.setAttributes(attributes)
return feature
return [feature]
@@ -72,4 +72,4 @@ def prepareAlgorithm(self, parameters, context, feedback):
def processFeature(self, feature, context, feedback):
if feature.hasGeometry():
feature.setGeometry(feature.geometry().removeInteriorRings(self.min_area))
return feature
return [feature]
@@ -75,4 +75,4 @@ def processFeature(self, feature, context, feedback):
if feature.hasGeometry():
new_geometry = feature.geometry().densifyByCount(self.vertices)
feature.setGeometry(new_geometry)
return feature
return [feature]
@@ -74,4 +74,4 @@ def processFeature(self, feature, context, feedback):
if feature.hasGeometry():
new_geometry = feature.geometry().densifyByDistance(float(interval))
feature.setGeometry(new_geometry)
return feature
return [feature]
@@ -80,4 +80,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(output_geometry)

return feature
return [feature]
@@ -168,4 +168,4 @@ def processFeature(self, feature, context, feedback):
attributes.append(value)
feature.setAttributes(attributes)
self._row_number += 1
return feature
return [feature]
@@ -118,4 +118,4 @@ def processFeature(self, feature, context, feedback):
raise QgsProcessingException(
self.tr('{} is not a geometry').format(value))
feature.setGeometry(value)
return feature
return [feature]
@@ -88,7 +88,7 @@ def processFeature(self, feature, context, feedback):
feature.setGeometry(QgsGeometry(self.convertToPolygons(feature.geometry())))
if feature.geometry().isEmpty():
feedback.reportError(self.tr("One or more line ignored due to geometry not having a minimum of three vertices."))
return feature
return [feature]

def convertWkbToPolygons(self, wkb):
multi_wkb = None
@@ -113,4 +113,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(output_geometry)

return feature
return [feature]
@@ -91,4 +91,4 @@ def processFeature(self, feature, context, feedback):
self.tr('Error orthogonalizing geometry'))

feature.setGeometry(output_geometry)
return feature
return [feature]
@@ -75,4 +75,4 @@ def processFeature(self, feature, context, feedback):
raise QgsProcessingException(self.tr('Error calculating point on surface: `{error_message}`'.format(error_message=output_geometry.error())))

feature.setGeometry(output_geometry)
return feature
return [feature]
@@ -79,7 +79,7 @@ def outputWkbType(self, input_wkb_type):
def processFeature(self, feature, context, feedback):
if feature.hasGeometry():
feature.setGeometry(QgsGeometry(self.convertToLines(feature.geometry())))
return feature
return [feature]

def convertWkbToLines(self, wkb):
multi_wkb = None
@@ -67,4 +67,4 @@ def processFeature(self, feature, context, feedback):
outGeom = QgsGeometry(reversedLine)

feature.setGeometry(outGeom)
return feature
return [feature]
@@ -100,4 +100,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(QgsGeometry(new_geom))

return feature
return [feature]
@@ -101,4 +101,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(QgsGeometry(new_geom))

return feature
return [feature]
@@ -122,4 +122,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(output_geometry)

return feature
return [feature]
@@ -85,4 +85,4 @@ def processFeature(self, feature, context, feedback):
feature[self.field_idx] = float(value)
except:
feature[self.field_idx] = None
return feature
return [feature]
@@ -74,7 +74,7 @@ QgsTessellateAlgorithm *QgsTessellateAlgorithm::createInstance() const
return new QgsTessellateAlgorithm();
}

QgsFeature QgsTessellateAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
QgsFeatureList QgsTessellateAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
{
QgsFeature f = feature;
if ( f.hasGeometry() )
@@ -105,7 +105,7 @@ QgsFeature QgsTessellateAlgorithm::processFeature( const QgsFeature &feature, Qg
f.setGeometry( g );
}
}
return f;
return QgsFeatureList() << f;
}

///@endcond
@@ -47,7 +47,7 @@ class QgsTessellateAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QgsProcessing::SourceType outputLayerType() const override;
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override;

QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

};

@@ -95,7 +95,7 @@ bool QgsAddIncrementalFieldAlgorithm::prepareAlgorithm( const QVariantMap &param
return true;
}

QgsFeature QgsAddIncrementalFieldAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
QgsFeatureList QgsAddIncrementalFieldAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
{
if ( !mGroupedFieldNames.empty() && mGroupedFields.empty() )
{
@@ -127,7 +127,7 @@ QgsFeature QgsAddIncrementalFieldAlgorithm::processFeature( const QgsFeature &fe
mGroupedValues[ groupAttributes ] = value;
}
f.setAttributes( attributes );
return f;
return QgsFeatureList() << f;
}

///@endcond
@@ -50,7 +50,7 @@ class QgsAddIncrementalFieldAlgorithm : public QgsProcessingFeatureBasedAlgorith
QgsFields outputFields( const QgsFields &inputFields ) const override;

bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

private:

@@ -73,9 +73,9 @@ bool QgsAssignProjectionAlgorithm::prepareAlgorithm( const QVariantMap &paramete
return true;
}

QgsFeature QgsAssignProjectionAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
QgsFeatureList QgsAssignProjectionAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
{
return feature;
return QgsFeatureList() << feature;
}

///@endcond
@@ -49,7 +49,7 @@ class QgsAssignProjectionAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QString outputName() const override;

bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

private:

@@ -95,7 +95,7 @@ QgsWkbTypes::Type QgsBoundaryAlgorithm::outputWkbType( QgsWkbTypes::Type inputWk
return outputWkb;
}

QgsFeature QgsBoundaryAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
QgsFeatureList QgsBoundaryAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
{
QgsFeature outFeature = feature;

@@ -113,7 +113,7 @@ QgsFeature QgsBoundaryAlgorithm::processFeature( const QgsFeature &feature, QgsP
outFeature.setGeometry( outputGeometry );
}
}
return outFeature;
return QgsFeatureList() << outFeature;
}

///@endcond
@@ -47,7 +47,7 @@ class QgsBoundaryAlgorithm : public QgsProcessingFeatureBasedAlgorithm

QString outputName() const override;
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override;
QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};

///@endcond PRIVATE
@@ -71,7 +71,7 @@ QgsFields QgsBoundingBoxAlgorithm::outputFields( const QgsFields &inputFields )
return fields;
}

QgsFeature QgsBoundingBoxAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
QgsFeatureList QgsBoundingBoxAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
{
QgsFeature f = feature;
if ( f.hasGeometry() )
@@ -95,7 +95,7 @@ QgsFeature QgsBoundingBoxAlgorithm::processFeature( const QgsFeature &feature, Q
<< QVariant();
f.setAttributes( attrs );
}
return f;
return QgsFeatureList() << f;
}

///@endcond
@@ -46,7 +46,7 @@ class QgsBoundingBoxAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QString outputName() const override;
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type ) const override { return QgsWkbTypes::Polygon; }
QgsFields outputFields( const QgsFields &inputFields ) const override;
QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

};

@@ -66,7 +66,7 @@ QgsCentroidAlgorithm *QgsCentroidAlgorithm::createInstance() const
return new QgsCentroidAlgorithm();
}

QgsFeature QgsCentroidAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &, QgsProcessingFeedback *feedback )
QgsFeatureList QgsCentroidAlgorithm::processFeature( const QgsFeature &f, QgsProcessingContext &, QgsProcessingFeedback *feedback )
{
QgsFeature feature = f;
if ( feature.hasGeometry() )
@@ -77,7 +77,7 @@ QgsFeature QgsCentroidAlgorithm::processFeature( const QgsFeature &f, QgsProcess
feedback->pushInfo( QObject::tr( "Error calculating centroid for feature %1" ).arg( feature.id() ) );
}
}
return feature;
return QgsFeatureList() << feature;
}

///@endcond
@@ -49,7 +49,7 @@ class QgsCentroidAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QgsProcessing::SourceType outputLayerType() const override { return QgsProcessing::TypeVectorPoint; }
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override { Q_UNUSED( inputWkbType ); return QgsWkbTypes::Point; }

QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};

///@endcond PRIVATE
@@ -69,7 +69,7 @@ QgsFields QgsConvexHullAlgorithm::outputFields( const QgsFields &inputFields ) c
return fields;
}

QgsFeature QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
QgsFeatureList QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback *feedback )
{
QgsFeature f = feature;
if ( f.hasGeometry() )
@@ -93,7 +93,7 @@ QgsFeature QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, Qg
f.setAttributes( attrs );
}
}
return f;
return QgsFeatureList() << f;
}

///@endcond
@@ -47,7 +47,7 @@ class QgsConvexHullAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QString outputName() const override;
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type ) const override { return QgsWkbTypes::Polygon; }
QgsFields outputFields( const QgsFields &inputFields ) const override;
QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

};

@@ -74,11 +74,11 @@ QgsFeatureRequest QgsDropGeometryAlgorithm::request() const
return QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry );
}

QgsFeature QgsDropGeometryAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
QgsFeatureList QgsDropGeometryAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
{
QgsFeature f = feature;
f.clearGeometry();
return f;
return QgsFeatureList() << f;
}

///@endcond
@@ -48,7 +48,7 @@ class QgsDropGeometryAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QString outputName() const override;
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override;
QgsFeatureRequest request() const override;
QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};

///@endcond PRIVATE
@@ -82,7 +82,7 @@ bool QgsDropMZValuesAlgorithm::prepareAlgorithm( const QVariantMap &parameters,
return true;
}

QgsFeature QgsDropMZValuesAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
QgsFeatureList QgsDropMZValuesAlgorithm::processFeature( const QgsFeature &feature, QgsProcessingContext &, QgsProcessingFeedback * )
{
QgsFeature f = feature;
if ( f.hasGeometry() )
@@ -95,7 +95,7 @@ QgsFeature QgsDropMZValuesAlgorithm::processFeature( const QgsFeature &feature,
f.setGeometry( QgsGeometry( newGeom.release() ) );
}

return f;
return QgsFeatureList() << f;
}

///@endcond
@@ -48,7 +48,7 @@ class QgsDropMZValuesAlgorithm : public QgsProcessingFeatureBasedAlgorithm
QString outputName() const override;
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override;
bool prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeature processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QgsFeatureList processFeature( const QgsFeature &feature, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

private:

0 comments on commit 43cd62b

Please sign in to comment.
You can’t perform that action at this time.