Знаходження кута між пересічними ознаками у двох класах функцій за допомогою ArcGIS Desktop та Python? [зачинено]


19

У мене є два пересічні лінії класу функцій. Я хочу знайти кут у кожній точці перетину, використовуючи ArcGIS 10 та Python.

Хтось може допомогти?


Я повторив метод Ваубера (спасибі) в сценарії python, використовуючи arcpy, але у мене виникають проблеми з розрахунком кута. Після завершення в Esri ArcMap (польовий калькулятор) він обчислює правильно. При обчисленні в сценарії python (знову ж таки, використовуючи калькулятор поля), він обчислюється неправильно (у вигляді десяткової). Це не просто перетворення радіану в задачу градусів. Функція дуги для обчислення поля як кута знаходиться нижче. Програмовані класи функцій (British National Grid). Чи потрібен додатковий крок, щоб обчислити кути в пітоні далеко від документа на карті
Енді

Відповіді:


13

Існує порівняно простий робочий процес. Він долає потенційні проблеми, через які дві функції можуть перетинатися в більш ніж одній точці. Він не вимагає сценаріїв (але може легко перетворитися на сценарій). Це можна зробити в першу чергу з меню ArcGIS.

Ідея полягає у використанні шару точок перетину, по одній точці для кожної окремої пари пересічних поліліній. Потрібно отримати невеликий шматочок кожної пересічної полілінії в цих точках перетину. Використовуйте орієнтації цих частин, щоб обчислити їх кути перетину.

