QGIS Distance Matrix – Creating Distance Matrix Along Line Layer in QGIS

field-calculatorlineqgisqgis-3

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

Best Answer

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))
        )
    )
)
Related Question