2

Does anyone know how I can turn this console script into a script that I can integrate into the processing toolbox? I tried a few options, but I don't do very well in python, and I always get stuck.

# Change your settings here:
layername = 'line' 
triggerfield = 'Trigger' # field must already exist
groupfieldname = 'mygroup' # will be added if does not exist
newlineindicator = 1

no changes needed below

layer = QgsProject.instance().mapLayersByName(layername)[0] # get the layer layer.startEditing() # start editing the layer layer.dataProvider().addAttributes([QgsField(groupfieldname,QVariant.Int)]) # add a group field layer.commitChanges() # save new added field to prevent crash when searching for it later layer.startEditing() # start editing the layer again groupid = 0 # initialize groupid fieldindex = layer.fields().indexFromName(groupfieldname) # get fieldindex of groupfield

for feat in layer.getFeatures(): # itereate over layer if feat[triggerfield] == newlineindicator: # if a new trigger appears, increase groupcounter groupid += 1 attrs = { fieldindex : groupid } # prepare attributes layer.dataProvider().changeAttributeValues({ feat.id() : attrs }) # assign current groupid to current feature layer.commitChanges() # save changes

What do you think? Can it be transformed into a function, with input and output parameters?

EDIT:

I got stuck at this point. Does anyone know how I can solve this script? How should I define "newlineindicator = 1" and "groupid = 0"?

# -*- coding: utf-8 -*-
from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsFeature,
                       QgsField,
                       QgsFields,
                       QgsProcessingException,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterString,
                       QgsProcessingParameterNumber,
                       QgsProcessingParameterEnum,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFeatureSink,
                       QgsProcessingParameterField,
                       )
import processing
class MergingMultipleLinesByValueField(QgsProcessingAlgorithm):
    INPUT = 'INPUT'
    OUTPUT = 'OUTPUT'
    TRIGGER_FIELD = 'trigger_field'
    groupfieldname = 'mygrup'
def tr(self, string):
    return QCoreApplication.translate('Processing', string)

def createInstance(self):
    return MergingMultipleLinesByValueField()

def name(self):
    return 'myscript'

def displayName(self):
    return self.tr('My Script')

def group(self):
    return self.tr('Example scripts')

def groupId(self):
    return 'examplescripts'

def shortHelpString(self):
    return self.tr("Example algorithm short description")

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


    self.addParameter(
        QgsProcessingParameterField(
            self.TRIGGER_FIELD,
            'Choose Trigger Field',
            '', 
            self.INPUT
        )
    )

    self.addParameter(
        QgsProcessingParameterField(
            self.groupfieldname,
            'Choose Trigger Field',
            '', 
            self.INPUT
        )
    ) 

    self.addParameter(
        QgsProcessingParameterFeatureSink(
            self.OUTPUT,
            self.tr('Output layer')
        )
    )

def processAlgorithm(self, parameters, context, feedback):
    source = self.parameterAsSource(
        parameters,
        self.INPUT,
        context
    )

    trigger = self.parameterAsString(
        parameters,
        self.TRIGGER_FIELD,
        context)

    groupfield = self.parameterAsString(
        parameters,
        self.groupfieldname,
        context)

    if source is None:
        raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))

    fields = QgsFields()
    fields.append(QgsField(trigger, QVariant.String))

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

    feedback.pushInfo('Extracting unique values from dissolve_field and computing sum')
    features = source.getFeatures()
    groupid = set(f[trigger] for f in features)
    groupid = source.fields().indexFromName(trigger)
    fieldindex = source.fields().indexFromName(groupfieldname) # get fieldindex of groupfield
    newlineindicator = 1
    groupid = 0

    for feat in source.getFeatures(): # itereate over layer
        if feat[trigger] == newlineindicator: # if a new trigger appears, increase groupcounter
                groupid += 1
        attrs = { fieldindex : groupid } # prepare attributes
        layer.dataProvider().changeAttributeValues({ feat.id() : attrs }) # assign current groupid to current feature
    layer.commitChanges() # save changes



    return {self.OUTPUT: dest_id}

Tony Pasca
  • 167
  • 5
  • 1
    Here is all you need to know to do it https://www.qgistutorials.com/en/docs/3/processing_python_scripts.html – Nil Dec 18 '20 at 08:56
  • 2
    If you "tried a few options" then its always a good idea to show us what they were and tell us how they didn't work - you might have been very close to the solution. – Spacedman Dec 18 '20 at 09:25
  • We have reached this option. Do you have any idea how to continue from here? @Spacedman – Tony Pasca Dec 18 '20 at 14:19
  • This task started here: https://gis.stackexchange.com/questions/381500/merging-multiple-lines-by-value-field – Tony Pasca Dec 18 '20 at 14:29

