Skip to content

Commit

Permalink
[processing] Tweak api for QgsProcessingFeatureBasedAlgorithm
Browse files Browse the repository at this point in the history
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 43cd62b
Show file tree
Hide file tree
Showing 63 changed files with 103 additions and 97 deletions.
13 changes: 8 additions & 5 deletions python/core/processing/qgsprocessingalgorithm.sip.in
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/AddTableField.py
Expand Up @@ -97,4 +97,4 @@ def processFeature(self, feature, context, feedback):
attributes = feature.attributes()
attributes.append(None)
feature.setAttributes(attributes)
return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/DeleteColumn.py
Expand Up @@ -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]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/DeleteHoles.py
Expand Up @@ -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]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/DensifyGeometries.py
Expand Up @@ -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]
Expand Up @@ -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]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/ExtendLines.py
Expand Up @@ -80,4 +80,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(output_geometry)

return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/FieldsMapper.py
Expand Up @@ -168,4 +168,4 @@ def processFeature(self, feature, context, feedback):
attributes.append(value)
feature.setAttributes(attributes)
self._row_number += 1
return feature
return [feature]
Expand Up @@ -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]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/LinesToPolygons.py
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/OffsetLine.py
Expand Up @@ -113,4 +113,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(output_geometry)

return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/Orthogonalize.py
Expand Up @@ -91,4 +91,4 @@ def processFeature(self, feature, context, feedback):
self.tr('Error orthogonalizing geometry'))

feature.setGeometry(output_geometry)
return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/PointOnSurface.py
Expand Up @@ -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]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/PolygonsToLines.py
Expand Up @@ -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
Expand Down
Expand Up @@ -67,4 +67,4 @@ def processFeature(self, feature, context, feedback):
outGeom = QgsGeometry(reversedLine)

feature.setGeometry(outGeom)
return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/SetMValue.py
Expand Up @@ -100,4 +100,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(QgsGeometry(new_geom))

return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/SetZValue.py
Expand Up @@ -101,4 +101,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(QgsGeometry(new_geom))

return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/SingleSidedBuffer.py
Expand Up @@ -122,4 +122,4 @@ def processFeature(self, feature, context, feedback):

feature.setGeometry(output_geometry)

return feature
return [feature]
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/TextToFloat.py
Expand Up @@ -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]
4 changes: 2 additions & 2 deletions src/3d/processing/qgsalgorithmtessellate.cpp
Expand Up @@ -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() )
Expand Down Expand Up @@ -105,7 +105,7 @@ QgsFeature QgsTessellateAlgorithm::processFeature( const QgsFeature &feature, Qg
f.setGeometry( g );
}
}
return f;
return QgsFeatureList() << f;
}

///@endcond
Expand Down
2 changes: 1 addition & 1 deletion src/3d/processing/qgsalgorithmtessellate.h
Expand Up @@ -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;

};

Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmaddincrementalfield.cpp
Expand Up @@ -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() )
{
Expand Down Expand Up @@ -127,7 +127,7 @@ QgsFeature QgsAddIncrementalFieldAlgorithm::processFeature( const QgsFeature &fe
mGroupedValues[ groupAttributes ] = value;
}
f.setAttributes( attributes );
return f;
return QgsFeatureList() << f;
}

///@endcond
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmaddincrementalfield.h
Expand Up @@ -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:

Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmassignprojection.cpp
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmassignprojection.h
Expand Up @@ -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:

Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmboundary.cpp
Expand Up @@ -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;

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

///@endcond
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmboundary.h
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmboundingbox.cpp
Expand Up @@ -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() )
Expand All @@ -95,7 +95,7 @@ QgsFeature QgsBoundingBoxAlgorithm::processFeature( const QgsFeature &feature, Q
<< QVariant();
f.setAttributes( attrs );
}
return f;
return QgsFeatureList() << f;
}

///@endcond
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmboundingbox.h
Expand Up @@ -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;

};

Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmcentroid.cpp
Expand Up @@ -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() )
Expand All @@ -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
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmcentroid.h
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmconvexhull.cpp
Expand Up @@ -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() )
Expand All @@ -93,7 +93,7 @@ QgsFeature QgsConvexHullAlgorithm::processFeature( const QgsFeature &feature, Qg
f.setAttributes( attrs );
}
}
return f;
return QgsFeatureList() << f;
}

///@endcond
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmconvexhull.h
Expand Up @@ -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;

};

Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmdropgeometry.cpp
Expand Up @@ -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
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmdropgeometry.h
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/analysis/processing/qgsalgorithmdropmzvalues.cpp
Expand Up @@ -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() )
Expand All @@ -95,7 +95,7 @@ QgsFeature QgsDropMZValuesAlgorithm::processFeature( const QgsFeature &feature,
f.setGeometry( QgsGeometry( newGeom.release() ) );
}

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

///@endcond
Expand Down
2 changes: 1 addition & 1 deletion src/analysis/processing/qgsalgorithmdropmzvalues.h
Expand Up @@ -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:

Expand Down

0 comments on commit 43cd62b

Please sign in to comment.