вот псевдокод для начала. Я надеюсь, что это поможет и что у кого-то будет время предоставить полный код (у меня нет в настоящее время)
Первое, что нужно сделать, это зациклить точку и выбрать линии, которые находятся в пределах порогового расстояния до каждой точки. Это можно сделать с помощью QgsSpatialIndex
В первом цикле, второе, что нужно сделать, это зациклить выбранные линии и найти ближайшую точку на линии. Это можно сделать напрямую на основе QgsGeometry :: closestSegmentWithContext
double QgsGeometry :: closestSegmentWithContext (const QgsPoint & point, QgsPoint & minDistPoint, int & afterVertex, double * leftOf = 0, double epsilon = DEFAULT_SEGMENT_EPSILON)
Ищет ближайший сегмент геометрии к заданной точке.
Параметры point Определяет точку для поиска
minDistPoint Receives the nearest point on the segment
afterVertex Receives index of the vertex after the closest segment. The vertex before the closest segment is always afterVertex -
1 leftOf Out: Возвращается, если точка лежит слева от правой стороны сегмента (<0 означает слева,> 0 означает справа) epsilon epsilon для привязки сегмента (добавлено в 1.8)
третий шаг (в первом цикле) будет состоять в обновлении геометрии точки с геометрией minDistPoint с наименьшим расстоянием
обновить с некоторым кодом (на QGIS3)
pointlayer = QgsProject.instance().mapLayersByName('point')[0] #iface.mapCanvas().layer(0)
lineLayer = QgsProject.instance().mapLayersByName('lines')[0] # iface.mapCanvas().layer(1)
epsg = pointlayer.crs().postgisSrid()
uri = "Point?crs=epsg:" + str(epsg) + "&field=id:integer&field=distance:double(20,2)&field=left:integer&index=yes"
snapped = QgsVectorLayer(uri,'snapped', 'memory')
prov = snapped.dataProvider()
testIndex = QgsSpatialIndex(lineLayer)
i=0
feats=[]
for p in pointlayer.getFeatures():
i+=1
mindist = 10000.
near_ids = testIndex.nearestNeighbor(p.geometry().asPoint(),4) #nearest neighbor works with bounding boxes, so I need to take more than one closest results and further check all of them.
features = lineLayer.getFeatures(QgsFeatureRequest().setFilterFids(near_ids))
for tline in features:
closeSegResult = tline.geometry().closestSegmentWithContext(p.geometry().asPoint())
if mindist > closeSegResult[0]:
closePoint = closeSegResult[1]
mindist = closeSegResult[0]
side = closeSegResult[3]
feat = QgsFeature()
feat.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(closePoint[0],closePoint[1])))
feat.setAttributes([i,mindist,side])
feats.append(feat)
prov.addFeatures(feats)
QgsProject.instance().addMapLayer(snapped)