4

I have a point layer which represent transport stops. Each stops have an id and the transport line id.

I would like to have calculated the distance between every stops on the same transport line (closest one is enough). In the example attached, I would like the distance between Stop B1 and Stop B2, the distance between stop B2 and stop B3 etc.

Is this possible within QGIS? With distance matrix? enter image description here

3
  • I don't think you can do it with a matrix distance. matrix distance is just a table that give feature by feature the distance between one another. I know how to solve your issue with Postgres/postgis and St_lineInterpolate / St_length. For a plain Qgis solution, you can split your road by your points and keep the length of each part (it's what you want). Sep 9, 2021 at 11:59
  • The distance to previous and next stop as atribute to each point is what I am looking for yes @BERA
    – Arthur
    Sep 9, 2021 at 12:54
  • I have 7600 bus stops and 140 lines. I would rather prefer a unique way to do it rather than split the work in 140 parts... @Maximilienjaffrès
    – Arthur
    Sep 9, 2021 at 12:54

1 Answer 1

8

To calculate the distance between two points along a line geometry, you can use QGIS expressions based on the following functions (see below how to combine these elements). Points not even have to be exactly on the line, it works even if the point is slightly away: it then automatically takes the nearest point on the line.

  • line_substring to get the part of the initial line between two points
  • line_locate_point To get the current point on the line.
  • length to calculate the length of this substring (part of the line).

Let's suppose you have (change the names accordingly in the expression below if using other names):

  1. A line layer called line with an attribute line_id.
  2. A point layer calles stops with an attribute id (that numbers the points in the correct sequence: from the first to the next etc.) and another attribute on_line (indicating on what line the point lies). If you don't have such a field, see below (edit 3).

On the point layer, calculate the length from each point to the next one with this expression (e.g. using Field calculator to create a new attribute). To get the previous point, replace +1 at the end of line 10 with -1.

length(
    line_substring( 
        geometry (get_feature ('line','line_id', on_line)),  
        line_locate_point( 
            geometry (get_feature ('line','line_id', on_line)),
            geometry (get_feature_by_id (@layer, id))
        ),
        line_locate_point( 
            geometry (get_feature ('line','line_id', on_line)),
            geometry (get_feature_by_id (@layer, id+1))
        )
    )
)

Screenshot: the distance from each white point to the next one along the corresponding line is calcluated; the label of each points shows the distance to the next one on the same line: the yellow line shows one of the segments, the one that has a distance of 21.92. The last point in each line has no distance as there is no next point:

enter image description here

Edit 1

If you have your id field in the stop (point) layer in a string format as on your screenshot (like Stop A1, Stop B22, Stop C15 etc.), use this expression to extract the digits to create an integer id-field:

regexp_substr ("id", '(\\d+)')

Edit 2

As both your layers are in CRS EPSG:4326, the results are in degrees, not in meters. This does not make sense. There are two options to solve this: either reproject both layers to a CRS using meters as units (e.g. local UTM zone). Or use a slightly modified expressions that automatically handles this issue. Just replace 3857 in the second last line with the EPSG-code of the CRS you want to use:

length (
    transform (     
        line_substring( 
            geometry (get_feature ('line','line_id', on_line)),  
            line_locate_point( 
                geometry (get_feature ('line','line_id', on_line)),
                geometry (get_feature_by_id (@layer, id))
            ),
            line_locate_point( 
                geometry (get_feature ('line','line_id', on_line)),
                geometry (get_feature_by_id (@layer, id+1))
            )
        )
        ,@layer_crs, 
        'EPSG:3857'
))

Edit 3

It even works if you donf't have a field on_line. You can get this information as part of the expression with overlay_nearest In this case, replace geometry (get_feature ('line','line_id', on_line)) with array_first (overlay_nearest ('line', $geometry)) - so the expression looks like:

length(
    line_substring( 
        array_first (overlay_nearest ('line', $geometry)),  
        line_locate_point( 
            array_first (overlay_nearest ('line', $geometry)),
            geometry (get_feature_by_id (@layer, id))
        ),
        line_locate_point( 
            array_first (overlay_nearest ('line', $geometry)),
            geometry (get_feature_by_id (@layer, id+1))
        )
    )
)
8
  • hi, thank you for this complete tuto. As my stop_id field is a string text this do not work. I'am trying to extract a part of this field to try your method. Will keep you update
    – Arthur
    Sep 9, 2021 at 13:24
  • 1
    You can create another field, e.g. using $id: if the points were created in the right order, this should be enough.
    – Babel
    Sep 9, 2021 at 13:25
  • 1
    Adeed a way to extract the number out of your string - see bottom of the solution.
    – Babel
    Sep 9, 2021 at 13:33
  • I have already an id field with unique id (but not unique for each route, unique for the complete table tehrefore there is 7600 ids). I've tried with this field and could run the expression. But lengh result looks completely false. Got '0,0052051305248736303' as a result when expect around 500m (verified within QGIS with distance tool'. Maybe I've done something wrong.
    – Arthur
    Sep 9, 2021 at 14:03
  • 1
    That's why it doesn't work: your results are in degrees, not meters. This does not make sense. I added a solution for this above, at the bottom, Edit 2
    – Babel
    Sep 9, 2021 at 14:17

Your Answer

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

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