Mam dwie klasy cech przecinających się linii. Chcę znaleźć kąt w każdym punkcie przecięcia za pomocą ArcGIS 10 i Pythona.
Czy ktoś może pomóc?
Mam dwie klasy cech przecinających się linii. Chcę znaleźć kąt w każdym punkcie przecięcia za pomocą ArcGIS 10 i Pythona.
Czy ktoś może pomóc?
Odpowiedzi:
Istnieje stosunkowo prosty przepływ pracy. Eliminuje to potencjalne problemy, które dwie cechy mogą przecinać w więcej niż jednym punkcie. Nie wymaga skryptowania (ale można go łatwo przekształcić w skrypt). Można to zrobić przede wszystkim z menu ArcGIS.
Chodzi o to, aby wykorzystać warstwę punktów przecięcia, po jednym punkcie dla każdej odrębnej pary przecinających się polilinii. Musisz uzyskać mały kawałek każdej przecinającej się polilinii w tych punktach przecięcia. Użyj orientacji tych elementów, aby obliczyć ich kąty przecięcia.
Oto kroki:
Upewnij się, że każda funkcja polilinii ma unikalny identyfikator w tabeli atrybutów. Zostanie to wykorzystane później do połączenia niektórych atrybutów geometrycznych polilinii z tabelą punktów przecięcia.
Geoprocessing | Intersect uzyskuje punkty (upewnij się, że chcesz określić punkty dla wyniku).
Geoprocessing | Buffer pozwala buforować punkty o niewielką ilość. Spraw, aby był naprawdę mały, aby część każdej linii w buforze się nie wyginała.
Geoprocessing | Clip (zastosowany dwukrotnie) ogranicza oryginalne warstwy polilinii do samych buforów. Ponieważ tworzy to nowe zestawy danych dla jego wyników, kolejne operacje nie modyfikują oryginalnych danych (co jest dobrą rzeczą).
Oto schemat tego, co się dzieje: dwie warstwy polilinii, pokazane w kolorze jasnoniebieskim i jasnoczerwonym, wytworzyły ciemne punkty przecięcia. Wokół tych punktów małe bufory są zaznaczone na żółto. Ciemniejsze niebieskie i czerwone segmenty pokazują wyniki obcinania oryginalnych elementów do tych buforów. Reszta algorytmu działa z ciemnymi segmentami. (Nie widać tego tutaj, ale niewielka czerwona polilinia przecina dwie niebieskie linie we wspólnym punkcie, tworząc coś, co wydaje się być buforem wokół dwóch niebieskich polilinii. To tak naprawdę dwa bufory wokół dwóch nakładających się punktów przecięcia czerwono-niebieskiego. , ten diagram pokazuje w sumie pięć buforów.)
Użyj narzędzia AddField , aby utworzyć cztery nowe pola w każdej z tych przyciętych warstw: [X0], [Y0], [X1] i [Y1]. Będą trzymać współrzędne punktowe, dzięki czemu będą się podwajać i dadzą im dużą precyzję.
Oblicz geometrię (wywoływaną przez kliknięcie prawym przyciskiem myszy każdego nowego nagłówka pola) umożliwia obliczenie współrzędnych xiy punktów początkowych i końcowych każdej przyciętej polilinii: umieść je w [X0], [Y0], [X1] i [Y1], odpowiednio. Odbywa się to dla każdej przyciętej warstwy, dlatego potrzeba 8 obliczeń.
Użyj narzędzia AddField , aby utworzyć nowe pole [Kąt] w warstwie punktu przecięcia.
Połącz przycięte tabele z tabelą punktów przecięcia na podstawie wspólnych identyfikatorów obiektów. (Połączenia są wykonywane przez kliknięcie prawym przyciskiem myszy nazwy warstwy i wybranie „Złączenia i relacje”).
W tym momencie tabela przecięć punktów ma 9 nowych pól: dwa mają nazwy [X0] itd., A jedno ma nazwę [Kąt]. Alias pola [X0], [Y0], [X1] i [Y1], które należą do jednej z połączonych tabel. Nazwijmy je (powiedzmy) „X0a”, „Y0a”, „X1a” i „Y1a”.
Użyj kalkulatora pola, aby obliczyć kąt w tabeli skrzyżowań. Oto blok kodu Pythona do obliczeń:
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
Wyrażenie pola obliczeniowego jest oczywiście jedynie
c
Pomimo długości tego bloku kodu matematyka jest prosta: (dx, dy) jest wektorem kierunku pierwszej polilinii, a (dxa, dya) jest wektorem kierunku drugiej. Ich długości r i ra (obliczone za pomocą twierdzenia Pitagorasa) służą do normalizacji ich do wektorów jednostkowych. (Nie powinno być problemu z zerowymi długościami, ponieważ obcinanie powinno dawać cechy długości dodatniej.) Rozmiar ich produktu klinowego dx dya - dydxa (po podzieleniu przez r i ra) jest sinus kąta. (Użycie iloczynu klinowego zamiast zwykłego iloczynu wewnętrznego powinno zapewnić lepszą dokładność numeryczną dla kątów bliskich zeru.) Wreszcie kąt jest przekształcany z radianów na stopnie. Wynik będzie wynosił od 0 do 90. Zwróć uwagę na unikanie trygonometrii aż do samego końca: takie podejście prowadzi do uzyskania wiarygodnych i łatwych do obliczenia wyników.
Niektóre punkty mogą pojawiać się wiele razy w warstwie przecięcia. Jeśli tak, otrzymają wiele kątów z nimi związanych.
Buforowanie i obcinanie w tym rozwiązaniu są stosunkowo drogie (kroki 3 i 4): nie chcesz tego robić w ten sposób, gdy zaangażowane są miliony punktów przecięcia. Poleciłem go, ponieważ (a) upraszcza proces znajdowania dwóch kolejnych punktów wzdłuż każdej polilinii w sąsiedztwie jego punktu przecięcia i (b) buforowanie jest tak podstawowe, że łatwo jest to zrobić w dowolnym systemie informacji geograficznej - nie jest wymagane dodatkowe licencjonowanie powyżej podstawowego poziomu ArcMap - i zwykle daje prawidłowe wyniki. (Inne operacje „geoprzetwarzania” mogą nie być tak niezawodne).
!table1.x0!
.
Uważam, że musisz utworzyć skrypt Pythona.
Możesz to zrobić za pomocą narzędzi geoprzetwarzania i arcpy.
Oto główne narzędzia i pomysły, które mogą Ci się przydać:
Może być bardzo trudno zakodować krok 2 (również niektóre narzędzia wymagają licencji ArcInfo). Następnie możesz także spróbować przeanalizować wierzchołki każdej polilinii (grupując je według identyfikatora po przecięciu).
Oto sposób na zrobienie tego:
point_x
, point_y
)vert0_x
, vert0_y
) i drugie ( vert1_x
, vert1_y
) jego wierzchołki.tan0 = (point_y - vert0_y) / (point_x - vert0_x)
tan1 = (vert1_y - point_y) / (vert1_x - point_x)
tan1
jest równy tan2
, to znalazłeś dwa wierzchołki linii, które mają punkt przecięcia pomiędzy nimi i możesz obliczyć kąt przecięcia dla tej linii. W przeciwnym razie musisz przejść do następnej pary wierzchołków (druga, trzecia) i tak dalej.Ostatnio próbowałem to zrobić sam.
Moja wskazówka oparta jest na okrągłych punktach wokół przecięć linii, a także punktach znajdujących się w odległości jednego metra od wtargnięć. Dane wyjściowe to klasa elementów polilinii, która ma atrybuty liczby kątów na przecięciach i kącie.
Zauważ, że linie powinny być planowane w celu znalezienia skrzyżowań, a odniesienie przestrzenne musi być ustawione z poprawnym wyświetlaniem długości linii (moja to WGS_1984_Web_Mercator_Auxiliary_Sphere).
Działa w konsoli ArcMap, ale łatwo można go zmienić na skrypt w przyborniku. Ten skrypt używa tylko warstwy linii w spisie treści, nic więcej.
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)