aquí hay un pseudocódigo para comenzar. Espero que esto ayude y que alguien tenga tiempo para proporcionar el código completo (no tengo en este momento)
Lo primero que debe hacer es realizar un bucle en el punto y seleccionar las líneas que se encuentran dentro de la distancia de umbral a cada punto. Esto se puede hacer con QgsSpatialIndex
Dentro del primer bucle, lo segundo que hay que hacer es realizar un bucle en las líneas seleccionadas y encontrar el punto más cercano en la línea. Esto se puede hacer directamente en función de QgsGeometry :: closestSegmentWithContext
double QgsGeometry :: closestSegmentWithContext (const QgsPoint &
punto, QgsPoint & minDistPoint, int & afterVertex, doble *
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 para la 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 o al lado derecho del segmento (< 0 significa izquierda, > 0 significa derecha)
épsilon épsilon para segmentar segmentos (añadido 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 distancia más pequeña
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)