Tìm góc giữa các tính năng giao nhau trong hai lớp tính năng bằng ArcGIS Desktop và Python? [đóng cửa]


19

Tôi có hai dòng featureclass giao nhau. Tôi muốn tìm góc ở mỗi điểm giao nhau bằng ArcGIS 10 và Python.

Có ai giúp được không?


Tôi đã sao chép phương thức whuber (cảm ơn bạn) trong một tập lệnh python bằng cách sử dụng arcpy nhưng tôi gặp vấn đề với việc tính toán góc. Khi hoàn thành trong Esri ArcMap (máy tính trường), nó sẽ tính toán chính xác. Khi được tính trong tập lệnh python (một lần nữa sử dụng máy tính trường), nó sẽ tính toán không chính xác (dưới dạng thập phân). Nó không chỉ đơn giản là một sự chuyển đổi từ radian sang độ vấn đề. Hàm arcpy để tính toán trường dưới dạng một góc bên dưới. Các lớp tính năng được chiếu (Lưới quốc gia Anh). Có một bước bổ sung nào tôi cần phải thực hiện để tính toán các góc của con trăn khỏi một tài liệu bản đồ
Andy

Câu trả lời:


13

Có một quy trình làm việc tương đối đơn giản. Nó khắc phục các vấn đề tiềm ẩn mà hai tính năng có thể giao nhau ở nhiều hơn một điểm. Nó không yêu cầu kịch bản (nhưng có thể dễ dàng biến thành kịch bản). Nó có thể được thực hiện chủ yếu từ menu ArcGIS.

Ý tưởng là khai thác một lớp các điểm giao nhau, một điểm cho mỗi cặp polylines giao nhau. Bạn cần phải có được một mảnh nhỏ của mỗi đa tuyến giao nhau tại các điểm giao nhau này. Sử dụng sự định hướng của các mảnh này để tính các góc giao nhau của chúng.

Dưới đây là các bước:

  1. Đảm bảo rằng mỗi tính năng đa tuyến có một mã định danh duy nhất trong bảng thuộc tính của nó. Điều này sẽ được sử dụng sau này để nối một số thuộc tính hình học của polylines vào bảng điểm giao nhau.

  2. Geoprocessing | Intersect có được các điểm (đảm bảo chỉ định bạn muốn các điểm cho đầu ra).

  3. Xử lý địa lý | Bộ đệm cho phép bạn đệm các điểm bằng một lượng nhỏ. Làm cho nó thực sự nhỏ để phần của mỗi dòng trong bộ đệm không bị uốn cong.

  4. Geoprocessing | Clip (áp dụng hai lần) giới hạn các lớp polyline ban đầu chỉ ở bộ đệm. Bởi vì điều này tạo ra các bộ dữ liệu mới cho đầu ra của nó, các hoạt động tiếp theo sẽ không sửa đổi dữ liệu gốc (đó là một điều tốt).

    Nhân vật

    Dưới đây là sơ đồ về những gì xảy ra: hai lớp polyline, được hiển thị bằng màu xanh nhạt và đỏ nhạt, đã tạo ra các điểm giao nhau tối. Xung quanh những điểm đệm nhỏ được thể hiện bằng màu vàng. Các đoạn màu xanh và đỏ đậm hơn cho thấy kết quả của việc cắt các tính năng gốc cho các bộ đệm này. Phần còn lại của thuật toán hoạt động với các phân đoạn tối. (Bạn không thể nhìn thấy nó ở đây, nhưng một đa tuyến nhỏ màu đỏ giao nhau giữa hai đường màu xanh lam tại một điểm chung, tạo ra một vùng đệm xung quanh hai polylines màu xanh. Nó thực sự là hai vùng đệm xung quanh hai điểm giao nhau màu đỏ-xanh. , sơ đồ này hiển thị tất cả năm bộ đệm.)

  5. Sử dụng công cụ AddField để tạo bốn trường mới trong mỗi lớp được cắt bớt này: [X0], [Y0], [X1] và [Y1]. Chúng sẽ giữ tọa độ điểm, do đó làm cho chúng tăng gấp đôi và cung cấp cho chúng rất nhiều độ chính xác.

  6. Tính toán Hình học (được gọi bằng cách nhấp chuột phải vào từng tiêu đề trường mới) cho phép bạn tính tọa độ x và y của điểm bắt đầu và điểm kết thúc của mỗi đa tuyến được cắt: đặt chúng vào [X0], [Y0], [X1] và [Y1], tương ứng. Điều này được thực hiện cho mỗi lớp được cắt bớt, vì vậy cần 8 phép tính.

  7. Sử dụng công cụ AddField để tạo trường [Góc] mới trong lớp điểm giao nhau.

  8. Tham gia các bảng được cắt vào bảng điểm giao nhau dựa trên các định danh đối tượng phổ biến. (Tham gia được thực hiện bằng cách nhấp chuột phải vào tên lớp và chọn "Tham gia và liên quan".)

    Tại thời điểm này, bảng giao điểm có 9 trường mới: hai trường được đặt tên [X0], v.v. và một được đặt tên là [Góc]. Bí danh các trường [X0], [Y0], [X1] và [Y1] thuộc về một trong các bảng đã nối. Chúng ta hãy gọi những cái này (giả sử) "X0a", "Y0a", "X1a" và "Y1a".

  9. Sử dụng Máy tính trường để tính góc trong bảng giao nhau. Đây là một khối mã Python để tính toán:

    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

    Tất nhiên, biểu thức tính toán trường chỉ đơn thuần là

    c

