Aqui está um pseudo-código para começar. Espero que isso ajude e que alguém tenha tempo para fornecer o código completo (não tenho no momento)
A primeira coisa a fazer é fazer um loop no ponto e selecionar as linhas que estão localizadas dentro da distância limite de cada ponto. Isso pode ser feito com QgsSpatialIndex
Dentro do primeiro loop, a segunda coisa a fazer é fazer um loop nas linhas selecionadas e encontrar o ponto mais próximo da linha. Isso pode ser feito diretamente com base em QgsGeometry :: closestSegmentWithContext
double QgsGeometry :: closestSegmentWithContext (const QgsPoint & point, QgsPoint & minDistPoint, int & afterVertex, double * leftOf = 0, epsilon duplo = DEFAULT_SEGMENT_EPSILON)
Procura o segmento mais próximo da geometria para o ponto especificado.
Parâmetros point Especifica o ponto para pesquisa
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: Retorna se o ponto estiver no lado esquerdo do lado direito do segmento (<0 significa esquerda,> 0 significa direita) epsilon epsilon para ajuste de segmento (adicionado em 1.8)
o terceiro passo (dentro do primeiro loop) consistiria em atualizar a geometria do ponto com a geometria do minDistPoint com a menor distância
atualizar com algum código (no 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)