Ich habe zwei sich überschneidende Linien-Feature-Classes. Ich möchte den Winkel an jedem Schnittpunkt mit ArcGIS 10 und Python ermitteln.
Kann jemand helfen?
Ich habe zwei sich überschneidende Linien-Feature-Classes. Ich möchte den Winkel an jedem Schnittpunkt mit ArcGIS 10 und Python ermitteln.
Kann jemand helfen?
Antworten:
Es gibt einen relativ einfachen Workflow. Es überwindet die potenziellen Probleme, die auftreten können, wenn sich zwei Features in mehr als einem Punkt überschneiden. Es erfordert kein Scripting (kann aber leicht in ein Script umgewandelt werden). Dies kann hauptsächlich über das ArcGIS-Menü erfolgen.
Die Idee ist, eine Schicht von Schnittpunkten auszunutzen, einen Punkt für jedes einzelne Paar sich schneidender Polylinien. Sie müssen ein kleines Stück jeder sich schneidenden Polylinie an diesen Schnittpunkten erhalten. Verwenden Sie die Ausrichtungen dieser Teile, um ihre Schnittwinkel zu berechnen.
Hier sind die Schritte:
Stellen Sie sicher, dass jedes der Polylinien-Features eine eindeutige Kennung in seiner Attributtabelle hat. Dies wird später verwendet, um einige geometrische Attribute der Polylinien mit der Schnittpunkttabelle zu verbinden.
Geoverarbeitung | Schnitt erhält die Punkte (stellen Sie sicher, dass Sie Punkte für die Ausgabe angeben ).
Mit Geoverarbeitung | Puffer können Sie die Punkte um einen winzigen Betrag puffern. Machen Sie es wirklich winzig, damit sich der Teil jeder Zeile innerhalb eines Puffers nicht verbiegt.
Geoverarbeitung | Clip (zweimal angewendet) beschränkt die ursprünglichen Polylinienebenen nur auf die Puffer. Da hierdurch neue Datensätze für die Ausgabe erstellt werden, werden durch nachfolgende Vorgänge die ursprünglichen Daten nicht geändert (was eine gute Sache ist).
Hier ist eine schematische Darstellung dessen, was passiert: Zwei hellblaue und hellrote Polylinienebenen haben dunkle Schnittpunkte erzeugt. Um diese Punkte sind winzige Puffer in gelb dargestellt. Die dunkleren blauen und roten Segmente zeigen die Ergebnisse des Zuschneidens der ursprünglichen Features zu diesen Puffern. Der Rest des Algorithmus arbeitet mit den dunklen Segmenten. (Sie können es hier nicht sehen, aber eine winzige rote Polylinie schneidet zwei der blauen Linien an einem gemeinsamen Punkt und erzeugt so etwas wie einen Puffer um zwei blaue Polylinien. Es sind wirklich zwei Puffer um zwei überlappende Punkte des Rot-Blau-Schnittpunkts. Also In diesem Diagramm werden insgesamt fünf Puffer angezeigt.)
Verwenden Sie das AddField- Werkzeug, um vier neue Felder in jeder dieser abgeschnittenen Ebenen zu erstellen: [X0], [Y0], [X1] und [Y1]. Sie halten Punktkoordinaten, machen sie also doppelt und geben ihnen viel Präzision.
Mit Geometrie berechnen (durch Klicken mit der rechten Maustaste auf jeden neuen Feldkopf) können Sie die x- und y-Koordinaten der Start- und Endpunkte jeder abgeschnittenen Polylinie berechnen: Fügen Sie diese in [X0], [Y0], [X1] ein. bzw. [Y1]. Dies erfolgt für jede abgeschnittene Ebene, sodass 8 Berechnungen erforderlich sind.
Verwenden Sie das AddField- Werkzeug, um ein neues [Winkel] -Feld in der Schnittpunktebene zu erstellen.
Verknüpfen Sie die beschnittenen Tabellen mit der Schnittpunkttabelle auf der Grundlage gemeinsamer Objektkennungen. (Verknüpfungen werden ausgeführt, indem Sie mit der rechten Maustaste auf den Ebenennamen klicken und "Verknüpfungen und Verknüpfungen" auswählen.)
Zu diesem Zeitpunkt enthält die Punktkreuzungstabelle 9 neue Felder: Zwei werden mit [X0] usw. bezeichnet, und eines wird mit [Winkel] bezeichnet. Alias der Felder [X0], [Y0], [X1] und [Y1], die zu einer der verknüpften Tabellen gehören. Nennen wir diese (sagen wir) "X0a", "Y0a", "X1a" und "Y1a".
Verwenden Sie den Feldrechner , um den Winkel in der Schnittpunkttabelle zu berechnen. Hier ist ein Python-Codeblock für die Berechnung:
dx = !x1!-!x0!
dy = !y1!-!y0!
dxa = !x1a!-!x0a!
dya = !y1a!-!y0a!
r = math.sqrt(math.pow(dx,2) + math.pow(dy,2))
ra = math.sqrt(math.pow(dxa,2) + math.pow(dya,2))
c = math.asin(abs((dx*dya - dy*dxa))/(r*ra)) / math.pi * 180
Der Feldberechnungsausdruck ist natürlich nur
c
Trotz der Länge dieses Codeblocks ist die Mathematik einfach: (dx, dy) ist ein Richtungsvektor für die erste Polylinie und (dxa, dya) ist ein Richtungsvektor für die zweite. Ihre Längen r und ra (berechnet über den Satz von Pythagoras) werden verwendet, um sie zu Einheitsvektoren zu normalisieren. (Bei Nulllängen sollte es kein Problem geben, da das Abschneiden Merkmale positiver Länge erzeugen sollte.) Die Größe ihres Keilprodukts dx dyadydxa (nach Division durch r und ra) ist der Sinus des Winkels. (Die Verwendung des Keilprodukts anstelle des üblichen Innenprodukts sollte eine bessere numerische Genauigkeit für Winkel nahe Null bieten.) Schließlich wird der Winkel vom Bogenmaß in Grad umgewandelt. Das Ergebnis liegt zwischen 0 und 90. Beachten Sie, dass die Trigonometrie bis zum Ende vermieden wird: Dieser Ansatz führt tendenziell zu zuverlässigen und einfach zu berechnenden Ergebnissen.
Einige Punkte werden möglicherweise mehrmals in der Schnittmenge angezeigt. In diesem Fall werden ihnen mehrere Winkel zugeordnet.
Das Puffern und Zuschneiden in dieser Lösung ist relativ teuer (Schritte 3 und 4): Sie möchten dies nicht auf diese Weise tun, wenn Millionen von Schnittpunkten betroffen sind. Ich habe es empfohlen, weil (a) es das Finden von zwei aufeinanderfolgenden Punkten entlang jeder Polylinie in der Nähe ihres Schnittpunkts vereinfacht und (b) das Puffern so einfach ist, dass es in jedem GIS einfach ist - es ist keine zusätzliche Lizenzierung erforderlich über dem ArcMap-Basisniveau - und führt normalerweise zu korrekten Ergebnissen. (Andere "Geoverarbeitungs" -Operationen sind möglicherweise nicht so zuverlässig.)
!table1.x0!
.
Ich glaube, Sie müssen Python-Skript erstellen.
Sie können dies mit Geoverarbeitungswerkzeugen und Arcpy tun.
Hier sind die wichtigsten Tools und Ideen, die für Sie nützlich sein können:
Möglicherweise ist es sehr schwierig, Schritt 2 zu codieren (für einige Tools ist eine ArcInfo-Lizenz erforderlich). Dann können Sie auch versuchen, die Scheitelpunkte jeder Polylinie zu analysieren (nach ID nach Schnittpunkt gruppieren).
Hier ist der Weg, um es zu tun:
point_x
, point_y
)vert0_x
, vert0_y
) und zweiten ( vert1_x
, vert1_y
) Scheitelpunkt davon.tan0 = (point_y - vert0_y) / (point_x - vert0_x)
tan1 = (vert1_y - point_y) / (vert1_x - point_x)
tan1
gleich ist tan2
, haben Sie zwei Scheitelpunkte Ihrer Linie gefunden, zwischen denen sich ein Schnittpunkt befindet, und Sie können den Schnittwinkel für diese Linie berechnen. Andernfalls müssen Sie mit dem nächsten Vertecepaar (zweites, drittes) fortfahren und so weiter.Kürzlich habe ich versucht, es alleine zu machen.
Meine Hinweisfunktion basiert auf kreisförmigen Punkten um die Schnittpunkte von Linien sowie auf Punkten, die sich in einem Abstand von einem Meter von Schnittpunkten befinden. Bei der Ausgabe handelt es sich um eine Polylinien-Feature-Class, die Attribute für die Anzahl der Winkel in Bezug auf Schnittpunkte und Winkel enthält.
Beachten Sie, dass Linien planarisiert werden sollten, um Schnittpunkte zu finden, und dass der Raumbezug mit der korrekten Anzeige der Linienlänge festgelegt werden muss (meine ist WGS_1984_Web_Mercator_Auxiliary_Sphere).
Wird in der ArcMap-Konsole ausgeführt, kann aber problemlos in ein Skript in der Toolbox umgewandelt werden. Dieses Skript verwendet nur die Linienebene im Inhaltsverzeichnis, nicht mehr.
import arcpy
import time
mxd = arcpy.mapping.MapDocument("CURRENT")
df = mxd.activeDataFrame
line = ' * YOUR POLYLINE FEATURE LAYER * ' # paste the name of line layer here
def crossing_cors(line_layer):
mxd = arcpy.mapping.MapDocument("CURRENT")
df = mxd.activeDataFrame
arcpy.env.overwriteOutput = True
sr = arcpy.Describe(line_layer).spatialReference
dict_cors = {}
dang_list = []
with arcpy.da.UpdateCursor(line_layer, ['SHAPE@', 'OID@']) as uc:
for row in uc:
if row[0] is None:
uc.deleteRow()
with arcpy.da.UpdateCursor(line_layer, 'SHAPE@', spatial_reference = sr) as uc:
for row in uc:
line = row[0].getPart(0)
for cor in line:
coord = (cor.X, cor.Y)
try:
dict_cors[coord] += 1
except:
dict_cors[coord] = 1
cors_only = [f for f in dict_cors if dict_cors[f]!=1]
cors_layer = arcpy.CreateFeatureclass_management('in_memory', 'cross_pnt', "POINT", spatial_reference = sr)
arcpy.AddField_management(cors_layer[0], 'ANGLE_NUM', 'LONG')
with arcpy.da.InsertCursor(cors_layer[0], ['SHAPE@', 'ANGLE_NUM']) as ic:
for x in cors_only:
pnt_geom = arcpy.PointGeometry(arcpy.Point(x[0], x[1]), sr)
ic.insertRow([pnt_geom, dict_cors[x]])
return cors_layer
def one_meter_dist(line_layer):
mxd = arcpy.mapping.MapDocument("CURRENT")
df = mxd.activeDataFrame
arcpy.env.overwriteOutput = True
sr = arcpy.Describe(line_layer).spatialReference
dict_cors = {}
dang_list = []
cors_list = []
with arcpy.da.UpdateCursor(line_layer, 'SHAPE@', spatial_reference = sr) as uc:
for row in uc:
line = row[0]
length_line = line.length
if length_line > 2.0:
pnt1 = line.positionAlongLine(1.0)
pnt2 = line.positionAlongLine(length_line - 1.0)
cors_list.append(pnt1)
cors_list.append(pnt2)
else:
pnt = line.positionAlongLine(0.5, True)
cors_layer = arcpy.CreateFeatureclass_management('in_memory', 'cross_one_meter', "POINT", spatial_reference = sr)
ic = arcpy.da.InsertCursor(cors_layer[0], 'SHAPE@')
for x in cors_list:
ic.insertRow([x])
return cors_layer
def circles(pnts):
import math
mxd = arcpy.mapping.MapDocument("CURRENT")
df = mxd.activeDataFrame
arcpy.env.overwriteOutput = True
sr = df.spatialReference
circle_layer = arcpy.CreateFeatureclass_management('in_memory', 'circles', "POINT", spatial_reference = sr)
ic = arcpy.da.InsertCursor(circle_layer[0], 'SHAPE@')
with arcpy.da.SearchCursor(pnts, 'SHAPE@', spatial_reference = sr) as sc:
for row in sc:
fp = row[0].centroid
list_circle =[]
for i in xrange(0,36):
an = math.radians(i * 10)
np_x = fp.X + (1* math.sin(an))
np_y = fp.Y + (1* math.cos(an))
pnt_new = arcpy.PointGeometry(arcpy.Point(np_x,np_y), sr)
ic.insertRow([pnt_new])
del ic
return circle_layer
def angles(centers, pnts, rnd):
mxd = arcpy.mapping.MapDocument("CURRENT")
df = mxd.activeDataFrame
sr = df.spatialReference
line_lyr = arcpy.CreateFeatureclass_management('in_memory', 'line_angles', "POLYLINE", spatial_reference = sr)
arcpy.AddField_management(line_lyr[0], 'ANGLE', "DOUBLE")
arcpy.AddField_management(line_lyr[0], 'ANGLE_COUNT', "LONG")
ic = arcpy.da.InsertCursor(line_lyr[0], ['SHAPE@', 'ANGLE', 'ANGLE_COUNT'])
arcpy.AddField_management(pnts, 'ID_CENT', "LONG")
arcpy.AddField_management(pnts, 'CENT_X', "DOUBLE")
arcpy.AddField_management(pnts, 'CENT_Y', "DOUBLE")
arcpy.Near_analysis(pnts, centers,'',"LOCATION")
with arcpy.da.UpdateCursor(line, ['SHAPE@', 'OID@']) as uc:
for row in uc:
if row[0] is None:
uc.deleteRow()
with arcpy.da.UpdateCursor(pnts, [u'ID_CENT', u'CENT_X', u'CENT_Y', u'NEAR_FID', u'NEAR_DIST', u'NEAR_X', u'NEAR_Y'], spatial_reference = sr) as uc:
for row in uc:
row[0] = row[3]
row[1] = row[5]
row[2] = row[6]
uc.updateRow(row)
if row[4] > 1.1:
uc.deleteRow()
arcpy.Near_analysis(pnts, rnd,'',"LOCATION")
list_id_cent = []
with arcpy.da.UpdateCursor(pnts, [u'ID_CENT', u'CENT_X', u'CENT_Y', u'NEAR_FID', u'NEAR_DIST', u'NEAR_X', u'NEAR_Y', 'SHAPE@'], spatial_reference = sr) as uc:
for row in uc:
pnt_init = (row[-1].centroid.X, row[-1].centroid.Y)
list_id_cent.append([(row[1], row[2]), row[3], pnt_init])
list_id_cent.sort()
values = set(map(lambda x:x[0], list_id_cent))
newlist = [[y for y in list_id_cent if y[0]==x] for x in values]
dict_cent_angle = {}
for comp in newlist:
dict_ang = {}
for i, val in enumerate(comp):
curr_pnt = comp[i][2]
prev_p = comp[i-1][2]
init_p = comp[i][0]
angle_prev = math.degrees(math.atan2(prev_p[1]-init_p[1], prev_p[0]-init_p[0]))
angle_next = math.degrees(math.atan2(curr_pnt[1]-init_p[1], curr_pnt[0]-init_p[0]))
diff = abs(angle_next-angle_prev)%180
vec1 = [(curr_pnt[0] - init_p[0]), (curr_pnt[1] - init_p[1])]
vec2 = [(prev_p[0] - init_p[0]), (prev_p[1] - init_p[1])]
ab = (vec1[0] * vec2[0]) + (vec1[1] * vec2[1])
mod_ab = math.sqrt(math.pow(vec1[0], 2) + math.pow(vec1[1], 2)) * math.sqrt(math.pow(vec2[0], 2) + math.pow(vec2[1], 2))
cos_a = round(ab/mod_ab, 2)
diff = math.degrees(math.acos(cos_a))
pnt1 = arcpy.Point(prev_p[0], prev_p[1])
pnt2 = arcpy.Point(init_p[0], init_p[1])
pnt3 = arcpy.Point(curr_pnt[0], curr_pnt[1])
line_ar = arcpy.Array([pnt1, pnt2, pnt3])
line_geom = arcpy.Polyline(line_ar, sr)
ic.insertRow([line_geom , diff, len(comp)])
del ic
lyr_lst = [f.name for f in arcpy.mapping.ListLayers(mxd)]
if 'line_angles' not in lyr_lst:
arcpy.mapping.AddLayer(df, arcpy.mapping.Layer(line_lyr[0]))
centers = crossing_cors(line)
pnts = one_meter_dist(line)
rnd = circles(centers)
angle_dict = angles(centers, pnts, rnd)