[GIS] Length of line on specific point (from point layer)

distancepostgispyqgisqgis

I have a line and a point layer. The points are manually set next to the line at specific places. Now I would like to get the distance between the end/start of the line and the points.

Distance from the black point along red line to start and to end of line:

  • Distance from Blackpoint #1 along the red Line to Start (i.e. 5km)
  • Distance from Blackpoint #1 along the red Line to End (i.e. 50km)
  • Distance from Blackpoint #2 along the red Line to Start (i.e. 10km)
  • Distance from Blackpoint #2 along the red Line to End (i.e. 45km)

As you can see in the image, the black points are not exactly ON the line. The closest line-node should give the result. I need to know the distances for every black point (I have hundreds of them!).

Line and Points

I'm looking for a QGIS, Python or PostGIS solution. Because the black points and the line are changeing some times, it would be great to have a "dynamic" approach without changeing the geometry of the line.

Best Answer

NOTE I edited the code because the questioner preferred to choose the track and the points he wanted to measure. The update in the code is the possibility to create a number of point output layers equal to the number of the line features but, if selected in the main dialog, this number could be lower.


I propose a solution using PyQGIS. My idea was creating a certain number of temporary points along the line and then using the nearest neighbor of them from each point of the input points (i.e. your Blackpoints) for measuring the distance from the start/end of the line.

Before posting the code, I need to do some remarks:

  1. in addition to the point layer and the line layer, my solution requires an additional parameter, called step, which is simply the spatial interval for the creation of the temporary points (remember that the accuracy of the result heavily depend from this parameter!);
  2. all the distances are calculated from the nearest neighbor of each Blackpoint to the start/end of the line (this means that the distance between the Blackpoint and its nearest neighbor is NOT evaluated, but I can immediately and easily add it to the code if needed).

The code will return a point memory layer which stores the same geometries and attributes of the input layer, plus two additional fields:

  • "DIST_START", which stores the distance from the start of the line;
  • "DIST_END", which stores the distance from the end of the line.

This is the code:

##Points=vector point
##Only_use_selected_points=boolean False
##Line=vector line
##Only_use_selected_lines=boolean False
##step=number 1

from qgis.core import *
from qgis.PyQt.QtCore import QVariant

layer1 = processing.getObject(Points)
crs = layer1.crs().toWkt()
layer2 = processing.getObject(Line)

if Only_use_selected_lines:
    line_feats = layer2.selectedFeatures()
else:
    line_feats = layer2.getFeatures()

for ft in line_feats:
    index = QgsSpatialIndex()
    tmp_points = {}
    line_geom = ft.geometry()
    len = line_geom.length()
    current = 0

    # This layer contains all the points created along the line (it isn't an output)
    temp_pts = QgsVectorLayer('Point?crs='+ crs, 'temp_pts' , 'memory')
    prov = temp_pts.dataProvider()
    # Uncomment the next line if you want to see the 'temp_pts' layer
    #QgsMapLayerRegistry.instance().addMapLayer(temp_pts)

    while current < len:
        point = line_geom.interpolate(current) # Create a point along the line at the current distance
        fet = QgsFeature()
        fet.setGeometry(point)
        (result, feat) = prov.addFeatures([fet])
        tmp_points[feat[0].id()] = current
        index.insertFeature(feat[0])
        current += step # Increase the distance by the step provided

    # This layer is the final output which stores the distances from the closest point along the line and the start/end of the line
    out = 'output_%s' % (ft.id())
    output = QgsVectorLayer('Point?crs='+ crs, out, 'memory') 
    prov2 = output.dataProvider()
    fields = layer1.pendingFields() # Fields from the input layer
    fields.append(QgsField('DIST_START', QVariant.Double, '', 10, 3)) # Name for the new field in the output layer
    fields.append(QgsField('DIST_END', QVariant.Double, '', 10, 3)) # Name for the new field in the output layer
    prov2.addAttributes(fields) # Add input layer fields to the outLayer
    output.updateFields()

    if Only_use_selected_points:
        point_feats = layer1.selectedFeatures()
    else:
        point_feats = layer1.getFeatures()

    for feat in point_feats:
        geom = feat.geometry()
        attrs = feat.attributes()
        nearest = index.nearestNeighbor(geom.asPoint(), 1)
        dist_start = tmp_points[nearest[0]]
        attrs.append(dist_start)
        dist_end = len - tmp_points[nearest[0]]
        attrs.append(dist_end)
        outGeom = QgsFeature()
        outGeom.setGeometry(geom)
        outGeom.setAttributes(attrs)
        prov2.addFeatures([outGeom])

    # Add the 'output' layer to the Layers panel
    QgsMapLayerRegistry.instance().addMapLayer(output)

Now let's give an example. Starting from these sample layers (14 points, the line has a length of about 16 km) and using a step of 1 m:

enter image description here

I obtain this point layer:

enter image description here

with this Attribute Table (the values are expressed in meters):

enter image description here