Finden Sie den Punkt heraus, der zwischen zwei parallelen Linien liegt


8

Ich habe ein Problem in ArcGIS. Ich arbeite an einer Navigationsdatenbank. In unserer Datenbank werden einspurige Straßen durch eine einzelne Linie dargestellt, während eine mehrspurige Straße (Straße mit Teiler in der Mitte) durch zwei parallele Linien (rote Linien im Bild) dargestellt wird.

Ich habe ein Punkt-Shapefile mit einigen Punkten, die in die mehrspurige Straße und einige außerhalb fallen.

Ich möchte ein ArcPy-Skript erstellen, das die Punkte findet, die in mehrspurige Straßen fallen. dh zwischen diesen parallelen Linien (im Bild markiert).

Ich weiß nicht, wie ich das erreichen soll. Kann mir jemand helfen?

Ein mehrspuriges Straßenproblem

Ich habe einige Übungen dazu gemacht und festgestellt, dass das Erstellen eines Puffers auf einer Seite der Linie innerhalb eines mehrspurigen Polygons (in Abbildung gezeigt) erstellt werden kann.

Geben Sie hier die Bildbeschreibung ein

Jetzt besteht das Problem darin, dass das Polygon tatsächlich die Linie überschreitet (dh die mehrspurige Grenze überlappt). so wird es unnötige Punkte fangen. Gibt es eine Möglichkeit, dieses Polygon an der Straßenlinie auszurichten?

Hinweis: Integrieren funktioniert hier nicht, da es auch die Straßenlinie verschiebt. Ich muss nur das Polygon entlang der Straßenlinie ausrichten.


So etwas wie Messen Sie den Azimut der Straße - Erstellen Sie Linien von jedem Punkt in Richtung Winkel Azimut + 90 Grad - Zählen Sie, wie viele Ihrer parallelen Linien diese Linie schneidet. Wenn null oder zwei -> draußen, wenn eins -> Sie haben es gefunden. Nur denken, kann funktionieren oder nicht. Eine andere Idee ist es, die Straße in zwei Richtungen in ein Polygon umzuwandeln und Punkte auszuwählen, die sich schneiden. Letzteres kann mit Python schwierig sein. Nun, das erste auch, wenn die Straßen gekrümmt sind. Aber mit einseitigem Puffer können Sie möglicherweise ganz schöne Straßenpolygone erstellen.
user30184

1
Hast du eine erweiterte Lizenz? Mit dem Near-Tool wäre das ganz einfach.
Radouxju

Ja, ich habe eine erweiterte Lizenz.
Akhil Kumar

Zuerst dachte ich daran, Pufferpolygon zu nehmen und dann dieses Polygon zu schneiden. und finden Sie heraus, welche Punkte in dieses geschnittene Polygon fallen. Das größte Problem ist jedoch, dass die Entfernung zwischen den Straßen nicht überall auf der Straße gleichbleibend ist. Irgendwo sind es nur 10 Meter, irgendwo um die 20 Meter, in diesem Fall wird die Polygon-Schnittlogik fehlschlagen
Akhil Kumar

1
Machen Sie einen Puffer auf der rechten Seite von 10 m von der linken Seite und einen Puffer auf der linken Seite von der rechten Seite. Auf diese Weise decken Sie eine Reichweite von 10 bis 20 m ab. Überlappungen verursachen keinen Schaden und Sie können die Polygone auch zuerst zusammenführen. Oder machen Sie ein Seitenpufferpolygon noch breiter und schneiden Sie es, indem Sie es mit der anderen Seite schneiden. Verwenden Sie Fantasie und spielen.
user30184

Antworten:


4

Ich würde unten arcpy (sogar manuell!) Algorithmus versuchen-

  1. Finden Sie die richtige Breite der zweispurigen Straßen. Hier müssen Sie möglicherweise Straßen mit derselben Breite gruppieren und das folgende Verfahren für jeden Cluster befolgen.
  2. Erstellen Sie einen Puffer für beide Linien in beide Richtungen (rechts und links) mit dieser Breite (oder etwas weniger - um den Straßenbereich sicherzustellen).
  3. Führen Sie das Schnittpunkt-Tool aus, um den überlappenden Bereich abzurufen.
  4. Ausführen Nach Position auswählen, um Punkte auszuwählen, die in dieses Polygon fallen.

Ich denke, das ist der richtige Weg. Finden Sie eine einfache Möglichkeit, die Linien zusammenzufügen, entweder durch Puffer oder schließen Sie die Linien, um ein einzelnes Polygon zu erstellen, und wählen Sie dann innerhalb aus.
Barrett

2

Ich würde sagen, das ist eine geometrische Übung.

