交差するラインフィーチャクラスが2つあります。ArcGIS 10とPythonを使用して、各交点の角度を見つけたいです。
誰でも助けることができますか?
交差するラインフィーチャクラスが2つあります。ArcGIS 10とPythonを使用して、各交点の角度を見つけたいです。
誰でも助けることができますか?
回答:
比較的単純なワークフローがあります。 2つのフィーチャが複数のポイントで交差する可能性がある潜在的な問題を克服します。スクリプトは必要ありません(ただし、すぐにスクリプトに変換できます)。主にArcGISメニューから実行できます。
考え方は、交差するポリラインの個別のペアごとに1つのポイントである、交差ポイントのレイヤーを活用することです。これらの交点で交差する各ポリラインの小片を取得する必要があります。これらのピースの向きを使用して、交差角度を計算します。
手順は次のとおりです。
各ポリラインフィーチャの属性テーブル内に一意の識別子があることを確認してください。これは、ポリラインのいくつかの幾何学的属性を交差点テーブルに結合するために後で使用されます。
Geoprocessing | Intersectはポイントを取得します(出力に必要なポイントを必ず指定してください)。
ジオプロセシング|バッファを使用すると、ポイントをわずかな量だけバッファリングできます。それが作る本当にバッファ内の各ラインの部分が曲がらないように、小さな。
ジオプロセシング|クリップ(2回適用)は、元のポリラインレイヤーをバッファーのみに制限します。これにより、出力用の新しいデータセットが生成されるため、後続の操作では元のデータが変更されません(これは良いことです)。
これが起こる概略図です。水色と水色で表示されている2つのポリラインレイヤーが、暗い交点を生成しています。これらのポイントの周りに小さなバッファーが黄色で表示されます。濃い青と赤のセグメントは、元のフィーチャをこれらのバッファーにクリップした結果を示しています。アルゴリズムの残りの部分は、暗いセグメントで機能します。(ここでは表示されませんが、小さな赤いポリラインが共通点で2つの青いラインと交差し、2つの青いポリラインの周りのバッファーのように見えます。実際には、赤と青の交差する2つのオーバーラップポイントの周りの2つのバッファーです。 、この図には全部で5つのバッファーが表示されています。)
AddFieldツールを使用して、これらのクリップされたレイヤーのそれぞれに4つの新しいフィールドを作成します:[X0]、[Y0]、[X1]、および[Y1]。それらはポイント座標を保持するので、それらを倍にし、多くの精度を与えます。
ジオメトリの計算(新しい各フィールドヘッダーを右クリックして呼び出されます)を使用すると、クリップされた各ポリラインの開始点と終了点のx座標とy座標を計算できます。これらを[X0]、[Y0]、[X1] 、および[Y1]、それぞれ。これはクリップされたレイヤーごとに行われるため、8つの計算が必要です。
AddFieldツールを使用して、交点レイヤーに新しい[角度]フィールドを作成します。
共通のオブジェクト識別子に基づいて、クリップされたテーブルを交差点テーブルに結合します。(結合は、レイヤー名を右クリックして[結合とリレート]を選択することにより実行されます。)
この時点で、ポイント交差テーブルには9つの新しいフィールドがあります。2つは[X0]など、もう1つは[Angle]という名前です。 結合されたテーブルの1つに属する[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)は2番目のポリラインの方向ベクトルです。それらの長さrおよびra(ピタゴラスの定理で計算)は、それらを単位ベクトルに正規化するために使用されます。(長さがゼロの場合、クリッピングは正の長さのフィーチャを生成するため、問題はないはずです。)ウェッジ積dx dya-dyのサイズdxa(rとraによる除算後)は、角度のサインです。(通常の内積ではなくウェッジ積を使用すると、ゼロに近い角度の数値精度が向上します。)最後に、角度はラジアンから度に変換されます。結果は0〜90になります。最後まで三角法が回避されることに注意してください。このアプローチでは、信頼性が高く、計算が簡単な結果が得られる傾向があります。
一部のポイントは、交差レイヤーに複数回表示される場合があります。もしそうなら、彼らはそれらに関連付けられた複数の角度を取得します。
このソリューションでのバッファリングとクリッピングは比較的高価です(ステップ3および4):数百万の交差点が関係する場合、この方法は望ましくありません。(a)交差点の近傍内の各ポリラインに沿って2つの連続する点を見つけるプロセスを単純化し、(b)バッファリングが非常に基本的であり、GISで簡単に実行できるため、追加のライセンスは不要であるため、推奨しました基本的なArcMapレベルの上にあり、通常は正しい結果を生成します。(他の「ジオプロセシング」操作はそれほど信頼できないかもしれません。)
!table1.x0!
。
Pythonスクリプトを作成する必要があると思います。
ジオプロセシングツールとarcpyを使用して実行できます。
あなたに役立つ主なツールとアイデアは次のとおりです。
ステップ2のコーディングは非常に難しいかもしれません(一部のツールではArcInfoライセンスが必要です)。次に、すべてのポリラインの頂点を分析することもできます(交差後にIDでグループ化)。
これを行う方法は次のとおりです。
point_x
、point_y
)vert0_x
、vert0_y
)および2番目の(vert1_x
、vert1_y
)頂点を取ります。tan0 = (point_y - vert0_y) / (point_x - vert0_x)
tan1 = (vert1_y - point_y) / (vert1_x - point_x)
tan1
がに等しい場合tan2
、交点が間にあるラインの2つの頂点が見つかり、このラインの交角を計算できます。それ以外の場合は、次の頂点のペア(2番目、3番目)などに進む必要があります。最近、私は自分でそれをやろうとしていました。
私の手掛かり機能は、交差点から1メートルの距離にある点だけでなく、線の交点の周りの円形点に基づいています。出力は、交差点と角度の角度番号の属性を持つポリラインフィーチャクラスです。
交差を見つけるためにラインを平面化する必要があり、空間参照は正しいライン長表示で設定する必要があることに注意してください(私の場合はWGS_1984_Web_Mercator_Auxiliary_Sphereです)。
ArcMapコンソールで実行しますが、ツールボックスで簡単にスクリプトに変換できます。このスクリプトは目次でラインレイヤーのみを使用し、それ以上は使用しません。
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)