4

I want to order a sequence of points along a polyline. I find solutions at this post and this one. They suggested to use line_locate_point() to identify the distance from each point in a point layer to the start point of an existing polyline layer:

line_locate_point (geometry:=geometry(get_feature('polyline', 'id', 939)), point:= $geometry)

in my case, 939 is the "id" of first link of the polyline layer. However the code returns all 0s or same value group.

The polyline shapefile and the point shapefile are in the same geographic coordinate system. I also tried project both shapefiles to the same coordinate system that has meter as the units. However, the results are either all 0s, or same value group, as shown in the picture below. the returned results are in the last column with two values:818 and 0.

enter image description here

The polyline layer is connected. Shortest path algorithm can find a route from the beginning point to the end point of the polyline.

Taras
  • 32,823
  • 4
  • 66
  • 137
Yuandong
  • 41
  • 3

2 Answers2

3

You may try using the "Add autoincremental field" geoalgorithm.

Let's assume there are two layers: a point 'points' and a polyline 'poly_test' respectively, see image below.

input

Open the "Add autoincremental field" from the Processing Toolbox (Ctrl+Alt+T).

auto_window

In the 'Sort expression [optional]' use the following expression:

line_locate_point(geometry:=overlay_intersects('lines_test', expression:=$geometry)[0], point:=$geometry)

The above expression involves the overlay_intersects() function demonstrated from QGIS 3.16 onwards.

Press Run and get the output: result

Taras
  • 32,823
  • 4
  • 66
  • 137
  • 1
    I can't get it to work on multiple features, even if I populate the Group value by option, it always returns an incorrect order. – pigreco Mar 08 '22 at 08:45
  • 1
    Let me investigate this issue. thank you for your comment! – Taras Mar 08 '22 at 08:52
2

You can create directly a sequence number along the line using this expression on the point layer (e.g. with Field calculator to create a new attribute). Only thing to adapt: in line 3, replace line with the name of your line layer.

It even works with several line feautres and even if points are not exactly on the line (see screenshot below) - to exclude points that are not exactly on the line, see the version of the expression at the bottom.

with_variable (
    'line',
    'line',
    array_find (
        array_sort (
            array_foreach (
                array_agg(
                    $geometry, 
                    group_by:=array_first (
                        overlay_nearest(
                            @line,
                            $id
                        )
                    )
                ),
                line_locate_point(
                    array_first (
                        overlay_nearest(
                            @line,
                            $geometry
                        )
                    ),
                    @element
                )
            )
        ), 
        line_locate_point( 
            array_first (
                overlay_nearest(
                    @line,
                    $geometry
                )
            )
            ,
            $geometry
        )
    )+1
)

The expression measures the distance each point has from the start point along the line, orders it accordingly (nearest to start-point, 2nd nearest etc.) and assigns an ascending number in this sequence.

The expression in action, here used to create a dynamic label. The points were created randomly in no order: enter image description here

Edit

If you want to exclude points that are not exactly on the line, use this expression:

with_variable (
    'line',
    'line',
    if (
        within (
            $geometry, 
            collect_geometries (
                aggregate (
                    @line, 
                    'collect', 
                    $geometry
                )
            )
        ),
        array_find (
            array_sort (
                array_foreach (
                    array_agg(
                        $geometry, 
                        group_by:=array_first (
                            overlay_nearest(
                                @line,
                                $id
                            )
                        ),
                        filter:=within (
                            $geometry, 
                            collect_geometries (
                                aggregate (
                                    @line, 
                                    'collect', 
                                    $geometry
                                )
                            )
                        )
                    ),
                    line_locate_point(
                        array_first (
                            overlay_nearest(
                                @line,
                                $geometry
                            )
                        ),
                        @element
                    )
                )
            ), 
            line_locate_point( 
                array_first (
                    overlay_nearest(
                        @line,
                        $geometry
                    )
                )
                ,
                $geometry
            )
        )+1,''
    )
)

enter image description here

Babel
  • 71,072
  • 14
  • 78
  • 208
  • Remark: for more features that touch or intersect you, the overlay_nearest function messes up so I suggest using the get_feature function instead – pigreco Mar 08 '22 at 13:45