Skip to content

Commit

Permalink
Add QgsProcessingFeatureBasedAlgorithm subclass
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
nyalldawson committed Jul 18, 2017
1 parent 71b9ce2 commit 1a41624
Show file tree
Hide file tree
Showing 5 changed files with 263 additions and 45 deletions.
103 changes: 103 additions & 0 deletions python/core/processing/qgsprocessingalgorithm.sip
Expand Up @@ -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
Expand Down Expand Up @@ -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 *
Expand Down
47 changes: 6 additions & 41 deletions src/core/processing/qgsnativealgorithms.cpp
Expand Up @@ -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;
}

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 );
}
feedback->pushInfo( QObject::tr( "Error calculating centroid for feature %1" ).arg( feature.id() ) );
}
sink->addFeature( out, QgsFeatureSink::FastInsert );

feedback->setProgress( current * step );
current++;
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
return true;
}

//
// QgsBufferAlgorithm
//
Expand Down
10 changes: 6 additions & 4 deletions src/core/processing/qgsnativealgorithms.h
Expand Up @@ -48,7 +48,7 @@ class QgsNativeAlgorithms: public QgsProcessingProvider
/**
* Native centroid algorithm.
*/
class QgsCentroidAlgorithm : public QgsProcessingAlgorithm
class QgsCentroidAlgorithm : public QgsProcessingFeatureBasedAlgorithm
{

public:
Expand All @@ -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;
};

/**
Expand Down
54 changes: 54 additions & 0 deletions src/core/processing/qgsprocessingalgorithm.cpp
Expand Up @@ -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;
}
94 changes: 94 additions & 0 deletions src/core/processing/qgsprocessingalgorithm.h
Expand Up @@ -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
Expand Down Expand Up @@ -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


0 comments on commit 1a41624

Please sign in to comment.