1 Answers1

1

This should do it:

from PyQt5.QtCore import QCoreApplication, QVariant
from qgis.core import (QgsField, QgsFeature, QgsProcessing, QgsExpression, QgsGeometry, QgsPoint, QgsFields, QgsWkbTypes,
                       QgsFeatureSink, QgsFeatureRequest, QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSink, QgsProcessingParameterField, QgsProcessingParameterFeatureSource, QgsProcessingParameterEnum, QgsProcessingParameterString, QgsProcessingParameterNumber)

class AddGroupByIndicator(QgsProcessingAlgorithm): SOURCE_LYR = 'SOURCE_LYR' TRIGGER_FIELD = 'TRIGGER_FIELD' GROUP_IDFIELD = 'GROUP_IDFIELD' INDICATOR_VALUE = 'INDICATOR_VALUE' OUTPUT = 'OUTPUT'

def initAlgorithm(self, config=None):

    self.addParameter(
        QgsProcessingParameterFeatureSource(
            self.SOURCE_LYR, self.tr('Source'))) # Take any source layer
    self.addParameter(
        QgsProcessingParameterField(
            self.TRIGGER_FIELD, self.tr('Trigger Field indicating a new Group'),'Trigger','SOURCE_LYR')) # Choose the Trigger field of the source layer, default if exists is 'Trigger'
    self.addParameter(
        QgsProcessingParameterString(
            self.GROUP_IDFIELD, self.tr('Name of new generated GroupID Field'),'groupid')) # String of the new added fieldname, default is 'groupid'
    self.addParameter(
        QgsProcessingParameterNumber(
            self.INDICATOR_VALUE, self.tr('Number indicating a new Group'),0,1)) # Indicator as number. 0=Int, 1 would be double; 1=default number
    self.addParameter(
        QgsProcessingParameterFeatureSink(
            self.OUTPUT, self.tr('SourceWithGroupID'))) # Output

def processAlgorithm(self, parameters, context, feedback):
    # Get Parameters and assign to variable to work with
    source_layer = self.parameterAsLayer(parameters, self.SOURCE_LYR, context)
    triggerfield = self.parameterAsString(parameters, self.TRIGGER_FIELD, context)
    groupfieldname = self.parameterAsString(parameters, self.GROUP_IDFIELD, context)
    newlineindicator = self.parameterAsInt(parameters, self.INDICATOR_VALUE, context)

    groupid = 0 # initialize groupid counter

    total = 100.0 / source_layer.featureCount() if source_layer.featureCount() else 0 # Initialize progress for progressbar

    fields = source_layer.fields() # get all fields of the sourcelayer
    fields.append(QgsField(groupfieldname, QVariant.Int, len=20)) # add a new field to this list

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

    for current, feat in enumerate(source_layer.getFeatures()): # iterate over source 
        if feat[triggerfield] == newlineindicator: # if trigger appears increase groupcounter
            groupid += 1
        new_feat = QgsFeature(fields) # copy source fields + appended
        idx = 0 # reset attribute fieldindex
        for attr in feat.attributes(): # iterate over attributes of source layer for the current feature
            new_feat[idx] = attr # copy attribute values over to the new layer
            idx += 1 # increase fieldindex counter
        new_feat[groupfieldname] = groupid # assign the groupid
        new_feat.setGeometry(feat.geometry()) # copy over the geometry of the source feature

        sink.addFeature(new_feat, QgsFeatureSink.FastInsert) # add feature to the output

        if feedback.isCanceled(): # Cancel algorithm if button is pressed
            break

        feedback.setProgress(int(current * total)) # Set Progress in Progressbar

    return {self.OUTPUT: dest_id} # Return result of algorithm



def tr(self, string):
    return QCoreApplication.translate('Processing', string)

def createInstance(self):
    return AddGroupByIndicator()

def name(self):
    return 'AddGroupByIndicator'

def displayName(self):
    return self.tr('Add group by indicator field')

def group(self):
    return self.tr('FROM GISSE')

def groupId(self):
    return 'from_gisse'

def shortHelpString(self):
    return self.tr('This Algorithm adds a new group id found by a trigger')

MrXsquared
  • 34,292
  • 21
  • 67
  • 117
  • 1
    The latest version is excellent. I can't believe you solved it nicely. Thank you for your help. The fact that you commented on each line helps me a lot to learn how to do it. You are awesome. – Tony Pasca Dec 18 '20 at 20:53