我确实有两个矢量层,其中一个是基于遥感“事件”的点层,第二个是来自本地研究的线层。
就我而言,这些是地震和构造断裂,但我想人们可以简单地选择“车祸和道路”作为一般示例。
因此,我想做的就是将点移动/复制到直线的最近点上,只要它在公差范围内(例如1-2km或0.0xx°),并使用新的点层(已移动+ attr y / n)。
有任何想法吗 ?
Linux,QGIS 1.8
我确实有两个矢量层,其中一个是基于遥感“事件”的点层,第二个是来自本地研究的线层。
就我而言,这些是地震和构造断裂,但我想人们可以简单地选择“车祸和道路”作为一般示例。
因此,我想做的就是将点移动/复制到直线的最近点上,只要它在公差范围内(例如1-2km或0.0xx°),并使用新的点层(已移动+ attr y / n)。
有任何想法吗 ?
Linux,QGIS 1.8
Answers:
发布了以下代码片段(在python控制台中测试)
from shapely.wkt import *
from shapely.geometry import *
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)
shplyLineString = shapely.wkt.loads(nearFeature.geometry().exportToWkt())
shplyPoint = shapely.wkt.loads(ptGeom.exportToWkt())
#nearest distance from point to line
dist = shplyLineString.distance(shplyPoint)
print dist
#the point on the road where the point should snap
shplySnapPoint = shplyLineString.interpolate(shplyLineString.project(shplyPoint))
#add rubber bands to the new points
snapGeometry = QgsGeometry.fromWkt(shapely.wkt.dumps(shplySnapPoint))
r = QgsRubberBand(canvas,QGis.Point)
r.setColor(Qt.red)
r.setToGeometry(snapGeometry,pointLayer)
编辑: 刚刚发现使用radicalSegmentWithContext的@radouxju方法在更少的代码行中给出了相同的结果。我想知道为什么他们想出这个奇怪的方法名称?应该是最近的PointPointOnGeometry之类的东西。
这样我们就可以避免身材匀称而喜欢
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
这是一个伪代码开始。我希望这会有所帮助,并希望有人有时间提供完整的代码(我目前没有)
首先要做的是在该点上循环并选择到每个点的阈值距离内的线。可以使用QgsSpatialIndex完成
在第一个循环中,第二件事是在选定的线上循环并找到该线上的最近点。这可以直接基于QgsGeometry :: closestSegmentWithContext完成
双QgsGeometry :: closestSegmentWithContext(const QgsPoint&point,QgsPoint&minDistPoint,int&afterVertex,double * leftOf = 0,double epsilon = DEFAULT_SEGMENT_EPSILON)
搜索最接近给定点的几何段。
参数point指定要搜索的点
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:返回该点是否位于线段右侧的左侧(<0表示左侧,> 0表示右侧)epsilon epsilon用于线段捕捉(在1.8中添加)
第三步(在第一个循环内)将包括用最小距离的minDistPoint几何更新点的几何
用一些代码更新(在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)