Skip to content

Commit 1a41624

Browse files
committed
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).
1 parent 71b9ce2 commit 1a41624

File tree

5 files changed

+263
-45
lines changed

5 files changed

+263
-45
lines changed

python/core/processing/qgsprocessingalgorithm.sip

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ class QgsProcessingAlgorithm
3030
%ConvertToSubClassCode
3131
if ( dynamic_cast< QgsProcessingModelAlgorithm * >( sipCpp ) != NULL )
3232
sipType = sipType_QgsProcessingModelAlgorithm;
33+
else if ( dynamic_cast< QgsProcessingFeatureBasedAlgorithm * >( sipCpp ) != NULL )
34+
sipType = sipType_QgsProcessingFeatureBasedAlgorithm;
3335
else
3436
sipType = sipType_QgsProcessingAlgorithm;
3537
%End
@@ -717,6 +719,107 @@ QFlags<QgsProcessingAlgorithm::Flag> operator|(QgsProcessingAlgorithm::Flag f1,
717719

718720

719721

722+
class QgsProcessingFeatureBasedAlgorithm : QgsProcessingAlgorithm
723+
{
724+
%Docstring
725+
An abstract QgsProcessingAlgorithm base class for processing algorithms which operate "feature-by-feature".
726+
727+
Feature based algorithms are algorithms which operate on individual features in isolation. These
728+
are algorithms where one feature is output for each input feature, and the output feature result
729+
for each input feature is not dependent on any other features present in the source.
730+
731+
For instance, algorithms like "centroids" and "buffers" are feature based algorithms since the centroid
732+
or buffer of a feature is calculated for each feature in isolation. An algorithm like "dissolve"
733+
is NOT suitable for a feature based algorithm as the dissolved output depends on multiple input features
734+
and these features cannot be processed in isolation.
735+
736+
Using QgsProcessingFeatureBasedAlgorithm as the base class for feature based algorithms allows
737+
shortcutting much of the common algorithm code for handling iterating over sources and pushing
738+
features to output sinks. It also allows the algorithm execution to be optimised in future
739+
(for instance allowing automatic multi-thread processing of the algorithm, or use of the
740+
algorithm in "chains", avoiding the need for temporary outputs in multi-step models).
741+
742+
.. versionadded:: 3.0
743+
%End
744+
745+
%TypeHeaderCode
746+
#include "qgsprocessingalgorithm.h"
747+
%End
748+
public:
749+
750+
QgsProcessingFeatureBasedAlgorithm();
751+
%Docstring
752+
Constructor for QgsProcessingFeatureBasedAlgorithm.
753+
%End
754+
755+
protected:
756+
757+
virtual void initAlgorithm( const QVariantMap &configuration = QVariantMap() );
758+
759+
760+
virtual QString outputName() const = 0;
761+
%Docstring
762+
Returns the translated, user visible name for any layers created by this algorithm.
763+
This name will be used as the default name when loading the resultant layer into a
764+
QGIS project.
765+
:rtype: str
766+
%End
767+
768+
virtual QgsProcessing::LayerType outputLayerType() const;
769+
%Docstring
770+
Returns the layer type for layers generated by this algorithm, if
771+
this is possible to determine in advance.
772+
:rtype: QgsProcessing.LayerType
773+
%End
774+
775+
virtual QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const;
776+
%Docstring
777+
Maps the input WKB geometry type (``inputWkbType``) to the corresponding
778+
output WKB type generated by the algorithm. The default behavior is that the algorithm maintains
779+
the same WKB type.
780+
:rtype: QgsWkbTypes.Type
781+
%End
782+
783+
virtual QgsFields outputFields( const QgsFields &inputFields ) const;
784+
%Docstring
785+
Maps the input source fields (``inputFields``) to corresponding
786+
output fields generated by the algorithm. The default behavior is that the algorithm maintains
787+
the same fields as are input.
788+
Algorithms which add, remove or modify existing fields should override this method and
789+
implement logic here to indicate which fields are output by the algorithm.
790+
:rtype: QgsFields
791+
%End
792+
793+
virtual QgsCoordinateReferenceSystem outputCrs( const QgsCoordinateReferenceSystem &inputCrs ) const;
794+
%Docstring
795+
Maps the input source coordinate reference system (``inputCrs``) to a corresponding
796+
output CRS generated by the algorithm. The default behavior is that the algorithm maintains
797+
the same CRS as the input source.
798+
:rtype: QgsCoordinateReferenceSystem
799+
%End
800+
801+
virtual bool processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback ) = 0;
802+
%Docstring
803+
Processes an individual input ``feature`` from the source. Algorithms should implement their
804+
logic in this method for performing the algorithm's operation (e.g. replacing the feature's
805+
geometry with the centroid of the original feature geometry for a 'centroid' type
806+
algorithm).
807+
808+
Implementations should return true if the feature should be kept and added to the algorithm's
809+
output sink, or false if the feature should be skipped and omitted from the output.
810+
811+
The provided ``feedback`` object can be used to push messages to the log and for giving feedback
812+
to users. Note that handling of progress reports and algorithm cancelation is handled by
813+
the base class and subclasses do not need to reimplement this logic.
814+
:rtype: bool
815+
%End
816+
817+
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
818+
QgsProcessingContext &context, QgsProcessingFeedback *feedback );
819+
820+
};
821+
822+
720823

