refFunctions v1.0

The wide use of Field calculator in Qgis allows to control behavior and output (for example in Print Composer) in a very accurate way. This is one of the points of strenght of this gis software.
These features bring the need to reference in some way between layers, For example retrieving a value from a layer using as a  field value as parameter. In such cases the Field calculator set of function look a bit poor. Only in the last release (v 2.6 Brighton) we can find two new function under record group that can help us:

getFeature( layer, attributeField, value )
attribute ( feature, layer)

nesting one in the other we can get a value from another layer. A kind of join on the fly:

attribute( getFeature( 'aLayer','aField',12345),[%MYFIELD%])

I thought it would be useful to extend the Referencing toolset, including more complex Analytical and spatial functions. To do this I found the excellent suggestions of Nathan Woodrow that explain how to extend field calculator default function set: http://nathanw.net/2012/11/10/user-defined-expression-functions-for-qgis/

Basically It’s about develop python decorators to qgsfunction class

from qgis.core import QgsExpression
@qgsfunction(Number of parameter, "Group", register=False, usesGeometry=True)
 def funcname(values, feature, parent):
 """
 Comment to function that appears in helper box
 """
 parameter1 = values[0]
 parameter2 = values[1]
 parameter3 = values[2]
[some python code to do something useful with provided values]
if result:
 return result
 else
 parent.setEvalErrorString("Error")
class plugin:
def init(self):
 QgsExpression.registerFunction(funcname)
def unload(self):
 QgsExpression.unregisterFunction('funcname')

This function can be used in field calculator as:

funcname(parameter1,parameter2,parameter3)

As we can see to the python function are passed 3 parameters:
values, a list that contains the parameter passed to function in field calculator. (The total number of parameters is specified in the first argument of decorator)
feature: a reference to the currently handled qgsfeature
parent: a reference used for comunication with field calculator

From the 2.4, If the geometry is needed for the processing of function a directive has to be declared: usesGeometry=True, otherwise by default, the feature reference passed has null geometry.

Using this technique I developed a plugin to provide a ‘Reference’ group under field calculator containing the following functions:

Table functions:

dbvalue(targetLayer,targetField,keyField,conditionValue)
Retrieve first targetField value from targetLayer when keyField is equal to conditionValue

dbvaluebyid('targetLayer','targetField',featureID)
Retrieve the targetField value from targetLayer using internal feature ID

dqquery(targetLayer,targetField,whereClause)
Retrieve first targetField value from targetLayer when whereClause is true

dbsql(connectionName,sqlQuery)
Retrieve results from SQL query

WKT functions:

WKTcentroid('WKTgeometry')
Return the center of mass of the given geometry as WKT point geometry

WKTpointonsurface('WKTgeometry')
Return the point within  the given geometry

WKTlenght('WKTgeometry')
Return the length of the given WKT geometry

WKTarea('WKTgeometry')
Return the area of the given WKT geometry

Geometry functions:

geomRedef('WKTgeometry')
redefine the current feature geometry with a new WKT geometry (experimental!)

geomnearest(targetLayer,targetField)
Retrieve target field value from the nearest target feature in target layer

geomdistance('targetLayer','targetField',distanceCheck)
Retrieve target field value from target feature in target layer if target feature is in distance

geomwithin(targetLayer,targetField)
Retrieve target field value when source feature is within target feature in target layer

geomtouches(targetLayer,targetField)
Retrieve target field value when source feature touches target feature in target layer

geomintersects(targetLayer,targetField)
Retrieve target field value when source feature intersects target feature in target layer

geomcontains(targetLayer,targetField)
Retrieve target field value when source feature contains target feature in target layer

geomcontains(targetLayer,targetField)
Retrieve target field value when source feature is disjoint from target feature in target layer

geomequals(targetLayer,targetField)
Retrieve target field value when source feature is equal to target feature in target layer

geomtouches(targetLayer,targetField)
Retrieve target field value when source feature touches target feature in target layer

geomoverlaps(targetLayer,targetField)
Retrieve target field value when source feature overlaps target feature in target layer

geomcrosses(targetLayer,targetField)
Retrieve target field value when source feature crosses target feature in target layer

I hope that the plug-in will be useful and will suggest new ideas

I hope that the plug-in will be useful and will bring new ideas on new functions and tools

Download plugin
Advertisements

10 comments

  1. Dave Morgan

    Great plugin. Having used MapInfo for a long time, the ability to add an attribute from one layer to another based on a spatial relationship is something I’ve missed in QGIS.

    • enricofertest

      Now in 2.6 Brighton, with calculated fields the reference functions, you can build complex projects where layers are dinamically linked each others without modifying existing tables… The only problem is the performance… If you are interested in those reletions have a try to a development version of a new plugin: https://github.com/enricofer/spatialJoin/archive/master.zip
      It use the new functions to realize spatialJoins…
      To install the plugin, unzip file and put the content in [User profile directory]/.qgis/python/plugins

  2. martel

    Hello. I’ve got a one question. Is there a possibility for geometry functions, that it will be works for two layers which have a different projection?

    • enricofertest

      Hi, actually functions operate on native, not reprojected, features geometries. Extending the use to layers with different srs it’s possible even if I fear it could bring to poor performance. Better if implemented with the restriction to OTF mode on…. Maybe in the future.

  3. Dejan

    Hi, I imported table layer with 2 columns as layer from delimited text files – no geometry (attribute only). I want to access the value in 2nd column when value in 1st column is greater than some value. I used the dbquery function and it is not working.
    dbquery( ‘mylayer’,’second’,’first > value’)
    and it returns the value in the 1st row
    In case when I use
    dbquery( ‘mylayer’,’second’,’first = value’)
    it is not working at all. Using QGIS 2.8.8

  4. Pingback: QGIS: geomnearest() | pigrecoinfinito

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s