voici un pseudo-code pour commencer. J'espère que cela aide et que quelqu'un aura le temps de fournir le code complet (je n'en ai pas pour le moment)
La première chose à faire est de boucler sur le point et de sélectionner les lignes situées à l'intérieur de la distance seuil de chaque point. Cela peut être fait avec QgsSpatialIndex
Dans la première boucle, la deuxième chose à faire est de boucler sur les lignes sélectionnées et de trouver le point le plus proche sur la ligne. Cela peut être fait directement sur la base de QgsGeometry :: narrowSegmentWithContext
double QgsGeometry :: étroitementSegmentWithContext (const QgsPoint & point, QgsPoint & minDistPoint, int & afterVertex, double * leftOf = 0, double epsilon = DEFAULT_SEGMENT_EPSILON)
Recherche le segment de géométrie le plus proche du point donné.
Paramètres point Spécifie le point de recherche
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: retourne si le point se trouve à gauche du côté droit du segment (<0 signifie gauche,> 0 signifie droite) epsilon epsilon pour l'accrochage de segment (ajouté en 1.8)
la troisième étape (au sein de la première boucle) consisterait à mettre à jour la géométrie du point avec la géométrie du minDistPoint avec la plus petite distance
mise à jour avec du code (sur 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)