721824
/************************************************************************
722825
* This file has been generated automatically from *

src/core/processing/qgsnativealgorithms.cpp

Lines changed: 6 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -87,53 +87,18 @@ QgsCentroidAlgorithm *QgsCentroidAlgorithm::createInstance() const
8787
return new QgsCentroidAlgorithm();
8888
}
8989

90-
QVariantMap QgsCentroidAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
90+
bool QgsCentroidAlgorithm::processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback )
9191
{
92-
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
93-
if ( !source )
94-
return QVariantMap();
95-
96-
QString dest;
97-
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), QgsWkbTypes::Point, source->sourceCrs() ) );
98-
if ( !sink )
99-
return QVariantMap();
100-
101-
long count = source->featureCount();
102-
if ( count <= 0 )
103-
return QVariantMap();
104-
105-
QgsFeature f;
106-
QgsFeatureIterator it = source->getFeatures();
107-
108-
double step = 100.0 / count;
109-
int current = 0;
110-
while ( it.nextFeature( f ) )
92+
if ( feature.hasGeometry() )
11193
{
112-
if ( feedback->isCanceled() )
94+
feature.setGeometry( feature.geometry().centroid() );
95+
if ( !feature.geometry() )
11396
{
114-
break;
115-
}
116-
117-
QgsFeature out = f;
118-
if ( out.hasGeometry() )
119-
{
120-
out.setGeometry( f.geometry().centroid() );
121-
if ( !out.geometry() )
122-
{
123-
QgsMessageLog::logMessage( QObject::tr( "Error calculating centroid for feature %1" ).arg( f.id() ), QObject::tr( "Processing" ), QgsMessageLog::WARNING );
124-
}
97+
feedback->pushInfo( QObject::tr( "Error calculating centroid for feature %1" ).arg( feature.id() ) );
12598
}
126-
sink->addFeature( out, QgsFeatureSink::FastInsert );
127-
128-
feedback->setProgress( current * step );
129-
current++;
13099
}
131-
132-
QVariantMap outputs;
133-
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
134-
return outputs;
100+
return true;
135101
}
136-
137102
//
138103
// QgsBufferAlgorithm
139104
//

src/core/processing/qgsnativealgorithms.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ class QgsNativeAlgorithms: public QgsProcessingProvider
4848
/**
4949
* Native centroid algorithm.
5050
*/
51-
class QgsCentroidAlgorithm : public QgsProcessingAlgorithm
51+
class QgsCentroidAlgorithm : public QgsProcessingFeatureBasedAlgorithm
5252
{
5353

5454
public:
@@ -57,16 +57,18 @@ class QgsCentroidAlgorithm : public QgsProcessingAlgorithm
5757
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
5858
QString name() const override { return QStringLiteral( "centroids" ); }
5959
QString displayName() const override { return QObject::tr( "Centroids" ); }
60-
virtual QStringList tags() const override { return QObject::tr( "centroid,center,average,point,middle" ).split( ',' ); }
60+
QStringList tags() const override { return QObject::tr( "centroid,center,average,point,middle" ).split( ',' ); }
6161
QString group() const override { return QObject::tr( "Vector geometry tools" ); }
6262
QString shortHelpString() const override;
6363
QgsCentroidAlgorithm *createInstance() const override SIP_FACTORY;
6464

6565
protected:
6666

67-
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
68-
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
67+
QString outputName() const override { return QObject::tr( "Centroids" ); }
68+
QgsProcessing::LayerType outputLayerType() const override { return QgsProcessing::TypeVectorPoint; }
69+
QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const override { Q_UNUSED( inputWkbType ); return QgsWkbTypes::Point; }
6970

71+
bool processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback ) override;
7072
};
7173

