5

In the expression builder (QGIS 3.38) attached to the "Default value" setting of the Attribute Form, I'd like to assign (i.e. fill/update) an attribute of the entities of a "feature" layer the name of the nearest feature in a 2 km radius of either the closest airport or the closest harbor:

enter image description here

To this end, I'm trying to use the overlay_nearest function, something like this, just to grab the idea:

array_first(
  array_sort(
    array_cat(
      overlay_nearest(
        layer:='airports',
        expression:="name",
        limit:=1,
        max_distance:=5000
      ),
      overlay_nearest(
        layer:='harbors',
        expression:="name",
        limit:=1,
        max_distance:=5000
      )
    )
  )
) 

map sample

But this code sorts the results alphabetically by their names, not by their corresponding distances to the layer's current feature.

I guess I have to aggregate in a structure like a nested array, the names with their corresponding distances to the feature of the third layer but then how to sort those based on the distance and assign the corresponding name to the feature?

I wish the overlay_nearest would have a way to expose the computed distance (because at some point, it has to be computed in the background) but that's not the case.

Does anybody have an efficient and easy-to-maintain solution to achieve that?

1

2 Answers 2

3
  1. Get the nearest point on layer airports: use overlay_nearest() with the optional argument limit:=1. Do the same for layer harbors. You might also add a max_distance() argument.
  2. Combine both nearest points with union()
  3. Get the closest point to your input out of these two points using closest_point().

The whole expression to do so:

closest_point(
    union(
        overlay_nearest (
            'airports',
            @geometry,
            limit:=1,
            max_distance:=1000
        )[0],
        overlay_nearest (
            'harbors',
            @geometry,
            limit:=1,
            max_distance:=1000
        )[0]
    ), 
    @geometry
)

Screenshot: yellow points 1 and 2 each have either the closest airport or harbor within max. 1000 m, marked with a black ring, created by the expression above; point 3 does not have within 1000 m any harbor or airport:

enter image description here

1
  • Hello, first of all thank you for having taken time to answer. I noticed that some points of the two original layers were circled while the new feature was located at a much greater distance than the max_distance values specified, can you confirm this? To me, this is clearly a bug. But while this is a really great answer to actually "visualize" which points are the closest to the new feature, my question was more on how to fill/update an existing field in the attribute table of the new feature layer with the name of the closest of feature of the two original layers. Commented Sep 8 at 18:27
2

I think I finally managed to do it (I was hoping it would have been easier, somehow...):

This is what I do have in the "Default value" of the field reference of the feature layer:

array_first(
    array_sort(
        array_remove_all(
            array(
                array(
                    round(
                        distance(
                            @geometry,
                            overlay_nearest(
                                layer:='airports',
                                expression:=@geometry,
                                limit:=1,
                                max_distance:=1000
                            )[0]
                        ),
                        places:=3
                    ),
                    overlay_nearest(
                        layer:='airports',
                        expression:=name,
                        limit:=1,
                        max_distance:=1000
                    )[0]
                ),
                array(
                    round(
                        distance(
                            @geometry,
                            overlay_nearest(
                                layer:='harbors',
                                expression:=@geometry,
                                limit:=1,
                                max_distance:=1000
                            )[0]
                        ),
                        places:=3
                    ),
                    overlay_nearest(
                        layer:='harbors',
                        expression:=name,
                        limit:=1,
                        max_distance:=1000
                    )[0]
                )
            ),
            value:=array(NULL,NULL)
        ),
        ascending:=true
    )
)[1]

I can also add the same kind of "geometry generator" in the symbology properties as Babel suggested. Here for a circling point:

closest_point(
    collect_geometries(
        array_remove_all(
            array(
                overlay_nearest(
                    layer:='airports',
                    expression:=@geometry,
                    limit:=1,
                    max_distance:=1000
                )[0],
                overlay_nearest(
                    layer:='harbors',
                    expression:=@geometry,
                    limit:=1,
                    max_distance:=1000
                )[0]
            ),
            value:=NULL
        )
    ),
    @geometry
)

and for the linking line, simply embed the same code within make_line(closest_point(...),@geometry).

All this results in:

qgis result gif

You can see the live update in the attribute table at the bottom, which is what I was searching for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.