QGIS Processing Modeler – Field Calculator Uses the Input Layer

pyqgisqgis-3qgis-modeler

I'm trying to measure horizontal and vertical difference between consecutive points in a layer.

In the following link you can download a WinRar file with
a shapefile and my model to test in QGIS 3: https://www.mediafire.com/file/hhfmlv4arkd5jx5/KP_test.rar/file

enter image description here

I can do this in the attribute table with field calculator, using $x , $y and: attribute(get_feature_by_id('Layer',@row_number),'x0_kp'), and: attribute(get_feature_by_id('Layer',@row_number),'y0_kp'), then I calculate the differences.

However, when I attempt to do this using the processing modeler the last four columns are NULL. I believe this is because it doesnt really alter the current Layer but it works in the memory and then saves the result in an output layer. Therefore when I call x0_kp and y0_kp they dont actually exist in Layer.

I imagine there are at least two solutions for this issue:

  1. find a different method to retrieve $x and $y from the next feature.
  2. after $x and $y are calculated, output the temporary results in a temp_Layer then call this layer from the Field calculator: attribute(get_feature_by_id('temp_Layer',@row_number),'x0_kp')

But in reality I have failed to make either happen.


The automated code from the processing modeler is shown below.

from qgis.core import QgsProcessing
from qgis.core import QgsProcessingAlgorithm
from qgis.core import QgsProcessingMultiStepFeedback
from qgis.core import QgsProcessingParameterVectorLayer
from qgis.core import QgsProcessingParameterFeatureSink
import processing


