Skip to content
Permalink
Browse files

Merge pull request #4873 from nyalldawson/processing_feature_alg

Add QgsProcessingFeatureBasedAlgorithm subclass
  • Loading branch information
nyalldawson committed Jul 18, 2017
2 parents 0b263f9 + 11cfc78 commit 857f8437d1b7d5f71f64894bf4f9033795c0a253
Showing with 932 additions and 280 deletions.
  1. +132 −0 python/core/processing/qgsprocessingalgorithm.sip
  2. +12 −0 python/plugins/processing/algs/help/qgis.yaml
  3. +23 −38 python/plugins/processing/algs/qgis/AddTableField.py
  4. +15 −36 python/plugins/processing/algs/qgis/AutoincrementalField.py
  5. +16 −36 python/plugins/processing/algs/qgis/BoundingBox.py
  6. +94 −0 python/plugins/processing/algs/qgis/DropMZValues.py
  7. +6 −0 python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
  8. +23 −1 python/plugins/processing/algs/qgis/QgisAlgorithm.py
  9. +86 −0 python/plugins/processing/algs/qgis/SetMValue.py
  10. +86 −0 python/plugins/processing/algs/qgis/SetZValue.py
  11. BIN python/plugins/processing/tests/testdata/custom/pointszm.dbf
  12. +1 −0 python/plugins/processing/tests/testdata/custom/pointszm.prj
  13. +1 −0 python/plugins/processing/tests/testdata/custom/pointszm.qpj
  14. BIN python/plugins/processing/tests/testdata/custom/pointszm.shp
  15. BIN python/plugins/processing/tests/testdata/custom/pointszm.shx
  16. BIN python/plugins/processing/tests/testdata/expected/m_dropped.dbf
  17. +1 −0 python/plugins/processing/tests/testdata/expected/m_dropped.prj
  18. +1 −0 python/plugins/processing/tests/testdata/expected/m_dropped.qpj
  19. BIN python/plugins/processing/tests/testdata/expected/m_dropped.shp
  20. BIN python/plugins/processing/tests/testdata/expected/m_dropped.shx
  21. BIN python/plugins/processing/tests/testdata/expected/set_m_value.dbf
  22. +77 −0 python/plugins/processing/tests/testdata/expected/set_m_value.gml
  23. +1 −0 python/plugins/processing/tests/testdata/expected/set_m_value.prj
  24. +1 −0 python/plugins/processing/tests/testdata/expected/set_m_value.qpj
  25. BIN python/plugins/processing/tests/testdata/expected/set_m_value.shp
  26. BIN python/plugins/processing/tests/testdata/expected/set_m_value.shx
  27. BIN python/plugins/processing/tests/testdata/expected/set_z_value.dbf
  28. +1 −0 python/plugins/processing/tests/testdata/expected/set_z_value.prj
  29. +1 −0 python/plugins/processing/tests/testdata/expected/set_z_value.qpj
  30. BIN python/plugins/processing/tests/testdata/expected/set_z_value.shp
  31. BIN python/plugins/processing/tests/testdata/expected/set_z_value.shx
  32. BIN python/plugins/processing/tests/testdata/expected/z_dropped.dbf
  33. +1 −0 python/plugins/processing/tests/testdata/expected/z_dropped.prj
  34. +1 −0 python/plugins/processing/tests/testdata/expected/z_dropped.qpj
  35. BIN python/plugins/processing/tests/testdata/expected/z_dropped.shp
  36. BIN python/plugins/processing/tests/testdata/expected/z_dropped.shx
  37. BIN python/plugins/processing/tests/testdata/expected/zm_dropped.dbf
  38. +1 −0 python/plugins/processing/tests/testdata/expected/zm_dropped.prj
  39. +1 −0 python/plugins/processing/tests/testdata/expected/zm_dropped.qpj
  40. BIN python/plugins/processing/tests/testdata/expected/zm_dropped.shp
  41. BIN python/plugins/processing/tests/testdata/expected/zm_dropped.shx
  42. +77 −14 python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
  43. +56 −143 src/core/processing/qgsnativealgorithms.cpp
  44. +30 −12 src/core/processing/qgsnativealgorithms.h
  45. +61 −0 src/core/processing/qgsprocessingalgorithm.cpp
  46. +126 −0 src/core/processing/qgsprocessingalgorithm.h
