2

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?

Taras
  • 32,823
  • 4
  • 66
  • 137
Chace Carpenter
  • 191
  • 1
  • 6

2 Answers2

3

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: enter image description here

Matt
  • 16,843
  • 3
  • 21
  • 52
Babel
  • 71,072
  • 14
  • 78
  • 208
0

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

Val P
  • 3,858
  • 1
  • 8
  • 33