Punkte auf Linien verschieben (~ Nachbarschaft)

14

Ich habe zwei Vektorebenen, von denen eine eine Punktebene ist, die auf "Ereignissen" durch Fernerkundung basiert, und die zweite ist eine Linienebene aus lokaler Forschung.

In meinem Fall sind dies Erdbeben und tektonische Störungen, aber ich denke, man könnte einfach "Autounfälle und Straßen" als allgemeines Beispiel wählen.

Was ich also tun möchte, ist, die Punkte auf den nächsten Punkt der Linien zu verschieben / zu kopieren, solange dieser innerhalb eines Toleranzabstands (z. B. 1-2 km oder 0.0xx °) liegt, wobei die neue Punktebene (+ attr) verschoben wird j / n).

Irgendwelche Ideen ?

Linux, QGIS 1.8

Chris Pallasch
quelle
3
Es würde eine PostGIS-Lösung geben: PostGIS: nächster Punkt auf einer Linienfolge zu einem bestimmten Punkt
unterdunkel
Suchen Sie eine vollständig automatisierte Funktion, um dies zu tun, oder wäre eine Art Einrastwerkzeug, um dies von Hand zu tun, in Ordnung?
Simbamangu
Ich habe eine ähnliche Frage gestellt, ich habe versucht, die Linie an den Punkten auszurichten, aber nie eine einfache Lösung gefunden. gis.stackexchange.com/questions/52232/…
GreyHippo
Was ist mit Triangulation und Distanzanpassung?
Huckfinn
Ich habe diese Frage zu einer Methode gefunden, die in ArcGIS mit Near funktioniert. Ging auf der Suche nach QGIS Near Equivalent und fand diesen Forumsbeitrag, in dem jemand GRASS v.distance vorschlug. Das führte mich zu diesem Tutorial , das eine Methode identifizieren kann. Vielleicht hat irgendwo da drin inzwischen jemand ein Plugin geschrieben?
Chris W

Antworten:

13

Veröffentlichte ein Code-Snippet (getestet in der Python-Konsole), das das Folgende tut

  1. Verwenden Sie QgsSpatialIndex, um die nächstgelegene Linienfunktion zu einem Punkt zu finden
  2. Finden Sie den nächstgelegenen Punkt auf dieser Linie zum Punkt. Als Abkürzung dafür habe ich ein formschönes Paket verwendet. Ich fand die QGis-Methoden dafür unzureichend (oder ich verstehe sie höchstwahrscheinlich nicht richtig)
  3. Gummibänder an den Einrastpositionen hinzugefügt
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)

Bearbeiten: Ich habe gerade festgestellt, dass die @ radouxju-Methode, die closerSegmentWithContext verwendet, die gleichen Ergebnisse in weniger Codezeilen liefert. Ich frage mich, warum sie auf diesen seltsamen Methodennamen gekommen sind? hätte so etwas wie "closerPointOnGeometry" sein sollen.

So können wir formschön ausweichen und tun gerne,

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
Vinayan
quelle
1
Laufen in Albtraum versuchen, diesen Python-Code zu formatieren .. Argh!
Vinayan
5

hier ist ein pseudocode zum starten. Ich hoffe, dass dies hilft und dass jemand Zeit hat, vollständigen Code bereitzustellen (ich habe im Moment keinen).

Zunächst müssen Sie eine Schleife um den Punkt ausführen und die Linien auswählen, die sich innerhalb des Schwellenabstands zu jedem Punkt befinden. Dies kann mit QgsSpatialIndex erfolgen

In der ersten Schleife müssen Sie als zweites die ausgewählten Linien durchlaufen und den nächstgelegenen Punkt auf der Linie finden. Dies kann direkt basierend auf QgsGeometry :: closerSegmentWithContext erfolgen

double QgsGeometry :: closerSegmentWithContext (const QgsPoint & point, QgsPoint & minDistPoint, int & afterVertex, double * leftOf = 0, double epsilon = DEFAULT_SEGMENT_EPSILON)

Sucht nach dem Geometriesegment, das dem angegebenen Punkt am nächsten liegt.

Parameter point Gibt den Punkt für die Suche an

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: Gibt zurück, ob der Punkt links oder rechts vom Segment liegt (<0 bedeutet links,> 0 bedeutet rechts). Epsilon epsilon für Segmentfang (hinzugefügt in 1.8)

Der dritte Schritt (innerhalb der ersten Schleife) würde darin bestehen, die Geometrie des Punkts mit der Geometrie des minDistPoint mit dem geringsten Abstand zu aktualisieren

Update mit etwas Code (auf 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)
radouxju
quelle