5

I ran a program that created points on the nearest edge of a polygon, but it only appears in the iface.mapLayer() canvas.

The code is taken from the answer in: https://gis.stackexchange.com/a/94428

enter image description here

You can see that no layer is selected but the points are still showing.

How do I get the points onto a vector layer or a shapefile?

EDIT: This is the modified code

import math
from qgis.gui import *
from PyQt4.QtCore import Qt

lineLayer = iface.mapCanvas().layer(0)
pointLayer =  iface.mapCanvas().layer(1)
canvas =  iface.mapCanvas()
spIndex = QgsSpatialIndex() #create spatial index object
lineIter =  lineLayer.getFeatures()

for lineFeature in lineIter:
    spIndex.insertFeature(lineFeature) 

pointIter =  pointLayer.getFeatures()

for feature in pointIter:
    ptGeom = feature.geometry()
    pt = feature.geometry().asPoint()

    nearestIds = spIndex.nearestNeighbor(pt,1) # we need only one neighbour
    featureId = nearestIds[0]
    nearestIterator = lineLayer.getFeatures(QgsFeatureRequest().setFilterFid(featureId))
    nearFeature = QgsFeature()
    nearestIterator.nextFeature(nearFeature)   

    closeSegResult = nearFeature.geometry().closestSegmentWithContext(ptGeom.asPoint())
    closePoint = closeSegResult[1]
    snapGeometry = QgsGeometry.fromPoint(QgsPoint(closePoint[0],closePoint[1])) 

    p1 = ptGeom.asPoint()
    p2 = snapGeometry.asPoint()

    dist = math.hypot(p2.x() - p1.x(), p2.y() - p1.y())
    print dist

    r = QgsRubberBand(canvas,QGis.Point)
    r.setColor(Qt.red)
    r.setToGeometry(snapGeometry,pointLayer)
Noura
  • 3,429
  • 3
  • 20
  • 41
Tali
  • 105
  • 4
  • Taken exactly from the other post or modified? Can you show your code exactly as it is run? – Michael Stimson Feb 06 '17 at 23:13
  • @MichaelMiles-Stimson Just made an update. Pretty much the same. – Tali Feb 06 '17 at 23:21
  • Assuming your layer 1 is an editable point layer you should only need to reset the geometry like feature.setGeometry(p2) and update the feature. I can't recall exactly how to update a feature but perhaps this page might help http://docs.qgis.org/testing/en/docs/pyqgis_developer_cookbook/vector.html#modify-features – Michael Stimson Feb 07 '17 at 00:09
  • Are you wanting to know how to convert the memory layer to a shapefile so it is saved to disk? – artwork21 Feb 07 '17 at 13:24

1 Answers1

6

First we can create a point layer in memory using something like:

result = QgsVectorLayer("Point?crs=epsg:4326", "result", "memory")
pr = result.dataProvider()
fet = QgsFeature()

Then inside your for loop, we can add points to the memory layer using the coordinates of the snapped geometry:

fet.setGeometry(QgsGeometry.fromPoint(p2))
pr.addFeatures([fet])

Then finally add the memory layer into the canvas:

QgsMapLayerRegistry.instance().addMapLayer(result)

So the full code could look something like this:

import math
from qgis.gui import *
from PyQt4.QtCore import Qt

lineLayer = iface.mapCanvas().layer(0)
pointLayer =  iface.mapCanvas().layer(1)
canvas =  iface.mapCanvas()
spIndex = QgsSpatialIndex() #create spatial index object
lineIter =  lineLayer.getFeatures()

for lineFeature in lineIter:
    spIndex.insertFeature(lineFeature) 

result = QgsVectorLayer("Point?crs=epsg:4326", "result", "memory")
pr = result.dataProvider()
fet = QgsFeature()

