QGIS – Fixing Expression for Selecting Nearest Neighboring Points

expressionpointqgisselect-by-expression

The problem

In QGIS 3.20.0 (Win 10), I have an expression that does not work as expected and I don't understand why. On a point layer, one point is selected. When using Select by expression, the expression should select the N (freely definable number) nearest neighboring points.

Download the QGIS project with layers that show what I tried and for testing my expressions.

What I tried

I created an expression based on two elements (see the expressions below):

  1. Get the geometry of the selected point. Check the layer points_selection in the project I linked to see that this works: selected points get a red ring around them. Create this as variable @selected_point.

  2. Create a circle around each point with the radius such that it reaches to point furthest away of the N nearest neighbors, where N is the number defined in line 8 (here: 3). Thus the circle covers all N neighbors. The radius is multiplied by 1.01 to get a small buffer around the circle to be sure that the furthest points falls within that circle and not on its boundary. Check the layer points_circle in the linked project: you see that a circle is created. Create this as variable @circle.

As you can see in the linked project, both elements work as expected. You can also check the layer points_line: if you select a point, lines to the K nearest neighbors are drawn.

What doesn't work

The idea is now to combine these two elements using the expression within (@selected_point, @circle), where @selected_point and @circle are the variables created with the expressions from steps 1 and 2. The within function should return true for those features (points) where the selected point (@selected_point, step 1) is within the circle (@circle) around the current feature's geometry.

The question

What went wrong with the expression: why doesn't it work as expected (see also screenshot at the bottom)?

The expressions

  1. Selected Point
geometry (
    get_feature_by_id (
        @layer, 
        array_first( 
            aggregate(
                @layer, 
                'array_agg', 
                $id,
                filter:=is_selected( )
            )
        )
    )
)
  1. Circle around point:
make_circle (
    $geometry,
    array_max(
        array_foreach (
            overlay_nearest (
                @layer, 
                $geometry, 
                limit:=3
            ),
            length (
                make_line (
                    $geometry, 
                    @element
                )
            )
        )
    )*1.01,
    200
)
  1. Defining both elements as variables and stick them together with within – the whole expression looks like this:
with_variable (
    'selected_pt',
    geometry (
    get_feature_by_id (
        @layer, 
        array_first( 
            aggregate(
                @layer, 
                'array_agg', 
                $id,
                filter:=is_selected( )
            )
        )
    )
),
    with_variable (
        'circle',
        make_circle (
    $geometry,
    array_max(
        array_foreach (
            overlay_nearest (
                @layer, 
                $geometry, 
                limit:=9
            ),
            length (
                make_line (
                    $geometry, 
                    @element
                )
            )
        )
    )*1.01,
    200
),
        within (
            @selected_pt, 
            @circle
        )
    )
)

Screenshot: setting the limit to 9 nearest neighbors as argument of overlay_nearest, I here still get 12 features selected (the already selected point + 11 neighbors). As you see, there are points selected further away that are selected and others, closer, that are not selected. Both (number of points and which points are selected) does not correspond to what I expect:
enter image description here

Best Answer

The expression works correctly.
The problem is related to the circle variable which creates a circle for each point.
When we use within (@selected_pt, @circle), QGIS selects all the circles that contain the selected point. Below a screenshot that demonstrates the above, the selected point is contained only in 5 circles.

enter image description here