У мене є два пересічні лінії класу функцій. Я хочу знайти кут у кожній точці перетину, використовуючи ArcGIS 10 та Python.
Хтось може допомогти?
У мене є два пересічні лінії класу функцій. Я хочу знайти кут у кожній точці перетину, використовуючи ArcGIS 10 та Python.
Хтось може допомогти?
Відповіді:
Існує порівняно простий робочий процес. Він долає потенційні проблеми, через які дві функції можуть перетинатися в більш ніж одній точці. Він не вимагає сценаріїв (але може легко перетворитися на сценарій). Це можна зробити в першу чергу з меню ArcGIS.
Ідея полягає у використанні шару точок перетину, по одній точці для кожної окремої пари пересічних поліліній. Потрібно отримати невеликий шматочок кожної пересічної полілінії в цих точках перетину. Використовуйте орієнтації цих частин, щоб обчислити їх кути перетину.
Ось такі кроки:
Переконайтесь, що кожна з полілінійних функцій має унікальний ідентифікатор у своїй таблиці атрибутів. Це буде використано пізніше для приєднання деяких геометричних атрибутів поліліній до таблиці точок перетину.
Геообробка | Перетинання отримує точки (не забудьте вказати потрібні точки для виводу).
Геообробка | Буфер дозволяє вам окупувати точки на невелику кількість. Зробіть це дійсно крихітним, щоб частина кожного рядка в буфері не згиналася.
Геопроцедура | Кліп (застосовується двічі) обмежує початкові шари полілінії лише буферами. Оскільки це створює нові набори даних для його виведення, наступні операції не змінять вихідні дані (що добре).
Ось схема того, що відбувається: два полілінічні шари, показані світло-блакитним та світло-червоним кольорами, створили темні точки перетину. Навколо цих точок крихітні буфери зображені жовтим кольором. Більш темні сині та червоні сегменти показують результати відсікання оригінальних особливостей цих буферів. Решта алгоритму працює з темними сегментами. (Тут ви не бачите, але крихітна червона полілінія перетинає дві сині лінії в загальній точці, утворюючи те, що, здається, буфер навколо двох синіх поліліній. Це дійсно два буфери навколо двох точок перекриття червоно-синього перетину. Таким чином , ця діаграма відображає всього п'ять буферів.)
Використовуйте інструмент AddField для створення чотирьох нових полів у кожному з цих обрізаних шарів: [X0], [Y0], [X1] та [Y1]. Вони будуть утримувати координати точок, тому зробіть їх подвійними та надайте їм велику точність.
Обчислення геометрії (викликається клацанням правою кнопкою миші на кожному новому заголовку поля) дозволяє обчислити x- і y- координати початкової та кінцевої точок кожної відсіченої полілінії: помістіть їх у [X0], [Y0], [X1] , і [Y1] відповідно. Це робиться для кожного обрізаного шару, тому потрібно 8 розрахунків.
Використовуйте інструмент AddField , щоб створити нове поле [Angle] у шарі точки перетину.
Приєднайте відрізані таблиці до таблиці перетину на основі загальних ідентифікаторів об'єктів. (Приєднання здійснюється за допомогою клацання правою кнопкою миші на назві шару та вибору "Приєднується та відноситься".)
На даний момент у таблиці перетину точок є 9 нових полів: два названі [X0] тощо, а одне - [Angle]. Псевдоніми поля [X0], [Y0], [X1] та [Y1], які належать до однієї з об'єднаних таблиць. Назвемо ці (скажімо) "X0a", "Y0a", "X1a" та "Y1a".
Використовуйте калькулятор поля для обчислення кута в таблиці перетину. Ось блок коду Python для обчислення:
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
Вираз обчислення поля - це, звичайно, просто
c
Незважаючи на довжину цього кодового блоку, математика проста: (dx, dy) - вектор напрямку для першого поліліну, а (dxa, dya) - вектор напрямку для другого. Їх довжини, r і ra (обчислюються за допомогою теореми Піфагора), використовуються для їх нормалізації до одиничних векторів. (З нульовою довжиною не повинно виникнути проблем, оскільки відсікання повинно створювати особливості позитивної довжини.) Розмір їхнього клинового продукту dx dya - dydxa (після ділення на r і ra) - синус кута. (Використання клинового виробу, а не звичайного внутрішнього виробу повинно забезпечувати кращу числову точність для майже нульових кутів.) Нарешті, кут перетворюється з радіанів у градуси. Результат буде лежати між 0 і 90. Зверніть увагу на те, щоб уникнути тригонометрії до самого кінця: такий підхід, як правило, дає надійні та легко обчислювані результати.
Деякі точки можуть з’являтися кілька разів у шарі перетину. Якщо так, вони отримають кілька кутів, пов'язаних з ними.
Буферизація та відсікання цього рішення відносно дорогі (кроки 3 та 4): ви не хочете робити це таким чином, коли задіяні мільйони точок перетину. Я рекомендував це, оскільки (а) це спрощує процес пошуку двох послідовних точок вздовж кожної полілінії в околицях його точки перетину, і (б) буферизація настільки основна, що це легко зробити в будь-якому ГІС - додаткові ліцензування не потрібні. вище базового рівня ArcMap - і зазвичай дає правильні результати. (Інші "геопроцесорні" операції можуть бути не настільки надійними.)
!table1.x0!
.
Я вважаю, що вам потрібно створити сценарій python.
Зробити це можна за допомогою інструментів для геообробки та архпії.
Ось основні інструменти та ідеї, які можуть бути корисними для вас:
Можливо, буде дуже важко кодувати крок 2 (також для деяких інструментів потрібна ліцензія ArcInfo). Потім ви також можете спробувати проаналізувати вертекси кожної полілінії (згрупувати їх за ідентифікацією після перетину).
Ось як це зробити:
point_x
, point_y
)vert0_x
, vert0_y
) та другу ( vert1_x
, vert1_y
) вертекси її.tan0 = (point_y - vert0_y) / (point_x - vert0_x)
tan1 = (vert1_y - point_y) / (vert1_x - point_x)
tan1
вона дорівнює tan2
, то ви знайшли дві вершини своєї лінії, які мають точку перетину між ними, і ви можете обчислити кут перетину для цієї лінії. Інакше вам доведеться перейти до наступної пари вершин (друга, третя) тощо.Останнім часом я намагався зробити це самостійно.
Моя підказка заснована на кругових точках навколо перетину ліній, а також на точках, розташованих на відстані одного метра від перетинів. Вихід - полілінійний клас функції, який має атрибути числа кутів на перетинах та куті.
Зауважте, що лінії повинні бути сплановані для того, щоб знайти перехрестя, а просторове значення має бути встановлено з відображенням правильної довжини лінії (моя WGS_1984_Web_Mercator_Auxiliary_Sphere).
Запуск у консолі ArcMap, але легко можна повернути до сценарію в панелі інструментів. Цей скрипт використовує лише рівень рядка в TOC, не більше того.
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)