@@ -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,136 @@ 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.
This is called once by the base class when creating the output sink for the algorithm (i.e. it is
not called once per feature processed).
: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.

This is called once by the base class when creating the output sink for the algorithm (i.e. it is
not called once per feature processed).
: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.

This is called once by the base class when creating the output sink for the algorithm (i.e. it is
not called once per feature processed).
:rtype: QgsCoordinateReferenceSystem
%End

virtual void initParameters( const QVariantMap &configuration = QVariantMap() );
%Docstring
Initializes any extra parameters added by the algorithm subclass. There is no need
to declare the input source or output sink, as these are automatically created by
QgsProcessingFeatureBasedAlgorithm.
%End

QgsCoordinateReferenceSystem sourceCrs() const;
%Docstring
Returns the source's coordinate reference system. This will only return a valid CRS when
called from a subclasses' processFeature() implementation.
:rtype: QgsCoordinateReferenceSystem
%End

virtual QgsFeature processFeature( const 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 the modified feature. Returning an invalid feature (e.g.
a default constructed 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.

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.

Algorithms can throw a QgsProcessingException if a fatal error occurred which should
prevent the algorithm execution from continuing. This can be annoying for users though as it
can break valid model execution - so use with extreme caution, and consider using
``feedback`` to instead report non-fatal processing failures for features instead.
:rtype: QgsFeature
%End

virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback );

};



