11

I would like to develop an expression that does the following nearest-neighbour analysis:

From a layer of points 'point_layer', I would like to calculate for each point the distance to the nearest point.

Once this distance value is calculated, the goal is to draw the buffer with the distance as a radius value and then symbolize the buffers with two categories:

  • Red green: points (buffers) located within 70 centimetres
  • Red red: points (buffers) located outside of 70 centimetres

Below I show a manual result recreation that should be obtained:

enter image description here

For my part, to try to obtain the expected result, I am working on this idea:

  1. To obtain the closest distances to each point I use the following function:

    array_sum (array_foreach (overlay_nearest(@layer, $geometry, limit:=2), length (make_line ( $geometry, @element))))
    

In this step, it seems that the result is correct, but it is not clear to me that it is necessary to use the function array_sum.

  1. Then I use the Geometry generator with the following expression that tries to integrate the previous function of distance calculation with the creation of buffers:
with_variable(
    'distance',
        array_sum ( array_foreach ( overlay_nearest( @layer, $geometry, limit:=2), length (make_line ($geometry, @element)))),
    collect_geometries(
        array_foreach(array_foreach(
                overlay_nearest( 
                    'point_layer',
                    $geometry, 
                    limit:=1),
                make_line(
                    $geometry,  
                    @element)),
            if (
                distance( 
                    start_point (@element), 
                    end_point ( @element))<@distance, 
                buffer (@element,@distance/2),
                buffer (@element,-@distance)))))

As you can see, with this expression I manage to draw the buffers. This is just a working idea.

Can you help with a new approach to enable me to complete the goal?

Previous result:

enter image description here

Attempt to implement the solution by @JGH user

I have implemented the solution proposed by user @JGH but it does not give the expected result. It seems that the virtual layer only loads 5 points:

enter image description here

It seems to be calculating the cumulative distance of 5 points. I am not sure.

It seems also that the distance values are too high.

1
  • 4
    Never compute distances using 3857. Use a suitable local projection that preserve distances (UTM or else). Also make sure each point has its own ID (no duplicates)
    – JGH
    Nov 15, 2023 at 12:19

2 Answers 2

8

You will need the computed distance to classify the output. One option is to create a virtual layer that will compute the distance, record it, buffer the point and that you can style as you wish using the rule based symbology.

enter image description here

Go to the menu Layer > Add Layer > Add/Edit Virtual Layer... and enter the following query. Replace myPointLayer with the real layer name, and you can add other fields from pt1.

SELECT pt1.id, pt2.id as nerest_pt_id,
       MIN(ST_Distance(pt1.geometry, pt2.geometry)) AS distance,
       st_buffer(pt1.geometry, MIN(ST_Distance(pt1.geometry, pt2.geometry))) as geometry
FROM myPointLayer AS pt1, myPointLayer AS pt2
WHERE pt1.id != pt2.id
GROUP BY pt1.id
ORDER BY distance DESC
7

Alternative solution

I use Rule-based symbology with the following expressions:

For Filter (calculate distances for each point and select distance <0.7):

distance(
  $geometry,
  array_get(
    overlay_nearest(
      layer:=@layer,
      expression:=$geometry,
      limit:=2
    ),
    1
  )
)<0.7

For the Geometry generator (draw buffers at distance):

with_variable(
    'dista',
distance(
  $geometry,
  array_get(
    overlay_nearest(
      layer:=@layer,
      expression:=$geometry,
      limit:=2
    ),
    1
  )
),
    collect_geometries (
        array_foreach (
            array_foreach (
                overlay_nearest( 
                    'point_layer',
                    $geometry, 
                    limit:=1
                ),
                make_line (
                    $geometry,  
                    @element
                )
            ),
            if (
                distance( 
                    start_point (@element), 
                    end_point (@element)
                )<@dista, 
                buffer(@element,@dista/1),
                buffer(@element,-@dista)
            )
        )
    )
)

The result:

enter image description here

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.