Add QgsProcessingFeatureBasedAlgorithm subclass

An abstract QgsProcessingAlgorithm base class for processing algorithms
which operate "feature-by-feature".

Feature based algorithms are algorithms which operate on individual
features in isolation. These are algorithms where one feature is
output for each input feature, and the output feature result
for each input feature is not dependent on any other features
present in the source.

For instance, algorithms like "centroids" and "buffers" are feature
based algorithms since the centroid or buffer of a feature is
calculated for each feature in isolation. An algorithm like "dissolve"
is NOT suitable for a feature based algorithm as the dissolved output
depends on multiple input features and these features cannot be
processed in isolation.

Using QgsProcessingFeatureBasedAlgorithm as the base class for feature
based algorithms allows shortcutting much of the common algorithm code
for handling iterating over sources and pushing features to output sinks.
It also allows the algorithm execution to be optimised in future
(for instance allowing automatic multi-thread processing of the
algorithm, or use of the algorithm in "chains", avoiding the need
for temporary outputs in multi-step models).
This commit is contained in:
Nyall Dawson 2017-07-10 19:28:21 +10:00
parent 71b9ce25c6
commit 1a41624370
5 changed files with 263 additions and 45 deletions

View File

@ -30,6 +30,8 @@ class QgsProcessingAlgorithm
%ConvertToSubClassCode
if ( dynamic_cast< QgsProcessingModelAlgorithm * >( sipCpp ) != NULL )
sipType = sipType_QgsProcessingModelAlgorithm;
else if ( dynamic_cast< QgsProcessingFeatureBasedAlgorithm * >( sipCpp ) != NULL )
sipType = sipType_QgsProcessingFeatureBasedAlgorithm;
else
sipType = sipType_QgsProcessingAlgorithm;
%End
@ -717,6 +719,107 @@ QFlags<QgsProcessingAlgorithm::Flag> operator|(QgsProcessingAlgorithm::Flag f1,
class QgsProcessingFeatureBasedAlgorithm : QgsProcessingAlgorithm
{
%Docstring
An abstract QgsProcessingAlgorithm base class for processing algorithms which operate "feature-by-feature".
Feature based algorithms are algorithms which operate on individual features in isolation. These
are algorithms where one feature is output for each input feature, and the output feature result
for each input feature is not dependent on any other features present in the source.
For instance, algorithms like "centroids" and "buffers" are feature based algorithms since the centroid
or buffer of a feature is calculated for each feature in isolation. An algorithm like "dissolve"
is NOT suitable for a feature based algorithm as the dissolved output depends on multiple input features
and these features cannot be processed in isolation.
Using QgsProcessingFeatureBasedAlgorithm as the base class for feature based algorithms allows
shortcutting much of the common algorithm code for handling iterating over sources and pushing
features to output sinks. It also allows the algorithm execution to be optimised in future
(for instance allowing automatic multi-thread processing of the algorithm, or use of the
algorithm in "chains", avoiding the need for temporary outputs in multi-step models).
.. versionadded:: 3.0
%End
%TypeHeaderCode
#include "qgsprocessingalgorithm.h"
%End
public:
QgsProcessingFeatureBasedAlgorithm();
%Docstring
Constructor for QgsProcessingFeatureBasedAlgorithm.
%End
protected:
virtual void initAlgorithm( const QVariantMap &configuration = QVariantMap() );
virtual QString outputName() const = 0;
%Docstring
Returns the translated, user visible name for any layers created by this algorithm.
This name will be used as the default name when loading the resultant layer into a
QGIS project.
:rtype: str
%End
virtual QgsProcessing::LayerType outputLayerType() const;
%Docstring
Returns the layer type for layers generated by this algorithm, if
this is possible to determine in advance.
:rtype: QgsProcessing.LayerType
%End
virtual QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const;
%Docstring
Maps the input WKB geometry type (``inputWkbType``) to the corresponding
output WKB type generated by the algorithm. The default behavior is that the algorithm maintains
the same WKB type.
:rtype: QgsWkbTypes.Type
%End
virtual QgsFields outputFields( const QgsFields &inputFields ) const;
%Docstring
Maps the input source fields (``inputFields``) to corresponding
output fields generated by the algorithm. The default behavior is that the algorithm maintains
the same fields as are input.
Algorithms which add, remove or modify existing fields should override this method and
implement logic here to indicate which fields are output by the algorithm.
:rtype: QgsFields
%End
virtual QgsCoordinateReferenceSystem outputCrs( const QgsCoordinateReferenceSystem &inputCrs ) const;
%Docstring
Maps the input source coordinate reference system (``inputCrs``) to a corresponding
output CRS generated by the algorithm. The default behavior is that the algorithm maintains
the same CRS as the input source.
:rtype: QgsCoordinateReferenceSystem
%End
virtual bool processFeature( QgsFeature &feature, 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 true if the feature should be kept and added to the algorithm's
output sink, or false if the feature should be skipped and omitted from the output.
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
the base class and subclasses do not need to reimplement this logic.
:rtype: bool
%End
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback );
};
/************************************************************************
* This file has been generated automatically from *

View File

@ -87,53 +87,18 @@ QgsCentroidAlgorithm *QgsCentroidAlgorithm::createInstance() const
return new QgsCentroidAlgorithm();
}
QVariantMap QgsCentroidAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
bool QgsCentroidAlgorithm::processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
return QVariantMap();
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), QgsWkbTypes::Point, source->sourceCrs() ) );
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 ( feature.hasGeometry() )
{
if ( feedback->isCanceled() )
feature.setGeometry( feature.geometry().centroid() );
if ( !feature.geometry() )
{
break;
feedback->pushInfo( QObject::tr( "Error calculating centroid for feature %1" ).arg( feature.id() ) );
}
QgsFeature out = f;
if ( out.hasGeometry() )
{
out.setGeometry( f.geometry().centroid() );
if ( !out.geometry() )
{
QgsMessageLog::logMessage( QObject::tr( "Error calculating centroid for feature %1" ).arg( f.id() ), QObject::tr( "Processing" ), QgsMessageLog::WARNING );
}
}
sink->addFeature( out, QgsFeatureSink::FastInsert );
feedback->setProgress( current * step );
current++;
}
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
return true;
}
//
// QgsBufferAlgorithm
//

View File

@ -48,7 +48,7 @@ class QgsNativeAlgorithms: public QgsProcessingProvider
/**
* Native centroid algorithm.
*/
class QgsCentroidAlgorithm : public QgsProcessingAlgorithm
class QgsCentroidAlgorithm : public QgsProcessingFeatureBasedAlgorithm
{
public:
@ -57,16 +57,18 @@ class QgsCentroidAlgorithm : public QgsProcessingAlgorithm
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override { return QStringLiteral( "centroids" ); }
QString displayName() const override { return QObject::tr( "Centroids" ); }
virtual QStringList tags() const override { return QObject::tr( "centroid,center,average,point,middle" ).split( ',' ); }
QStringList tags() const override { return QObject::tr( "centroid,center,average,point,middle" ).split( ',' ); }
QString group() const override { return QObject::tr( "Vector geometry tools" ); }
QString shortHelpString() const override;
QgsCentroidAlgorithm *createInstance() const override SIP_FACTORY;
protected:
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
QString outputName() const override { return QObject::tr( "Centroids" ); }
QgsProcessing::LayerType outputLayerType() const override { return QgsProcessing::TypeVectorPoint; }
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override { Q_UNUSED( inputWkbType ); return QgsWkbTypes::Point; }
bool processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback ) override;
};
/**

View File

@ -608,3 +608,57 @@ bool QgsProcessingAlgorithm::createAutoOutputForParameter( QgsProcessingParamete
}
//
// QgsProcessingFeatureBasedAlgorithm
//
void QgsProcessingFeatureBasedAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), outputName(), outputLayerType() ) );
addOutput( new QgsProcessingOutputVectorLayer( QStringLiteral( "OUTPUT" ), outputName(), outputLayerType() ) );
}
QVariantMap QgsProcessingFeatureBasedAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
return QVariantMap();
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest,
outputFields( source->fields() ),
outputWkbType( source->wkbType() ),
outputCrs( source->sourceCrs() ) ) );
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;
}
if ( processFeature( f, feedback ) )
{
sink->addFeature( f, QgsFeatureSink::FastInsert );
}
feedback->setProgress( current * step );
current++;
}
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
}

View File

@ -53,6 +53,8 @@ class CORE_EXPORT QgsProcessingAlgorithm
SIP_CONVERT_TO_SUBCLASS_CODE
if ( dynamic_cast< QgsProcessingModelAlgorithm * >( sipCpp ) != NULL )
sipType = sipType_QgsProcessingModelAlgorithm;
else if ( dynamic_cast< QgsProcessingFeatureBasedAlgorithm * >( sipCpp ) != NULL )
sipType = sipType_QgsProcessingFeatureBasedAlgorithm;
else
sipType = sipType_QgsProcessingAlgorithm;
SIP_END
@ -697,6 +699,98 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingAlgorithm::Flags )
/**
* \class QgsProcessingFeatureBasedAlgorithm
* \ingroup core
* An abstract QgsProcessingAlgorithm base class for processing algorithms which operate "feature-by-feature".
*
* Feature based algorithms are algorithms which operate on individual features in isolation. These
* are algorithms where one feature is output for each input feature, and the output feature result
* for each input feature is not dependent on any other features present in the source.
*
* For instance, algorithms like "centroids" and "buffers" are feature based algorithms since the centroid
* or buffer of a feature is calculated for each feature in isolation. An algorithm like "dissolve"
* is NOT suitable for a feature based algorithm as the dissolved output depends on multiple input features
* and these features cannot be processed in isolation.
*
* Using QgsProcessingFeatureBasedAlgorithm as the base class for feature based algorithms allows
* shortcutting much of the common algorithm code for handling iterating over sources and pushing
* features to output sinks. It also allows the algorithm execution to be optimised in future
* (for instance allowing automatic multi-thread processing of the algorithm, or use of the
* algorithm in "chains", avoiding the need for temporary outputs in multi-step models).
*
* \since QGIS 3.0
*/
class CORE_EXPORT QgsProcessingFeatureBasedAlgorithm : public QgsProcessingAlgorithm
{
public:
/**
* Constructor for QgsProcessingFeatureBasedAlgorithm.
*/
QgsProcessingFeatureBasedAlgorithm() = default;
protected:
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
/**
* Returns the translated, user visible name for any layers created by this algorithm.
* This name will be used as the default name when loading the resultant layer into a
* QGIS project.
*/
virtual QString outputName() const = 0;
/**
* Returns the layer type for layers generated by this algorithm, if
* this is possible to determine in advance.
*/
virtual QgsProcessing::LayerType outputLayerType() const { return QgsProcessing::TypeVectorAny; }
/**
* Maps the input WKB geometry type (\a inputWkbType) to the corresponding
* output WKB type generated by the algorithm. The default behavior is that the algorithm maintains
* the same WKB type.
*/
virtual QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const { return inputWkbType; }
/**
* Maps the input source fields (\a inputFields) to corresponding
* output fields generated by the algorithm. The default behavior is that the algorithm maintains
* the same fields as are input.
* Algorithms which add, remove or modify existing fields should override this method and
* implement logic here to indicate which fields are output by the algorithm.
*/
virtual QgsFields outputFields( const QgsFields &inputFields ) const { return inputFields; }
/**
* Maps the input source coordinate reference system (\a inputCrs) to a corresponding
* output CRS generated by the algorithm. The default behavior is that the algorithm maintains
* the same CRS as the input source.
*/
virtual QgsCoordinateReferenceSystem outputCrs( const QgsCoordinateReferenceSystem &inputCrs ) const { return inputCrs; }
/**
* Processes an individual input \a 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 true if the feature should be kept and added to the algorithm's
* output sink, or false if the feature should be skipped and omitted from the output.
*
* The provided \a 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
* the base class and subclasses do not need to reimplement this logic.
*/
virtual bool processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback ) = 0;
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};
#endif // QGSPROCESSINGALGORITHM_H