Answers:
有一个相对简单的工作流程。 它克服了两个要素可能在多个点相交的潜在问题。它不需要脚本(但可以很容易地变成脚本)。它主要可以从ArcGIS菜单完成。
想法是利用一层相交点,每条相交的折线对分别有一个点。您需要在这些交点处获得一小段每个相交的折线。使用这些零件的方向来计算它们的相交角。
步骤如下:
确保每个折线要素在其属性表中都有唯一的标识符。稍后将使用它来将折线的某些几何属性连接到相交点表。
Geoprocessing | Intersect获取点(确保指定要输出的点)。
地理处理|缓冲区可让您少量缓冲点。使其真正很小,以使缓冲区中每行的部分不会弯曲。
地理处理(应用两次)将原始折线图层限制为仅缓冲区。由于这会为其输出生成新的数据集,因此后续操作将不会修改原始数据(这是一件好事)。
这是发生的情况的示意图:以浅蓝色和浅红色显示的两个折线图层产生了暗交点。在这些点周围,黄色显示了微小的缓冲区。较深的蓝色和红色部分显示了将原始特征剪切到这些缓冲区的结果。该算法的其余部分适用于暗段。(您在这里看不到它,但是一条微小的红色折线在一条公共点与两条蓝线相交,从而在两条蓝色折线之间产生了缓冲区。实际上,这是在红蓝相交的两个重叠点周围的两个缓冲区。因此,此图总共显示了五个缓冲区。)
使用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步):当涉及数百万个交点时,您不想这样做。我之所以推荐它,是因为(a)简化了在相交点附近沿每条折线查找两个连续点的过程,并且(b)缓冲是如此基础,因此在任何GIS中都很容易实现-无需额外的许可高于基本ArcMap水平-通常会产生正确的结果。(其他“地理处理”操作可能不太可靠。)
!table1.x0!
。
我相信您需要创建python脚本。
您可以使用地理处理工具和arcpy来完成。
以下是对您有用的主要工具和想法:
可能很难对第2步进行编码(某些工具也需要ArcInfo许可)。然后,您也可以尝试分析每条折线的顶点(交点后按ID将它们分组)。
这是这样做的方法:
point_x
,point_y
)vert0_x
,vert0_y
)和第二(vert1_x
,vert1_y
它)verteces。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)