Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Port some existing algorithms to QgsProcessingFeatureBasedAlgorithm
  • Loading branch information
nyalldawson committed Jul 18, 2017
1 parent 1a41624 commit 7e3c435
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 175 deletions.
14 changes: 14 additions & 0 deletions python/core/processing/qgsprocessingalgorithm.sip
Expand Up @@ -798,6 +798,20 @@ class QgsProcessingFeatureBasedAlgorithm : QgsProcessingAlgorithm
: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 bool processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback ) = 0;
%Docstring
Processes an individual input ``feature`` from the source. Algorithms should implement their
Expand Down
61 changes: 23 additions & 38 deletions python/plugins/processing/algs/qgis/AddTableField.py
Expand Up @@ -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'
Expand All @@ -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,
Expand All @@ -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 True
51 changes: 15 additions & 36 deletions python/plugins/processing/algs/qgis/AutoincrementalField.py
Expand Up @@ -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')
Expand All @@ -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 True
24 changes: 23 additions & 1 deletion python/plugins/processing/algs/qgis/QgisAlgorithm.py
Expand Up @@ -25,7 +25,7 @@

__revision__ = '$Format:%H$'

from qgis.core import QgsProcessingAlgorithm
from qgis.core import QgsProcessingAlgorithm, QgsProcessingFeatureBasedAlgorithm
from qgis.PyQt.QtCore import QCoreApplication
from processing.algs.help import shortHelp

Expand All @@ -50,3 +50,25 @@ def trAlgorithm(self, string, context=''):

def createInstance(self):
return type(self)()


class QgisFeatureBasedAlgorithm(QgsProcessingFeatureBasedAlgorithm):

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

def shortHelpString(self):
return shortHelp.get(self.id(), None)

def tr(self, string, context=''):
if context == '':
context = self.__class__.__name__
return QCoreApplication.translate(context, string)

def trAlgorithm(self, string, context=''):
if context == '':
context = self.__class__.__name__
return string, QCoreApplication.translate(context, string)

def createInstance(self):
return type(self)()
Expand Up @@ -2556,11 +2556,11 @@ tests:
FIELD_NAME: field
FIELD_PRECISION: 2
FIELD_TYPE: '1'
INPUT_LAYER:
INPUT:
name: custom/points.shp
type: vector
results:
OUTPUT_LAYER:
OUTPUT:
name: expected/add_field.gml
type: vector

Expand Down
118 changes: 37 additions & 81 deletions src/core/processing/qgsnativealgorithms.cpp
Expand Up @@ -528,11 +528,9 @@ QVariantMap QgsClipAlgorithm::processAlgorithm( const QVariantMap &parameters, Q
}


void QgsTransformAlgorithm::initAlgorithm( const QVariantMap & )
void QgsTransformAlgorithm::initParameters( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterCrs( QStringLiteral( "TARGET_CRS" ), QObject::tr( "Target CRS" ), QStringLiteral( "EPSG:4326" ) ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Reprojected" ) ) );
}

QString QgsTransformAlgorithm::shortHelpString() const
Expand All @@ -547,57 +545,40 @@ QgsTransformAlgorithm *QgsTransformAlgorithm::createInstance() const
return new QgsTransformAlgorithm();
}

QVariantMap QgsTransformAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
bool QgsTransformAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
return QVariantMap();

QgsCoordinateReferenceSystem targetCrs = parameterAsCrs( parameters, QStringLiteral( "TARGET_CRS" ), context );

QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(), source->wkbType(), targetCrs ) );
if ( !sink )
return QVariantMap();

long count = source->featureCount();
if ( count <= 0 )
return QVariantMap();

QgsFeature f;
QgsFeatureRequest req;
// perform reprojection in the iterators...
req.setDestinationCrs( targetCrs );
mDestCrs = parameterAsCrs( parameters, QStringLiteral( "TARGET_CRS" ), context );
return true;
}

QgsFeatureIterator it = source->getFeatures( req );
bool QgsTransformAlgorithm::processFeature( QgsFeature &feature, QgsProcessingFeedback * )
{
if ( !mCreatedTransform )
{
mCreatedTransform = true;
mTransform = QgsCoordinateTransform( sourceCrs(), mDestCrs );
}

double step = 100.0 / count;
int current = 0;
while ( it.nextFeature( f ) )
if ( feature.hasGeometry() )
{
if ( feedback->isCanceled() )
QgsGeometry g = feature.geometry();
if ( g.transform( mTransform ) == 0 )
{
break;
feature.setGeometry( g );
}
else
{
feature.clearGeometry();
}

sink->addFeature( f, QgsFeatureSink::FastInsert );
feedback->setProgress( current * step );
current++;
}

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


void QgsSubdivideAlgorithm::initAlgorithm( const QVariantMap & )
void QgsSubdivideAlgorithm::initParameters( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "MAX_NODES" ), QObject::tr( "Maximum nodes in parts" ), QgsProcessingParameterNumber::Integer,
256, false, 8, 100000 ) );

addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Subdivided" ) ) );
}

QString QgsSubdivideAlgorithm::shortHelpString() const
Expand All @@ -615,53 +596,28 @@ QgsSubdivideAlgorithm *QgsSubdivideAlgorithm::createInstance() const
return new QgsSubdivideAlgorithm();
}

QVariantMap QgsSubdivideAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
QgsWkbTypes::Type QgsSubdivideAlgorithm::outputWkbType( QgsWkbTypes::Type inputWkbType ) const
{
std::unique_ptr< QgsFeatureSource > source( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !source )
return QVariantMap();

int maxNodes = parameterAsInt( parameters, QStringLiteral( "MAX_NODES" ), context );
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, source->fields(),
QgsWkbTypes::multiType( source->wkbType() ), source->sourceCrs() ) );
if ( !sink )
return QVariantMap();

long count = source->featureCount();
if ( count <= 0 )
return QVariantMap();

QgsFeature f;
QgsFeatureIterator it = source->getFeatures();
return QgsWkbTypes::multiType( inputWkbType );
}

double step = 100.0 / count;
int current = 0;
while ( it.nextFeature( f ) )
bool QgsSubdivideAlgorithm::processFeature( QgsFeature &feature, QgsProcessingFeedback *feedback )
{
if ( feature.hasGeometry() )
{
if ( feedback->isCanceled() )
{
break;
}

QgsFeature out = f;
if ( out.hasGeometry() )
feature.setGeometry( feature.geometry().subdivide( mMaxNodes ) );
if ( !feature.geometry() )
{
out.setGeometry( f.geometry().subdivide( maxNodes ) );
if ( !out.geometry() )
{
QgsMessageLog::logMessage( QObject::tr( "Error calculating subdivision for feature %1" ).arg( f.id() ), QObject::tr( "Processing" ), QgsMessageLog::WARNING );
}
feedback->reportError( QObject::tr( "Error calculating subdivision for feature %1" ).arg( feature.id() ) );
}
sink->addFeature( out, QgsFeatureSink::FastInsert );

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

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
bool QgsSubdivideAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
{
mMaxNodes = parameterAsInt( parameters, QStringLiteral( "MAX_NODES" ), context );
return true;
}


Expand Down

0 comments on commit 7e3c435

Please sign in to comment.