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 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?
Câu trả lời:
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:
Đả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.
Geoprocessing | Intersect có được các điểm (đảm bảo chỉ định bạn muốn các điểm cho đầu ra).
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.
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).
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.)
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.
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.
Sử dụng công cụ AddField để tạo trường [Góc] mới trong lớp điểm giao nhau.
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".
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.)
!table1.x0!
.
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:
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 đó:
point_x
, point_y
)vert0_x
, vert0_y
) và thứ hai ( vert1_x
, vert1_y
) của nó.tan0 = (point_y - vert0_y) / (point_x - vert0_x)
tan1 = (vert1_y - point_y) / (vert1_x - point_x)
tan1
bằ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.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)