Mặc dù chiều dài của khối mã này, toán học rất đơn giản: (dx, dy) là một vectơ chỉ hướng cho đa tuyến đầu tiên và (dxa, dya) là một vectơ chỉ phương cho thứ hai. Độ dài của chúng, r và ra (được tính toán qua Định lý Pythagore), được sử dụng để chuẩn hóa chúng thành các vectơ đơn vị. (Không nên có vấn đề gì với độ dài bằng 0, vì việc cắt sẽ tạo ra các tính năng có độ dài dương.) Kích thước của sản phẩm nêm của họ dx dya - dydxa (sau khi chia cho r và ra) là sin của góc. (Sử dụng sản phẩm nêm thay vì sản phẩm bên trong thông thường sẽ cung cấp độ chính xác số tốt hơn cho các góc gần bằng không.) Cuối cùng, góc được chuyển đổi từ radian sang độ. Kết quả sẽ nằm trong khoảng từ 0 đến 90. Lưu ý việc tránh lượng giác cho đến khi kết thúc: phương pháp này có xu hướng tạo ra kết quả đáng tin cậy và dễ dàng tính toán.

Một số điểm có thể xuất hiện nhiều lần trong lớp giao nhau. Nếu vậy, họ sẽ nhận được nhiều góc độ liên quan đến chúng.

Bộ đệm và cắt trong giải pháp này tương đối tốn kém (bước 3 và 4): bạn không muốn làm theo cách này khi hàng triệu điểm giao nhau được tham gia. Tôi đã khuyến nghị vì (a) nó đơn giản hóa quá trình tìm hai điểm liên tiếp dọc theo mỗi đa tuyến trong vùng lân cận điểm giao nhau của nó và (b) đệm rất cơ bản nên dễ dàng thực hiện trong bất kỳ GIS nào - không cần cấp phép bổ sung trên mức ArcMap cơ bản - và thường tạo ra kết quả chính xác. (Các hoạt động "xử lý địa lý" khác có thể không đáng tin cậy lắm.)


1
Điều này có thể hoạt động, nhưng bạn không thể tham chiếu tên trường trong bộ mã, vì vậy bạn phải bọc mã trong một hàm và gọi nó bằng cách sử dụng tên trường làm đối số.
mvexel

@mv Cảm ơn bạn đã quan sát. Người ta cũng có thể sử dụng VBS thay vì Python - VBS sẽ phân tích tên trường trong khối mã.
whuber

1
Nó thực sự hoạt động như một bùa mê khi sử dụng hàm bao. Tôi thấy rằng trong ArcGIS 10 và khi sử dụng Python, bạn không cần phải đặt bí danh cho các biến, bạn có thể thêm tên bảng tham gia trong tham chiếu trường, như !table1.x0!.
mvexel

6

Tôi tin rằng bạn cần phải tạo kịch bản python.

Bạn có thể làm điều đó bằng cách sử dụng các công cụ xử lý địa lý và arcpy.