Ось такі кроки:

  1. Переконайтесь, що кожна з полілінійних функцій має унікальний ідентифікатор у своїй таблиці атрибутів. Це буде використано пізніше для приєднання деяких геометричних атрибутів поліліній до таблиці точок перетину.

  2. Геообробка | Перетинання отримує точки (не забудьте вказати потрібні точки для виводу).

  3. Геообробка | Буфер дозволяє вам окупувати точки на невелику кількість. Зробіть це дійсно крихітним, щоб частина кожного рядка в буфері не згиналася.

  4. Геопроцедура | Кліп (застосовується двічі) обмежує початкові шари полілінії лише буферами. Оскільки це створює нові набори даних для його виведення, наступні операції не змінять вихідні дані (що добре).

    Малюнок

    Ось схема того, що відбувається: два полілінічні шари, показані світло-блакитним та світло-червоним кольорами, створили темні точки перетину. Навколо цих точок крихітні буфери зображені жовтим кольором. Більш темні сині та червоні сегменти показують результати відсікання оригінальних особливостей цих буферів. Решта алгоритму працює з темними сегментами. (Тут ви не бачите, але крихітна червона полілінія перетинає дві сині лінії в загальній точці, утворюючи те, що, здається, буфер навколо двох синіх поліліній. Це дійсно два буфери навколо двох точок перекриття червоно-синього перетину. Таким чином , ця діаграма відображає всього п'ять буферів.)

  5. Використовуйте інструмент AddField для створення чотирьох нових полів у кожному з цих обрізаних шарів: [X0], [Y0], [X1] та [Y1]. Вони будуть утримувати координати точок, тому зробіть їх подвійними та надайте їм велику точність.

  6. Обчислення геометрії (викликається клацанням правою кнопкою миші на кожному новому заголовку поля) дозволяє обчислити x- і y- координати початкової та кінцевої точок кожної відсіченої полілінії: помістіть їх у [X0], [Y0], [X1] , і [Y1] відповідно. Це робиться для кожного обрізаного шару, тому потрібно 8 розрахунків.

  7. Використовуйте інструмент AddField , щоб створити нове поле [Angle] у шарі точки перетину.

  8. Приєднайте відрізані таблиці до таблиці перетину на основі загальних ідентифікаторів об'єктів. (Приєднання здійснюється за допомогою клацання правою кнопкою миші на назві шару та вибору "Приєднується та відноситься".)

    На даний момент у таблиці перетину точок є 9 нових полів: два названі [X0] тощо, а одне - [Angle]. Псевдоніми поля [X0], [Y0], [X1] та [Y1], які належать до однієї з об'єднаних таблиць. Назвемо ці (скажімо) "X0a", "Y0a", "X1a" та "Y1a".

  9. Використовуйте калькулятор поля для обчислення кута в таблиці перетину. Ось блок коду 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 - і зазвичай дає правильні результати. (Інші "геопроцесорні" операції можуть бути не настільки надійними.)


1
Це може працювати, але ви не можете посилатись на імена полів у блоці коду, тому вам доведеться загортати код у функцію та викликати його, використовуючи назви полів як аргументи.
mvexel

@mv Дякую за зауваження. Можна також використовувати VBS замість Python - VBS буде аналізувати імена полів у блоці коду.
whuber

1
Це фактично спрацювало як шарм при використанні функціональної обгортки. Я виявив, що в ArcGIS 10 і при використанні Python вам не потрібно псевдоніму змінних, ви можете додати ім'я таблиці таблиці приєднання у посиланні на поле, наприклад !table1.x0!.
mvexel

6

Я вважаю, що вам потрібно створити сценарій python.

Зробити це можна за допомогою інструментів для геообробки та архпії.

Ось основні інструменти та ідеї, які можуть бути корисними для вас:

  1. Зробіть перетин двох ваших полілінійних (давайте назвемо їх PLINE_FC1, PLINE_FC2) функціональних класів (потрібні точкові функції, як результат - POINT_FC), використовуючи інструмент Intersect . Ви матимете ідентифікатори від PLINE_FC1, PLINE_FC2 у пунктах POINT_FC.
  2. Розділити PLINE_FC1 по POINT_FC за допомогою інструмента « Розділити лінію на точку». У результаті у вас будуть розбиті полілінії - головна перевага його в тому, що ви можете просто взяти першу / останню вершину такого рядка, порівняти її з наступною / попередньою вершиною (різниця координат) і обчислити кут. Отже, у вас буде кут лінії в точці перетину. Тут є одна проблема - вам доведеться запустити цей інструмент вручну кілька разів, щоб зрозуміти, як записується вихід. Я маю на увазі, якщо вона займає поліліній, розділіть її, запишіть на результат два полілінії, а потім перейдіть до наступної полілінії та повторіть. Або може бути ця частина (результат розщеплення) записується в різні класи пам'яті, а потім додається до виводу. Це головна проблема - усвідомити, як записується вихід, щоб після фільтрації можна було фільтрувати лише першу частину кожної полілінії. Ще одне можливе рішення полягає в тому, щоб зафіксувати всі полілініни, розбиті за результатамиSearchCursor і візьміть лише перше зіткнення (за ідентифікацією джерел поліліній PLINE_FC1).
  3. Для того, щоб отримати кут, вам потрібно буде отримати доступ до вершин поліліній результату за допомогою arcpy . Запишіть отримані кути в точки (POINT_FC).
  4. Повторіть кроки 2-3 для PLINE_FC2.
  5. Суб'єктні атрибути кута (у POINT_FC) та отримайте результат.

Можливо, буде дуже важко кодувати крок 2 (також для деяких інструментів потрібна ліцензія ArcInfo). Потім ви також можете спробувати проаналізувати вертекси кожної полілінії (згрупувати їх за ідентифікацією після перетину).

Ось як це зробити:

  1. Виберіть першу точку перетину POINT_FC. Отримати його координати ( point_x, point_y)
  2. За його ідентифікатором візьміть відповідну вихідну полілінію від PLINE_FC1.
  3. Візьміть першу ( vert0_x, vert0_y) та другу ( vert1_x, vert1_y) вертекси її.
  4. Для першої вершини обчисліть дотичну лінію між цією вершиною та точкою перетину: tan0 = (point_y - vert0_y) / (point_x - vert0_x)
  5. Обчисліть те ж саме для другої вершини: tan1 = (vert1_y - point_y) / (vert1_x - point_x)
  6. Якщо tan1вона дорівнює tan2, то ви знайшли дві вершини своєї лінії, які мають точку перетину між ними, і ви можете обчислити кут перетину для цієї лінії. Інакше вам доведеться перейти до наступної пари вершин (друга, третя) тощо.
  7. Повторіть кроки 1-6 для кожної точки перетину.
  8. Повторіть кроки 1-7 для другого полілінійного класу PLINE_FC2.
  9. Суб'єктні атрибути кута від PLINE_FC1 та PLINE_FC2 та отримайте результат.

1

Останнім часом я намагався зробити це самостійно.

Моя підказка заснована на кругових точках навколо перетину ліній, а також на точках, розташованих на відстані одного метра від перетинів. Вихід - полілінійний клас функції, який має атрибути числа кутів на перетинах та куті.

Зауважте, що лінії повинні бути сплановані для того, щоб знайти перехрестя, а просторове значення має бути встановлено з відображенням правильної довжини лінії (моя 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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.