class Pins_trials1(QgsProcessingAlgorithm):

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterVectorLayer('interpretationpoints', 'Interpretation Points', types=[QgsProcessing.TypeVectorPoint], defaultValue=None))
        self.addParameter(QgsProcessingParameterVectorLayer('kppoints', 'KP points', types=[QgsProcessing.TypeVectorPoint], defaultValue=None))
        self.addParameter(QgsProcessingParameterVectorLayer('interpretationlines', 'Interpretation Lines', types=[QgsProcessing.TypeVectorLine], defaultValue=None))
        self.addParameter(QgsProcessingParameterFeatureSink('Test_kp', 'test_KP', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue=None))

    def processAlgorithm(self, parameters, context, model_feedback):
        # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
        # overall progress through the model
        feedback = QgsProcessingMultiStepFeedback(4, model_feedback)
        results = {}
        outputs = {}

        # Add X/Y fields to KP
        alg_params = {
            'CRS': QgsCoordinateReferenceSystem('EPSG:4326'),
            'INPUT': parameters['kppoints'],
            'PREFIX': '',
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['AddXyFieldsToKp'] = processing.run('native:addxyfields', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(1)
        if feedback.isCanceled():
            return {}

        # Field calculator x1_kp
        alg_params = {
            'FIELD_LENGTH': 15,
            'FIELD_NAME': 'x1_kp',
            'FIELD_PRECISION': 3,
            'FIELD_TYPE': 0,
            'FORMULA': 'attribute(get_feature_by_id(\'Calculated\',@row_number),\'x0_kp\')',
            'INPUT': outputs['AddXyFieldsToKp']['OUTPUT'],
            'NEW_FIELD': True,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }
        outputs['FieldCalculatorX1_kp'] = processing.run('qgis:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

        feedback.setCurrentStep(2)
        if feedback.isCanceled():
            return {}

        # Field calculator y1_kp
        alg_params = {
            'FIELD_LENGTH': 15,
            'FIELD_NAME': 'y1_kp',
            'FIELD_PRECISION': 3,
            'FIELD_TYPE': 0,
            'FORMULA': 'attribute(get_feature_by_id(\'Calculated\',@row_number),\'y0_kp\')',
            'INPUT': outputs['FieldCalculatorX1_kp']['OUTPUT'],
            'NEW_FIELD': True,
            'OUTPUT': parameters['Test_kp']
        }
        outputs['FieldCalculatorY1_kp'] = processing.run('qgis:fieldcalculator', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
        results['Test_kp'] = outputs['FieldCalculatorY1_kp']['OUTPUT']

        feedback.setCurrentStep(3)
        if feedback.isCanceled():
            return {}

        return results

    def name(self):
        return 'PINS_trials1'

    def displayName(self):
        return 'PINS_trials1'

    def group(self):
        return 'F'

    def groupId(self):
        return 'Fu'

    def createInstance(self):
        return Pins_trials1()

Best Answer

To access the input layer in the expression, use the @layer variable.

To access the id of the feature, use the function $id.


You can calculate the difference between the x values of the following id feature and the current feature with the following expression:

attribute(
  get_feature_by_id(
    @layer,
    $id + 1),
  'x') - 
attribute(
  $currentfeature,
  'x') 

Note than the $currentfeature parameter is not necessary, you can use the attribute('x') variant.


Here is the model to test the x difference:

<!DOCTYPE model>
<Option type="Map">
  <Option type="Map" name="children">
    <Option type="Map" name="native:addxyfields_2">
      <Option value="true" type="bool" name="active"/>
      <Option name="alg_config"/>
      <Option value="native:addxyfields" type="QString" name="alg_id"/>
      <Option value="Add X/Y fields to KP" type="QString" name="component_description"/>
      <Option value="351.7406720872316" type="double" name="component_pos_x"/>
      <Option value="504.71744827318093" type="double" name="component_pos_y"/>
      <Option name="dependencies"/>
      <Option value="native:addxyfields_2" type="QString" name="id"/>
      <Option name="outputs"/>
      <Option value="false" type="bool" name="outputs_collapsed"/>
      <Option value="true" type="bool" name="parameters_collapsed"/>
      <Option type="Map" name="params">
        <Option type="List" name="CRS">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="ProjectCrs" type="QString" name="static_value"/>
          </Option>
        </Option>
        <Option type="List" name="INPUT">
          <Option type="Map">
            <Option value="kppoints" type="QString" name="parameter_name"/>
            <Option value="0" type="int" name="source"/>
          </Option>
        </Option>
        <Option type="List" name="PREFIX">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="" type="QString" name="static_value"/>
          </Option>
        </Option>
      </Option>
    </Option>
    <Option type="Map" name="qgis:fieldcalculator_1">
      <Option value="true" type="bool" name="active"/>
      <Option name="alg_config"/>
      <Option value="qgis:fieldcalculator" type="QString" name="alg_id"/>
      <Option value="Field calculator dx" type="QString" name="component_description"/>
      <Option value="526.7406720872316" type="double" name="component_pos_x"/>
      <Option value="623.7174482731809" type="double" name="component_pos_y"/>
      <Option name="dependencies"/>
      <Option value="qgis:fieldcalculator_1" type="QString" name="id"/>
      <Option type="Map" name="outputs">
        <Option type="Map" name="dx">
          <Option value="qgis:fieldcalculator_1" type="QString" name="child_id"/>
          <Option value="dx" type="QString" name="component_description"/>
          <Option value="716.7406720872316" type="double" name="component_pos_x"/>
          <Option value="698.7174482731809" type="double" name="component_pos_y"/>
          <Option type="invalid" name="default_value"/>
          <Option value="false" type="bool" name="mandatory"/>
          <Option value="dx" type="QString" name="name"/>
          <Option value="OUTPUT" type="QString" name="output_name"/>
        </Option>
      </Option>
      <Option value="true" type="bool" name="outputs_collapsed"/>
      <Option value="true" type="bool" name="parameters_collapsed"/>
      <Option type="Map" name="params">
        <Option type="List" name="FIELD_LENGTH">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="0" type="int" name="static_value"/>
          </Option>
        </Option>
        <Option type="List" name="FIELD_NAME">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="dx" type="QString" name="static_value"/>
          </Option>
        </Option>
        <Option type="List" name="FIELD_PRECISION">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="0" type="int" name="static_value"/>
          </Option>
        </Option>
        <Option type="List" name="FIELD_TYPE">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="0" type="int" name="static_value"/>
          </Option>
        </Option>
        <Option type="List" name="FORMULA">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="attribute(&#xd;&#xa;  get_feature_by_id(&#xd;&#xa;    @layer,&#xd;&#xa;&#x9;$id + 1),&#xd;&#xa;  'x') - &#xd;&#xa;attribute(&#xd;&#xa;  $currentfeature,&#xd;&#xa;  'x') " type="QString" name="static_value"/>
          </Option>
        </Option>
        <Option type="List" name="INPUT">
          <Option type="Map">
            <Option value="native:addxyfields_2" type="QString" name="child_id"/>
            <Option value="OUTPUT" type="QString" name="output_name"/>
            <Option value="1" type="int" name="source"/>
          </Option>
        </Option>
        <Option type="List" name="NEW_FIELD">
          <Option type="Map">
            <Option value="2" type="int" name="source"/>
            <Option value="true" type="bool" name="static_value"/>
          </Option>
        </Option>
      </Option>
    </Option>
  </Option>
  <Option name="help"/>
  <Option name="modelVariables"/>
  <Option value="" type="QString" name="model_group"/>
  <Option value="difference_against_next_feature_attribute" type="QString" name="model_name"/>
  <Option type="Map" name="parameterDefinitions">
    <Option type="Map" name="kppoints">
      <Option type="List" name="data_types">
        <Option value="0" type="int"/>
      </Option>
      <Option type="invalid" name="default"/>
      <Option value="KP points" type="QString" name="description"/>
      <Option value="0" type="int" name="flags"/>
      <Option name="metadata"/>
      <Option value="kppoints" type="QString" name="name"/>
      <Option value="vector" type="QString" name="parameter_type"/>
    </Option>
    <Option type="Map" name="qgis:fieldcalculator_1:dx">
      <Option value="true" type="bool" name="create_by_default"/>
      <Option value="-1" type="int" name="data_type"/>
      <Option type="invalid" name="default"/>
      <Option value="dx" type="QString" name="description"/>
      <Option value="0" type="int" name="flags"/>
      <Option name="metadata"/>
      <Option value="qgis:fieldcalculator_1:dx" type="QString" name="name"/>
      <Option value="sink" type="QString" name="parameter_type"/>
      <Option value="true" type="bool" name="supports_non_file_outputs"/>
    </Option>
  </Option>
  <Option type="Map" name="parameters">
    <Option type="Map" name="kppoints">
      <Option value="kppoints" type="QString" name="component_description"/>
      <Option value="188" type="double" name="component_pos_x"/>
      <Option value="397" type="double" name="component_pos_y"/>
      <Option value="kppoints" type="QString" name="name"/>
    </Option>
  </Option>
</Option>


1


2

Related Question