PSEUDO-CODE:

  • Suchen Sie für jeden Punkt (schwarzer Punkt) die nächste Straße und die Punktprojektion auf dieser Straße (roter Punkt).
  • Zeichnen Sie eine kurze Linie (gestrichelt) in entgegengesetzter Richtung, beginnend am schwarzen Punkt
  • Finden Sie heraus, ob es einen Schnittpunkt zwischen der kurzen Linie und der gleichnamigen Straße gibt, blauer Stern. Wenn es einen gibt, ist der schwarze Punkt derjenige, nach dem wir suchen.

Geben Sie hier die Bildbeschreibung ein

Wie man sieht, gibt es Sonderfälle - eingekreiste schwarze Punkte:

  1. Sehr kurvenreiche 1-Linien-Straße. Dies kann beseitigt werden, indem a) nur mit Straßen mit zwei Linien gearbeitet wird oder b) sichergestellt wird, dass die FIDs von Straßen, die roten Punkt und Stern schneiden, unterschiedlich sind. Wenn die kurvenreiche Straße jedoch eine Kreuzung mit einer anderen Straße mit einer Linie hat, funktioniert dies möglicherweise nicht.
  2. Der schwarze Punkt befindet sich auf der Verlängerung einer genau senkrechten Straße mit einer Linie. In diesem Fall besteht die Möglichkeit, dass eine einspurige Straße als nächster Nachbar ausgewählt wird.
  3. Schwarzer Punkt sitzt auf der Linie.

Alle oben genannten Fälle sind sehr unwahrscheinlich. Dennoch scheint es am sichersten zu sein, nur mit zweilinigen Straßen zu arbeiten, dh sie in eine separate Feature-Class zu exportieren. Fall 3 ist lustig, wir überlassen es dem Zufall, da der kürzeste Abstand zur Linie niemals Null ist und somit eine "entgegengesetzte" Richtung des Strahls gefunden werden kann, der 2 Punkte verbindet.

Python-Implementierung:

import arcpy, traceback, os, sys
from arcpy import env
env.overwriteoutput=True

# things to change ---------
maxD=30
mxd = arcpy.mapping.MapDocument("CURRENT")
pointLR = arcpy.mapping.ListLayers(mxd,"NODES")[0]
lineLR = arcpy.mapping.ListLayers(mxd,"LINKS")[0]
sjOneToMany=r'D:\scratch\sj2.shp'
RDNAME='street'
# -------------------------
dDest=arcpy.Describe(lineLR)
SR=dDest.spatialReference

try:
    def showPyMessage():
        arcpy.AddMessage(str(time.ctime()) + " - " + message)
    g = arcpy.Geometry()
    geometryList=arcpy.CopyFeatures_management(pointLR,g)
    n=len(geometryList)
    endPoint=arcpy.Point()

    arcpy.SpatialJoin_analysis(pointLR, lineLR,sjOneToMany,"JOIN_ONE_TO_MANY","KEEP_COMMON","","WITHIN_A_DISTANCE",maxD)
    initFidList=(-1,)
    for fid in range(n):
        query='"TARGET_FID" = %s' %str(fid)
        nearTable=arcpy.da.TableToNumPyArray(sjOneToMany,("TARGET_FID","JOIN_FID"),query)
        if len(nearTable)<2:continue
        fidLines=[int(row[1]) for row in nearTable]
        query='"FID" in %s' %str(tuple(fidLines))
        listOfLines={}
        blackPoint=geometryList[fid]
        with arcpy.da.SearchCursor(lineLR,("FID", "Shape@","STREET"),query) as rows:
            dMin=100000
            for row in rows:
                shp=row[1];dCur=blackPoint.distanceTo(shp)
                listOfLines[row[0]]=row[-2:]
                if dCur<dMin:
                    fidNear,lineNear, roadNear=row
                    dMin=dCur
            chainage=lineNear.measureOnLine(blackPoint)
            redPoint=lineNear.positionAlongLine (chainage).firstPoint
            smallD=blackPoint.distanceTo(redPoint)
            fp=blackPoint.firstPoint
            dX=(redPoint.X-fp.X)*(maxD-smallD)/smallD
            dY=(redPoint.Y-fp.Y)*(maxD-smallD)/smallD
            endPoint.X=fp.X-dX;endPoint.Y=fp.Y-dY
            dashLine=arcpy.Polyline(arcpy.Array([fp,endPoint]),SR)

            for n in listOfLines:
                if n==fidNear:continue
                line, road=listOfLines[n]
                if road!=roadNear:continue
                blueStars=dashLine.intersect(line,1)
                if blueStars.partCount==0:continue
                initFidList+=(fid,); break
    query='"FID" in %s' %str(initFidList)
    arcpy.SelectLayerByAttribute_management(pointLR, "NEW_SELECTION", query)
    arcpy.AddMessage ('\n %i point(s) found' %(len(initFidList)-1))
except:
    message = "\n*** PYTHON ERRORS *** "; showPyMessage()
    message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
    message = "Python Error Info: " +  str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()            

Es gibt eine andere mögliche Lösung, die vielleicht eleganter ist. Es geht um Triangulation. Lassen Sie mich wissen, ob es von Interesse ist und ich werde meine Antwort aktualisieren


