Aquí hay un pseudocódigo para comenzar. Espero que esto ayude y que alguien tenga tiempo para proporcionar el código completo (no lo tengo en este momento)
Lo primero que debe hacer es recorrer el punto y seleccionar las líneas que se encuentran dentro de la distancia umbral a cada punto. Esto se puede hacer con QgsSpatialIndex
Dentro del primer bucle, lo segundo que debe hacer es recorrer las líneas seleccionadas y encontrar el punto más cercano en la línea. Esto se puede hacer directamente basado en QgsGeometry ::arestSegmentWithContext
double QgsGeometry ::arestSegmentWithContext (const QgsPoint & point, QgsPoint & minDistPoint, int & afterVertex, double * leftOf = 0, double epsilon = DEFAULT_SEGMENT_EPSILON)
Busca el segmento de geometría más cercano al punto dado.
Parámetros punto Especifica el punto de búsqueda
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: Devuelve si el punto se encuentra a la izquierda del lado derecho del segmento (<0 significa izquierda,> 0 significa derecha) epsilon epsilon para el ajuste de segmento (agregado en 1.8)
El tercer paso (dentro del primer bucle) consistiría en actualizar la geometría del punto con la geometría del minDistPoint con la menor distancia
actualizar con algún código (en 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)