QGIS – How to Extract and Connect Two Closest Points with Matching IDs

pointproximityqgisqgis-expression

In QGIS, a have point vector Layer_A with a unique ID field and point vector Layer_B with multiple points for each Layer_A ID. For each point on Layer_A I want I need to create a line that connects the two closest points on Layer_B that have the same ID as the current Layer_A feature.

How can this be solved using the expression builder in the Model Designer?

My current approach for the Geometry by expression tool using Layer_A as input layer is:

make_line(
    overlay_nearest(
         @Layer_B_OUTPUT,
         $geometry,
         limit:=2
    )[1],
    overlay_nearest(
         @Layer_B_OUTPUT,
         $geometry,          
         limit:=2
    )[0]
)

Here's an example screenshot with Layer_A points in yellow and Layer_B points in grey:

Example screenshot

As expected, I am still missing a filter for overlay_nearest() to select only the features with a matching ID. And I don't know if limiting it to the first two occurrences actually returns the two closest features.

I tried to add a filter like this, but it does not seem to work:

with_variable(
    'current_id',
    "ID",
    make_line(
        overlay_nearest(
             @Layer_B_OUTPUT,
             $geometry,
             filter:= ID = @current_id, --This does not seem to work
             limit:=2
        )[1],
        overlay_nearest(
             @Layer_B_OUTPUT,
             $geometry,
             filter:= ID = @current_id, --This does not seem to work
             limit:=2
        )[0]
    )
)

Maybe it's related to the problem described here?

Best Answer

In theory you should be able to adapt your expression to include a filter as follows:

with_variable('layer_a_id',$id,
    make_line(
        overlay_nearest(
             layer:='Layer_B',
             expression:=$geometry,
             filter:="id"=@layer_a_id,
             limit:=2
        )[1],
        overlay_nearest(
             layer:='Layer_B',
             expression:=$geometry,          
             filter:="id"=@layer_a_id,
             limit:=2
        )[0]
    )
)

The problem is that there is a bug (probably) where variables in filter expressions appear to get ignored. See: Variable is not recognize in QGIS expression using overlay_nearest function for details.

So the workaround is - as noted in the above question - to wrap the overlay_nearest calls in an eval. It's very clunky and hopefully it will be fixed in QGIS at some point.

with_variable('layer_a_id',attribute('id'),
    make_line(
        eval(
            'overlay_nearest(
                 layer:=\'Layer_B\',
                 expression:=$geometry,
                 filter:="id"='|| @layer_a_id ||',
                 limit:=2
            )[1]'),
        eval(
            'overlay_nearest(
                 layer:=\'Layer_B\',
                 expression:=$geometry,
                 filter:="id"='|| @layer_a_id ||',
                 limit:=2
            )[0]')
    )
)

enter image description here