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?

3

2 Answers 2

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

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.