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.