Dưới đây là các công cụ và ý tưởng chính có thể hữu ích cho bạn:

  1. Tạo giao điểm của hai polyline của bạn (hãy gọi chúng là PLINE_FC1, PLINE_FC2) featurec Cầu (kết quả là bạn cần các tính năng điểm - POINT_FC) bằng công cụ Intersect . Bạn sẽ có ID từ PLINE_FC1, PLINE_FC2 trong các điểm POINT_FC.
  2. Tách PLINE_FC1 cho POINT_FC bằng công cụ Split Line At Point. Theo kết quả, bạn sẽ có các polylines phân tách - lợi thế chính của nó là bạn chỉ cần lấy đỉnh đầu tiên / cuối cùng của dòng đó so sánh với đỉnh tiếp theo / trước (độ lệch tọa độ) và tính toán góc. Vì vậy, bạn sẽ có góc của đường thẳng của bạn trong điểm giao nhau. Có một vấn đề ở đây - bạn phải chạy công cụ này một cách thủ công nhiều lần để nhận ra đầu ra được viết như thế nào. Ý tôi là nếu nó lấy polyline, tách nó, viết hai polylines kết quả cho đầu ra và sau đó tiến hành polyline tiếp theo và lặp lại. Hoặc có thể là phần này (kết quả của việc chia tách) được ghi vào các lớp tính năng bộ nhớ khác nhau, sau đó được thêm vào đầu ra. Đây là vấn đề chính - để nhận ra cách viết đầu ra để có thể chỉ lọc phần đầu tiên của mỗi đa tuyến sau khi tách. Một giải pháp khả thi khác là lặp lại tất cả các polylines được chia tách vớiSearchCoder và chỉ gặp lần đầu tiên (theo ID của polylines nguồn PLINE_FC1).
  3. Để có được góc, bạn sẽ cần truy cập vào các đỉnh của polyline kết quả bằng cách sử dụng arcpy . Viết các góc kết quả cho các điểm (POINT_FC).
  4. Lặp lại các bước 2-3 cho PLINE_FC2.
  5. Các thuộc tính góc trừ (trong POINT_FC) và nhận kết quả.

Có thể sẽ rất khó để mã hóa bước 2 (cũng có một số công cụ yêu cầu giấy phép ArcInfo). Sau đó, bạn cũng có thể cố gắng phân tích các đỉnh của mọi đa tuyến (nhóm chúng theo ID sau khi giao nhau).

Đây là cách để làm điều đó:

  1. Lấy điểm giao nhau đầu tiên POINT_FC. Lấy tọa độ của nó ( point_x, point_y)
  2. Bằng ID của nó, lấy polyline nguồn tương ứng từ PLINE_FC1.
  3. Lấy đầu tiên ( vert0_x, vert0_y) và thứ hai ( vert1_x, vert1_y) của nó.
  4. Đối với đỉnh đầu tiên tính tiếp tuyến của đường thẳng giữa đỉnh này và điểm giao nhau: tan0 = (point_y - vert0_y) / (point_x - vert0_x)
  5. Tính toán điều tương tự cho đỉnh thứ hai: tan1 = (vert1_y - point_y) / (vert1_x - point_x)
  6. Nếu tan1bằng tan2, thì bạn đã tìm thấy hai đỉnh của đường thẳng của bạn có điểm giao nhau ở giữa và bạn có thể tính góc của giao điểm cho đường này. Nếu không, bạn phải tiến hành các cặp tiếp theo (thứ hai, thứ ba) và như vậy.
  7. Lặp lại các bước 1-6 cho mọi điểm giao nhau.
  8. Lặp lại các bước 1-7 cho featureclass polyline thứ hai PLINE_FC2.
  9. Trừ các thuộc tính góc từ PLINE_FC1 và PLINE_FC2 và nhận kết quả.

1

Gần đây tôi đã cố gắng tự làm điều đó.

Tính năng đầu mối của tôi dựa trên các điểm tròn xung quanh giao điểm của các đường cũng như các điểm nằm trên khoảng cách một mét từ giao lộ. Đầu ra là lớp tính năng đa tuyến có các thuộc tính của số góc trên các giao điểm và góc.

Lưu ý rằng các dòng nên được đặt phẳng để tìm giao điểm và tham chiếu không gian phải được đặt với hiển thị độ dài dòng chính xác (của tôi là WGS_1984_Web_Mercator_Auxadder_Sphere).

Chạy trong bảng điều khiển ArcMap nhưng có thể dễ dàng chuyển thành tập lệnh trong hộp công cụ. Kịch bản này chỉ sử dụng lớp dòng trong TOC, không có gì hơn.

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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.