/************************************************************************
* This file has been generated automatically from *
@@ -160,6 +160,9 @@ qgis:distancetonearesthub: >
qgis:dropgeometries: >
This algorithm removes any geometries from an input layer and returns a layer containing only the feature attributes.

qgis:dropmzvalues: >
This algorithm can remove any measure (M) or Z values from input geometries.

qgis:eliminateselectedpolygons: >
This algorithm combines selected polygons of the input layer with certain adjacent polygons by erasing their common boundary. The adjacent polygon can be either the one with the largest or smallest area or the one sharing the largest common boundary with the polygon to be eliminated. The selected features will always be eliminated whether the option "Use only selected features" is set or not.
Eliminate is normally used to get rid of sliver polygons, i.e. tiny polygons that are a result of polygon intersection processes where boundaries of the inputs are similar but not identical.
@@ -503,13 +506,22 @@ qgis:selectbyexpression: >
qgis:selectbylocation: >
This algorithm creates a selection in a vector layer. The criteria for selecting features is based on the spatial relationship between each feature and the features in an additional layer.

qgis:setmvalue: >
This algorithm sets the M value for geometries in a layer.

If M values already exist in the layer, they will be overwritten with the new value. If no M values exist, the geometry will be upgraded to include M values and the specified value used as the initial M value for all geometries.

qgis:setstyleforrasterlayer: >
This algorithm sets the style of a raster layer. The style must be defined in a QML file.

qgis:setstyleforvectorlayer: >
This algorithm sets the style of a vector layer. The style must be defined in a QML file.

qgis:setzvalue: >
This algorithm sets the Z value for geometries in a layer.

If Z values already exist in the layer, they will be overwritten with the new value. If no Z values exist, the geometry will be upgraded to include Z values and the specified value used as the initial Z value for all geometries.

qgis:simplifygeometries: >
This algorithm simplifies the geometries in a line or polygon layer. It creates a new layer with the same features as the ones in the input layer, but with geometries containing a lower number of vertices.

@@ -27,19 +27,14 @@

from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsField,
QgsFeatureSink,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterString,
QgsProcessingParameterNumber,
QgsProcessingParameterEnum,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
QgsProcessingParameterEnum)
from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm


class AddTableField(QgisAlgorithm):
class AddTableField(QgisFeatureBasedAlgorithm):

OUTPUT_LAYER = 'OUTPUT_LAYER'
INPUT_LAYER = 'INPUT_LAYER'
FIELD_NAME = 'FIELD_NAME'
FIELD_TYPE = 'FIELD_TYPE'
FIELD_LENGTH = 'FIELD_LENGTH'
@@ -55,10 +50,9 @@ def __init__(self):
self.type_names = [self.tr('Integer'),
self.tr('Float'),
self.tr('String')]
self.field = None

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER,
self.tr('Input layer')))
def initParameters(self, config=None):
self.addParameter(QgsProcessingParameterString(self.FIELD_NAME,
self.tr('Field name')))
self.addParameter(QgsProcessingParameterEnum(self.FIELD_TYPE,
@@ -68,41 +62,32 @@ def initAlgorithm(self, config=None):
10, False, 1, 255))
self.addParameter(QgsProcessingParameterNumber(self.FIELD_PRECISION,
self.tr('Field precision'), QgsProcessingParameterNumber.Integer, 0, False, 0, 10))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT_LAYER, self.tr('Added')))

def name(self):
return 'addfieldtoattributestable'

def displayName(self):
return self.tr('Add field to attributes table')

def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
def outputName(self):
return self.tr('Added')

fieldType = self.parameterAsEnum(parameters, self.FIELD_TYPE, context)
fieldName = self.parameterAsString(parameters, self.FIELD_NAME, context)
fieldLength = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
fieldPrecision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)
def prepareAlgorithm(self, parameters, context, feedback):
field_type = self.parameterAsEnum(parameters, self.FIELD_TYPE, context)
field_name = self.parameterAsString(parameters, self.FIELD_NAME, context)
field_length = self.parameterAsInt(parameters, self.FIELD_LENGTH, context)
field_precision = self.parameterAsInt(parameters, self.FIELD_PRECISION, context)

fields = source.fields()
fields.append(QgsField(fieldName, self.TYPES[fieldType], '',
fieldLength, fieldPrecision))
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT_LAYER, context,
fields, source.wkbType(), source.sourceCrs())
self.field = QgsField(field_name, self.TYPES[field_type], '',
field_length, field_precision)
return True

features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
def outputFields(self, inputFields):
inputFields.append(self.field)
return inputFields

for current, input_feature in enumerate(features):
if feedback.isCanceled():
break

output_feature = input_feature
attributes = input_feature.attributes()
attributes.append(None)
output_feature.setAttributes(attributes)

sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))

return {self.OUTPUT_LAYER: dest_id}
def processFeature(self, feature, feedback):
attributes = feature.attributes()
attributes.append(None)
feature.setAttributes(attributes)
return feature
@@ -26,26 +26,15 @@
__revision__ = '$Format:%H$'

from qgis.PyQt.QtCore import QVariant
from qgis.core import (QgsField,
QgsFeatureSink,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from qgis.core import (QgsField)
from processing.algs.qgis.QgisAlgorithm import QgisFeatureBasedAlgorithm


class AutoincrementalField(QgisAlgorithm):

INPUT = 'INPUT'
OUTPUT = 'OUTPUT'
class AutoincrementalField(QgisFeatureBasedAlgorithm):

def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer')))

self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Incremented')))
self.current = 0

def group(self):
return self.tr('Vector table tools')
@@ -56,26 +45,16 @@ def name(self):
def displayName(self):
return self.tr('Add autoincremental field')

def processAlgorithm(self, parameters, context, feedback):
source = self.parameterAsSource(parameters, self.INPUT, context)
fields = source.fields()
fields.append(QgsField('AUTO', QVariant.Int))

(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, source.wkbType(), source.sourceCrs())

features = source.getFeatures()
total = 100.0 / source.featureCount() if source.featureCount() else 0
for current, input_feature in enumerate(features):
if feedback.isCanceled():
break

output_feature = input_feature
attributes = input_feature.attributes()
attributes.append(current)
output_feature.setAttributes(attributes)
def outputName(self):
return self.tr('Incremented')

sink.addFeature(output_feature, QgsFeatureSink.FastInsert)
feedback.setProgress(int(current * total))
def outputFields(self, inputFields):
inputFields.append(QgsField('AUTO', QVariant.Int))
return inputFields

return {self.OUTPUT: dest_id}
def processFeature(self, feature, feedback):
attributes = feature.attributes()
attributes.append(self.current)
self.current += 1
feature.setAttributes(attributes)
return feature

0 comments on commit 857f843

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