How to refer to another layer in the field calculator?
Spatial joins are available in the field calculator after installing the refFunctions plugin.
geomwithin(targetLayer,targetField)
Out of the box, field calculator does not support spatial joins across feature layers. But, if you have a look at NathanW's post on the function editor for qgis expressions you will be able to make out that we can script our own data interaction.
The following script will allow you to express what you're after. It works by iterating through all features on the polygon layer and if there is a spatial join, then reference tabular data from the specified column:
from qgis.core import *
from qgis.gui import *
from qgis.utils import iface
allfeatures = None
index = QgsSpatialIndex()
indexMade = 0
refLayer = None
@qgsfunction(args="auto", group='Custom')
def spatialJoinLookup(layerName, refColumn, defaultValue, geom, feature, parent):
if geom is None:
return defaultValue
# globals so we don't create the index, refLayer more than once
global allfeatures
global index
global indexMade
global refLayer
# Get the reference layer
if refLayer is None:
for layer in iface.mapCanvas().layers():
if layerName == layer.name():
refLayer = layer
break
if refLayer is None:
raise Exception("Layer [" + layerName + "] not found")
# Create the index if not exists
if indexMade == 0:
index = QgsSpatialIndex()
allAttrs = layer.pendingAllAttributesList()
layer.select(allAttrs)
allfeatures = {feature.id(): feature for (feature) in refLayer.getFeatures()}
for f in allfeatures.values():
index.insertFeature(f)
indexMade = 1
# Use spatail index to find intersect
fid = None
ids = index.intersects(geom.boundingBox())
for id in ids:
fid = id
break # Only get the first match.
if fid is not None:
return allfeatures[fid].attribute(refColumn)
# Default
return defaultValue
Polygon Layer Example
Below is an example of a polygon layer that you might have. I've also created a corresponding point layer that you will see in the final image.
Expression Usage
Note, if you want to use a separate column you must change the second argument to match the column name in the polygon dataset. Example, you could use the 'AreaNumber' column, but would have to match the column type in the field calculator settings.
Result
You can see that the default column value has been applied where there is no spatial join, and the other's have matched the correct data. Note the script I've given will only join on the first match. You would need to create some other business logic if your polygons were overlapping.
It can be done in Field Calculator with function aggregate()
. In point layer create new field with field calculator expression like this:
aggregate(
layer:= 'polygon_layer_name',
aggregate:='concatenate',
expression:=joining_field_name,
concatenator:=', ',
filter:=intersects($geometry, geometry(@parent))
)
Where layer
is polygon layer name written like string, aggreagate
is aggregate function (can be used also sum etc.), expression
is field from values will be taken,concatenator
is joining character string (have to be set, even in this case) and filter
is filtering features based on expression (in this case interesects layer geometry with geometry of parent layer).
For more info check Aggregates QGIS documentation.
For automatic updates can be used virtual fields or you can set the expression as Default value in Attributes Form settings in Layer Properties (Attribute form setting documentation).