pointIter =  pointLayer.getFeatures()
for feature in pointIter:
    ptGeom = feature.geometry()
    pt = feature.geometry().asPoint()
    nearestIds = spIndex.nearestNeighbor(pt,1) # we need only one neighbour
    featureId = nearestIds[0]
    nearestIterator = lineLayer.getFeatures(QgsFeatureRequest().setFilterFid(featureId))
    nearFeature = QgsFeature()
    nearestIterator.nextFeature(nearFeature)   
    closeSegResult = nearFeature.geometry().closestSegmentWithContext(ptGeom.asPoint())
    closePoint = closeSegResult[1]
    snapGeometry = QgsGeometry.fromPoint(QgsPoint(closePoint[0],closePoint[1])) 
    p1 = ptGeom.asPoint()
    p2 = snapGeometry.asPoint()
    dist = math.hypot(p2.x() - p1.x(), p2.y() - p1.y())
    print dist
    r = QgsRubberBand(canvas,QGis.Point)
    r.setColor(Qt.red)
    r.setToGeometry(snapGeometry,pointLayer)
    fet.setGeometry(QgsGeometry.fromPoint(p2))
    pr.addFeatures([fet])

QgsMapLayerRegistry.instance().addMapLayer(result)

Example:

Sample points and lines before we run the code:

Before running code

After we run the code, the red points are rubberband snapped points on canvas, the blue points are those saved in the memory layer:

After running code


EDIT:

If you want to add your original points also to the memory layer, we can add the following lines after we have created the layer:

# Create new memory layer
mlayer = QgsVectorLayer("Point?crs=epsg:4326", "result", "memory")
mlayer_data = mlayer.dataProvider()
feature = QgsFeature()

# Get original features from point layer and add to memory layer
feats = [feat for feat in pointLayer.getFeatures()]
attr = pointLayer.dataProvider().fields().toList()
mlayer_data.addAttributes(attr)
mlayer.updateFields()
mlayer_data.addFeatures(feats)

So now your full code might look something like the following (note: I changed some naming terms):

import math
from qgis.gui import *
from PyQt4.QtCore import Qt

lineLayer = iface.mapCanvas().layer(0)
pointLayer =  iface.mapCanvas().layer(1)

canvas =  iface.mapCanvas()
spIndex = QgsSpatialIndex() #create spatial index object
lineIter =  lineLayer.getFeatures()

for lineFeature in lineIter:
    spIndex.insertFeature(lineFeature) 

# Create new memory layer
mlayer = QgsVectorLayer("Point?crs=epsg:4326", "result", "memory")
mlayer_data = mlayer.dataProvider()
feature = QgsFeature()

# Get original features from point layer and add to memory layer
feats = [feat for feat in pointLayer.getFeatures()]
attr = pointLayer.dataProvider().fields().toList()
mlayer_data.addAttributes(attr)
mlayer.updateFields()
mlayer_data.addFeatures(feats)

pointIter =  pointLayer.getFeatures()
for feature in pointIter:
    ptGeom = feature.geometry()
    pt = feature.geometry().asPoint()
    nearestIds = spIndex.nearestNeighbor(pt,1) # we need only one neighbour
    featureId = nearestIds[0]
    nearestIterator = lineLayer.getFeatures(QgsFeatureRequest().setFilterFid(featureId))
    nearFeature = QgsFeature()
    nearestIterator.nextFeature(nearFeature)   
    closeSegResult = nearFeature.geometry().closestSegmentWithContext(ptGeom.asPoint())
    closePoint = closeSegResult[1]
    snapGeometry = QgsGeometry.fromPoint(QgsPoint(closePoint[0],closePoint[1])) 
    p1 = ptGeom.asPoint()
    p2 = snapGeometry.asPoint()
    dist = math.hypot(p2.x() - p1.x(), p2.y() - p1.y())
    print dist
    r = QgsRubberBand(canvas,QGis.Point)
    r.setColor(Qt.red)
    r.setToGeometry(snapGeometry,pointLayer)
    feature.setGeometry(QgsGeometry.fromPoint(p2))
    # Add rubberband snapped features to memory layer
    mlayer_data.addFeatures([feature])

QgsMapLayerRegistry.instance().addMapLayer(mlayer)
Joseph
  • 75,746
  • 7
  • 171
  • 282
  • I have a question though. Will all the features from the original points be stored in the new layer? And how could this be added? – Tali Feb 07 '17 at 22:42
  • 1
    @NathalieVonHuth - Most welcome, glad it worked! I've edited the post to include how to add the original features of your point layer before adding the rubberband snapped features :) – Joseph Feb 08 '17 at 10:17