In QGIS I have a point feature class that I want to snap to the nearest line feature class so that the point is on top of the line. How would I do this?
-
2gis.stackexchange.com/a/44848/276– Mapperz ♦Oct 13, 2022 at 17:08
-
You can find some useful code here gis.stackexchange.com/questions/237224/…– Val POct 13, 2022 at 18:18
-
Please, do not forget about "What should I do when someone answers my question?"– TarasOct 26, 2022 at 9:39
2 Answers
Use Menu Processing > Toolbox > Geometry by expression
on the point layer with this expression:
closest_point (
overlay_nearest ('name_of_your_line_layer',$geometry)[0],
$geometry
)
The same effect can be achieved using Geometry generator (for visualization purpose only, see here for details).
Solution using Geometry generator: blue = initial points, red = points snapped to closest line:
If you are looking for a Pyqgis solution you can try this code:
from math import sqrt
#define layers
points = QgsProject.instance().mapLayersByName("point")[0]
linelayer = QgsProject.instance().mapLayersByName("line")[0]
provider = linelayer.dataProvider()
#spatial index for polygonal layer (Archaeological Features)
spIndex = QgsSpatialIndex() #create spatial index object
linelayer.removeSelection()
feat = QgsFeature()
fit = provider.getFeatures() #gets all features in layer
#selected points on DP
selected_DP = []
new_points = []
for p_feat in points.selectedFeatures():
old_DP_point = p_feat.geometry().asPoint()
old_DP_point_Wkt = p_feat.geometry().asWkt()
selected_DP.append(old_DP_point)
old_DP_point_id = [p_feat.id()]
# insert features to index
while fit.nextFeature(feat):
spIndex.insertFeature(feat)
pt = selected_DP[0]
# QgsSpatialIndex.nearestNeighbor (QgsPoint point, int neighbors)
nearestIds = spIndex.nearestNeighbor(pt,1) # we need only one neighbour
single_nearestIds = []
if len(nearestIds) == 1:
single_nearestIds = nearestIds
if len(nearestIds) > 1:
single_nearestIds.append(nearestIds[-1])
#select nearest polygon by id
linelayer.select(nearestIds)
linelayer
for feature in linelayer.selectedFeatures():
g_poly = feature.geometry()
calc_new_geom = g_poly.closestSegmentWithContext(old_DP_point)
new_geom = calc_new_geom[1]
sqrDist = calc_new_geom[0]
real_distance = sqrt(sqrDist)
if real_distance <= 5:
new_pt_wtk = new_geom.asWkt()
points.startEditing()
points.beginEditCommand("Snap Drawing Point")
points.selectByIds(old_DP_point_id)
new_geometry = QgsGeometry.fromWkt(new_pt_wtk)
points.changeGeometry(old_DP_point_id[0], new_geometry)
# Save the changes in the buffer
points.triggerRepaint()
#iface.mapCanvas().refresh()
points.endEditCommand()
else:
print ('nudda')
linelayer.removeSelection()
The logic used in this code is the same used in a plugin that I've created some time ago. You can check the complete code here: https://github.com/PCA-Geodata/Snap-Point-QGIS-Plugin