Das ist ziemlich komplex, wow. Es scheint viel einfacher zu sein, aus den Linien ein Polygon zu erstellen und dann Raycasting zu verwenden . Das Bestimmen, ob sich ein Punkt auf einer Linie befindet, sollte ebenfalls einfach sein.
Paul

1
Wenn Sie Polygone aus korrekten Linien erstellen können, ist kein Casting erforderlich. Nach Standort auswählen reicht aus. Das Erstellen von Polygonen ist jedoch eine Herausforderung
FelixIP

Wird es gut in Kurven funktionieren - nur zur Verdeutlichung :)
SIslam

1
@SIslam sollte auch bei großen Biegungen funktionieren, ähnlich wie in Fall 1 (siehe ob n == fidNear: continue). Nun, wenn es keine einspurige Straße gibt. Ich denke immer wieder, dass das Auflösen helfen kann, aber nicht immer
FelixIP

@ Islam Ups! Dies ist nicht der Fall, da die Bedingung (wenn n == fidNear: continue) Punkte eliminiert, die außerhalb der Biegung sitzen, aber den Punkt innen als einen Punkt außerhalb markiert. Scharfe Drehung erforderlich, Radius kleiner als Breite?
FelixIP

0

Da die Straßen parallel sind, habe ich angenommen, dass sie mit dem Copy ParallelWerkzeug in der Bearbeitungssymbolleiste erstellt wurden, sodass das Linienpaar dieselbe Richtung hat. Wir können dann über die Koordinaten der ersten Zeile iterieren und sie einem Polygon hinzufügen und dann über die Rückseite der zweiten Zeile iterieren . Es gibt definitiv einen besseren Weg, um sich dem Ergreifen von Linienpaaren zu nähern. Der OID-Ansatz funktioniert, ist aber nicht sehr hübsch.

import collections
import arcpy

FC = "fc"
points = "points"
pgons = "pgons"
arcpy.env.overwriteOutput = True

def buildpoly(oid_coords):
    #create ddict of the form OID:<x1y1, x2y2, ..., xn-1yn-1, xnyn>
    ddict = collections.defaultdict(list)    
    for k,v in oid_coords:
        ddict[k].append(v)

    line1,line2 = ddict.keys()    

    #Assume that the parallel lines have same direction, so reverse the second
    arr = arcpy.Array()
    arr.extend(arcpy.Point(*pt) for pt in ddict[line1])    
    arr.extend(arcpy.Point(*pt) for pt in ddict[line2][::-1])

    return arcpy.Polygon(arr)

#id is an integer field that pairs parallel lines together
unique = list(set(t[0] for t in arcpy.da.SearchCursor(FC, "id")))
polygons = []
for uni in unique:
    polygons.append(buildpoly([r for r in row] for row in arcpy.da.SearchCursor(FC,
                                                                                ["OID@", "SHAPE@XY"],
                                                                                "id={}".format(uni),
                                                                                explode_to_points=True)))


arcpy.CopyFeatures_management(polygons, pgons)

Von dort aus ist es ein Aufruf, die Ebene nach Ort zu schneiden / auszuwählen / was haben Sie. Beachten Sie, dass das Sgeformte Polygon nicht perfekt ist, da ich es freihändig gezeichnet habe und es einige Bögen explode_to_pointsgibt, die nicht richtig funktionieren. Einfach laufen Densifyoder gleichwertig.

Geben Sie hier die Bildbeschreibung ein


Dies ist ein Straßennetzdatensatz, daher werden 1-spurige Straßen über einen Knoten mit 2 Spuren verbunden, dh es gibt keine Paare paralleler Merkmale
FelixIP

Vielleicht möchten Sie Ihre Lösung erweitern, indem Sie zuerst die Auflösung nach einzelnen Straßennamen hinzufügen (keine m-Teile) und als Ergebnis Fälle von 1 oder 2 Zeilen berücksichtigen
FelixIP

@ FelixIP, ich bin nicht sehr vertraut mit Netzwerk-Datasets. Meine Lösung war größtenteils ein Proof of Concept, wie es mit einfachen Linien gemacht werden kann (OP kann es erweitern, um mAuflösung, Mehrteiligkeit usw. abzudecken ). Ich weiß nicht, wie solche Funktionen tatsächlich in einem Netzwerk dargestellt werden.
Paul

@ Paul Die Straße mit dem gleichen Namen kann durch 100er-Segmente dargestellt werden, die in verschiedenen Zeilen in der Tabelle stehen. Außerdem könnte eine zweispurige Straße irgendwo einspurig werden. Das Auflösen wird schlecht scheitern, wenn keine Teile nicht in (1,2) enthalten sind. Deshalb habe ich mich nicht für eine Triangulationslösung entschieden
FelixIP

1
@AkhilKumar, es spielt keine Rolle, ob sie ungefähr parallel sind. Dies verfolgt die vorhandenen Linien.
Paul
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.