Geographic Information Systems Asked on June 13, 2021
In QGIS 3.18, I have two point layers: layer_1
with attribute id_1
and layer_2
with id_2
. I want to use the overlay_nearest
function: overlay_nearest(layer[,expression][,filter][,limit=1][,max_distance][,cache=false])
Applied on layer_1
, I want to get for each point on layer_1
all points on layer_2
where id_2
has a different value from id_1
(to connect them by a line). However, I can’t find out how the syntax of the filter-argument works, how to refer to attribute values of two different layers for comparison.
The basic expression is: overlay_nearest( 'layer_2', $geometry, [filter])
How is the syntax of the [filter]
part? filter:="id1" <> "id2"
obviously doesn’t work. $id <> "id2"
and $id <> "id1"
don’t work either.
Edit: see this example here to make the idea clearer.
layer1
, labeled with id1
layer2
, labeled with id2
Each red point should be connected to it’s nearest white point, except when their id is the same. However, on the upper left (red arrows), you see that 12 is connected to 12, 17 to 17:
The expression used in geometry generator on layer1
for this is as follows (the filter condition is on lines 6 to 18):
collect_geometries (
array_foreach (
overlay_nearest(
'layer2',
$geometry,
filter:=attribute (
get_feature_by_id (
'layer1',
$id
),
'id1'
) <> attribute (
get_feature_by_id (
'layer2',
$id
),
'id2'
) ,
limit:=1
),
make_line (
$geometry,
@element
)
)
)
*Edit
See more here in the responses to the feature request, especially:
The purpose of the filter parameter is to reduce the dataset (layer2)
to use for research, so it’s applied before it proceeds to any overlay
test
I tested some configurations, tried to understand how it works. So ! I figure it out.
The scope of the filter
parameter in the expression function of overlay_nearest
is purely the layer set in the layer
argument, as if you put this expression in the layer
select by expression.
To achieve your goal, I came up with another solution. Instead of select the nearest $geometry
of the other layer, I selected the nearest $currentfeature
. And in the end, instead of building a line directly, I put an if
condition to return a NULL
geometry if the nearest layer_2
feature id2
equals the current id1
.
collect_geometries(
array_foreach(
overlay_nearest(
'layer_2',
$currentfeature,
limit:=1
),
if(
condition:=attribute(@element, 'id2') = "id1",
result_when_true:=NULL,
result_when_false:=make_line(geometry(@element), $geometry)
)
)
)
EDIT : display more connections / increase the overlay_nearest limit |
---|
With the QGIS expression above, if you increase the overlay_nearest
limit, say 2 instead of 1, if one of the collected geometries is NULL
because it meets the condition, it nullifies all the geometries and it's not what we want.
Just replace in this case the NULL
condition result with a zero lenght line :
collect_geometries(
array_foreach(
overlay_nearest(
'layer_2',
$currentfeature,
limit:=2 -- update here
),
if(
condition:=attribute(@element, 'id2') = "id1",
result_when_true:=make_line($geometry, $geometry), -- update here
result_when_false:=make_line(geometry(@element), $geometry)
)
)
)
EDIT : each layer_1 point have the same number of lines |
---|
Hey ! Yes it works but when
layer_1.id1 = layer_2.id2
I got only one line for this point ...
Okay ... I heard you, you are right, GIS is a demanding discipline so I'm reviewing my copy !
To achieve this, I defined a variable, placed on top for easy access, lines_per_point
. You can set the number of lines per point, here 2.
With the comment of @MrXsquared, I used array_filter
instead an if
condition in the array_for_each
, it drops all NULL values.
But ! I increased by 1 the overlay_nearest
limit of features returned (I considered that layer_2
identifiants are unique, so filter can drop for one layer_1
point only one line maximum (when layer_1.id1 = layer_2.id2
)).
Ok, it returns arrays of lenght 2 or 3. Here I used array_slice
to get only the two first features (beware, the slice doesn't work as a Python list, it includes the 2 bounds), so [0, 1], so [0, lines_per_point - 1].
We have now an array of length 2 with layer_2
features. So, I applied an array_foreach
to create the line with make_line(geometry(@element), $geometry)
.
(Obviously, you'll get different number of lines for each layer_1
points if the lines per point equals the number of features of layer_2
)
The QGIS expression below :
with_variable('lines_per_point', 2, -- adjust here
collect_geometries(
array_foreach(
array_slice(
array_filter(
overlay_nearest(
'layer_2',
$currentfeature,
limit:=@lines_per_point + 1
),
attribute(@element,'id2') <> "id1"
),
0, @lines_per_point - 1
),
make_line(geometry(@element), $geometry)
)
))
Correct answer by J. Monticolo on June 13, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP