2

QGIS I have multiple lines in one layer (trench) and multiple points in another later.

I've searched the whole Internet for a plug-in which can: connect points to the nearest line, creating a new line in a new layer.

Maybe the plug-in is already there, but I couldn't find it...

What I have:

without_lines

Desired output:

created_new_lines

0

2 Answers 2

3

You can use the QGIS Python console for this. The output results as a memory layer. I'm using QGIS 2.18.28. Probably in QGIS 3 the code does not work, due to software changes.

Just paste this code into the Python console. You have to edit the code according to your layer names. In my case I have a shapefile points and a shapefile lines. You have to know the projection of your shapefiles and they have to have the same projection. Otherwise we have to do some transformation. Choose the right projection when adding the memory layer.

UPDATE

I've recognized that the ID of the nearest point (= line ID) was the same for every point. Now the code is updated and for every point there is the right line ID (e.g. for categorizing the data like in the picture). Additionally I've inserted a function to hide some QGIS Python console output. This reduces the running time of the code.

import math, os, sys
from contextlib import contextmanager

from operator import itemgetter

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from qgis.core import *
from qgis.gui import *
from qgis.networkanalysis import *

import time, datetime
start_ts = time.time()

# function to hide specific QGIS output
@contextmanager
def silence_stdout():
    new_target = open(os.devnull, "w")
    old_target, sys.stdout = sys.stdout, new_target
    try:
        yield new_target
    finally:
        sys.stdout = old_target

# define input layer points and lines
p_lyr = QgsMapLayerRegistry.instance().mapLayersByName('points')[0]
p_lyr.dataProvider().createSpatialIndex()
l_lyr = QgsMapLayerRegistry.instance().mapLayersByName('roads')[0]
l_lyr.dataProvider().createSpatialIndex()
lines = [feature for feature in l_lyr.getFeatures()]

# set up memory layer for the shortest distance
d_lyr = QgsVectorLayer('LineString', 'shortestDistance', 'memory')
QgsMapLayerRegistry.instance().addMapLayer(d_lyr)
prov = d_lyr.dataProvider()

# adding three attributes (holding point_id, road_id and the distance)
prov.addAttributes( [ QgsField("point_id", QVariant.Int), QgsField("line_id", QVariant.Int), QgsField("distance",QVariant.Int)])

# loop through all points and get the shortest distance to the next road
for i,points in enumerate(p_lyr.getFeatures()):
    with silence_stdout():
        # getting closest point on segment and its id from every line to a single point; getting minimum distance after sorting cswc list with itemgetter (sorting the l.geometry().closestSegmentWithContext(...) output)
        # use l.id() (Python id() function) instead of l["osm_id"] when you don't have an attribute "id" describing the line data, or replace it with another attribute name
        cswc = min([(l["osm_id"],l.geometry().closestSegmentWithContext(QgsPoint(points.geometry().asPoint()))) for l in lines], key=itemgetter(1))
        minDistPoint = cswc[1][1]   # nearest point on line
        minDistLine = cswc[0]       # line id of nearest point
        feat = QgsFeature()
        line = QgsGeometry.fromPolyline([QgsPoint(points.geometry().asPoint()), QgsPoint(minDistPoint[0], minDistPoint[1])]) # creating line between point and nearest point on segment
        feat.setGeometry(line)
        # use point.id() (Python id() function) instead of points["id"] when you don't have an attribute "id" describing the point data, or replace it with another attribute name
        feat.setAttributes([points["id"], minDistLine, line.geometry().length()])
        prov.addFeatures([feat])
    ts = time.time()
    print i

print('Time used: {}sec'.format(ts - start_ts))

d_lyr.updateExtents()
d_lyr.triggerRepaint()
d_lyr.updateFields()

In a test case I have a layer with 8000 points and a layer with about 7100 lines (OSM data). It takes about 10min (i5-5500, 8GB RAM) to calculate the shortest distance layer.

enter image description here

4
  • This won't work at my Qgis.. Maybe I'm doing something wrong..? Can you pm me, so I can share my shapefiles with you? Commented Mar 12, 2019 at 10:05
  • Please clarify: QGIS version (2 or 3), any errors (Python console or other). Do you know how to use the Python console? See the docs docs.qgis.org/2.18/en/docs/user_manual/plugins/….
    – Stefan
    Commented Mar 12, 2019 at 11:58
  • I am using both versions and tested in both versions.. I'm not familiar with the console... But PieterB's answer helped me out! Thanks! Commented Mar 13, 2019 at 15:15
  • Thanks @Stefan for sharing. This script works great!
    – jan_b
    Commented Oct 14, 2020 at 13:33
2

There is not such a plugin. And if you do not want to use python, you can combine two build-in algorithms in QGIS3

I think that this answer can solve your problem: https://gis.stackexchange.com/a/280787/7849

1
  • 1
    I never thought this approach is really fast. Using QGIS 2.18.xx I recommend to use the SAGA tool "Convert lines to points" in the Processing Toolbox. It takes only a few seconds to convert 1500 lines to 1.5 million points (1m spacing). And it takes only a moment to get the hub distance between 1500 points and the 1.5 million points.
    – Stefan
    Commented Mar 14, 2019 at 10:10

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.