7274
/**

src/core/processing/qgsprocessingalgorithm.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,3 +608,57 @@ bool QgsProcessingAlgorithm::createAutoOutputForParameter( QgsProcessingParamete
608608
}
609609

610610

611+
//
612+
// QgsProcessingFeatureBasedAlgorithm
613+
//
614+
615+
void QgsProcessingFeatureBasedAlgorithm::initAlgorithm( const QVariantMap & )
616+
{
617+
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
618+
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), outputName(), outputLayerType() ) );
619+
addOutput( new QgsProcessingOutputVectorLayer( QStringLiteral( "OUTPUT" ), outputName(), outputLayerType() ) );
620+
}
621+
622+
QVariantMap QgsProcessingFeatureBasedAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
623+
{
624+
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
625+
if ( !source )
626+
return QVariantMap();
627+
628+
QString dest;
629+
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest,
630+
outputFields( source->fields() ),
631+
outputWkbType( source->wkbType() ),
632+
outputCrs( source->sourceCrs() ) ) );
633+
if ( !sink )
634+
return QVariantMap();
635+
636+
long count = source->featureCount();
637+
if ( count <= 0 )
638+
return QVariantMap();
639+
640+
QgsFeature f;
641+
QgsFeatureIterator it = source->getFeatures();
642+
643+
double step = 100.0 / count;
644+
int current = 0;
645+
while ( it.nextFeature( f ) )
646+
{
647+
if ( feedback->isCanceled() )
648+
{
649+
break;
650+
}
651+
652+
if ( processFeature( f, feedback ) )
653+
{
654+
sink->addFeature( f, QgsFeatureSink::FastInsert );
655+
}
656+
657+
feedback->setProgress( current * step );
658+
current++;
659+
}
660+
661+
QVariantMap outputs;
662+
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
663+
return outputs;
664+
}

src/core/processing/qgsprocessingalgorithm.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ class CORE_EXPORT QgsProcessingAlgorithm
5353
SIP_CONVERT_TO_SUBCLASS_CODE
5454
if ( dynamic_cast< QgsProcessingModelAlgorithm * >( sipCpp ) != NULL )
5555
sipType = sipType_QgsProcessingModelAlgorithm;
56+
else if ( dynamic_cast< QgsProcessingFeatureBasedAlgorithm * >( sipCpp ) != NULL )
57+
sipType = sipType_QgsProcessingFeatureBasedAlgorithm;
5658
else
5759
sipType = sipType_QgsProcessingAlgorithm;
5860
SIP_END
@@ -697,6 +699,98 @@ Q_DECLARE_OPERATORS_FOR_FLAGS( QgsProcessingAlgorithm::Flags )
697699

698700

699701

702+
/**
703+
* \class QgsProcessingFeatureBasedAlgorithm
704+
* \ingroup core
705+
* An abstract QgsProcessingAlgorithm base class for processing algorithms which operate "feature-by-feature".
706+
*
707+
* Feature based algorithms are algorithms which operate on individual features in isolation. These
708+
* are algorithms where one feature is output for each input feature, and the output feature result
709+
* for each input feature is not dependent on any other features present in the source.
710+
*
711+
* For instance, algorithms like "centroids" and "buffers" are feature based algorithms since the centroid
712+
* or buffer of a feature is calculated for each feature in isolation. An algorithm like "dissolve"
713+
* is NOT suitable for a feature based algorithm as the dissolved output depends on multiple input features
714+
* and these features cannot be processed in isolation.
715+
*
716+
* Using QgsProcessingFeatureBasedAlgorithm as the base class for feature based algorithms allows
717+
* shortcutting much of the common algorithm code for handling iterating over sources and pushing
718+
* features to output sinks. It also allows the algorithm execution to be optimised in future
719+
* (for instance allowing automatic multi-thread processing of the algorithm, or use of the
720+
* algorithm in "chains", avoiding the need for temporary outputs in multi-step models).
721+
*
722+
* \since QGIS 3.0
723+
*/
724+
725+
class CORE_EXPORT QgsProcessingFeatureBasedAlgorithm : public QgsProcessingAlgorithm
726+
{
727+
public:
728+
729+
/**
730+
* Constructor for QgsProcessingFeatureBasedAlgorithm.
731+
*/
732+
QgsProcessingFeatureBasedAlgorithm() = default;
733+
734+
protected:
735+
736+
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
737+
738+
/**
739+
* Returns the translated, user visible name for any layers created by this algorithm.
740+
* This name will be used as the default name when loading the resultant layer into a
741+
* QGIS project.
742+
*/
743+
virtual QString outputName() const = 0;
744+
745+
/**
746+
* Returns the layer type for layers generated by this algorithm, if
747+
* this is possible to determine in advance.
748+
*/
749+
virtual QgsProcessing::LayerType outputLayerType() const { return QgsProcessing::TypeVectorAny; }
750+
751+
/**
752+
* Maps the input WKB geometry type (\a inputWkbType) to the corresponding
753+
* output WKB type generated by the algorithm. The default behavior is that the algorithm maintains
754+
* the same WKB type.
755+
*/
756+
virtual QgsWkbTypes::Type outputWkbType( QgsWkbTypes::Type inputWkbType ) const { return inputWkbType; }
757+
758+
/**
759+
* Maps the input source fields (\a inputFields) to corresponding
760+
* output fields generated by the algorithm. The default behavior is that the algorithm maintains
761+
* the same fields as are input.
762+
* Algorithms which add, remove or modify existing fields should override this method and
763+
* implement logic here to indicate which fields are output by the algorithm.
764+
*/
765+
virtual QgsFields outputFields( const QgsFields &inputFields ) const { return inputFields; }
766+
767+
/**
768+
* Maps the input source coordinate reference system (\a inputCrs) to a corresponding
769+
* output CRS generated by the algorithm. The default behavior is that the algorithm maintains
770+
* the same CRS as the input source.
771+
*/
772+
virtual QgsCoordinateReferenceSystem outputCrs( const QgsCoordinateReferenceSystem &inputCrs ) const { return inputCrs; }
773+
774+
/**
775+
* Processes an individual input \a feature from the source. Algorithms should implement their
776+
* logic in this method for performing the algorithm's operation (e.g. replacing the feature's
777+
* geometry with the centroid of the original feature geometry for a 'centroid' type
778+
* algorithm).
779+
*
780+
* Implementations should return true if the feature should be kept and added to the algorithm's
781+
* output sink, or false if the feature should be skipped and omitted from the output.
782+
*
783+
* The provided \a feedback object can be used to push messages to the log and for giving feedback
784+
* to users. Note that handling of progress reports and algorithm cancelation is handled by
785+
* the base class and subclasses do not need to reimplement this logic.
786+
*/
787+
virtual bool processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback ) = 0;
788+
789+
virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
790+
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
791+
792+
};
793+
700794
#endif // QGSPROCESSINGALGORITHM_H
701795

702796

0 commit comments

Comments
 (0)