ArcGIS DesktopとPythonを使用して、2つのフィーチャクラスで交差するフィーチャ間の角度を検出しますか?[閉まっている]


19

交差するラインフィーチャクラスが2つあります。ArcGIS 10とPythonを使用して、各交点の角度を見つけたいです。

誰でも助けることができますか?


arcpyを使用してPythonスクリプトでwhuberのメソッドを複製しました(ありがとう)が、角度の計算に問題があります。Esri ArcMap(フィールド計算機)内で完了すると、正しく計算されます。pythonスクリプト内で計算する場合(フィールド計算機を使用)、計算が正しくありません(10進数として)。ラジアンから度への単純な変換問題ではありません。角度としてフィールドを計算するarcpy関数は次のとおりです。フィーチャクラスが投影されます(British National Grid)。私は、マップdocumから離れpythonで角度を計算するために必要な追加のステップがあります
アンディ・

回答:


13

比較的単純なワークフローがあります。 2つのフィーチャが複数のポイントで交差する可能性がある潜在的な問題を克服します。スクリプトは必要ありません(ただし、すぐにスクリプトに変換できます)。主にArcGISメニューから実行できます。

考え方は、交差するポリラインの個別のペアごとに1つのポイントである、交差ポイントのレイヤーを活用することです。これらの交点で交差する各ポリラインの小片を取得する必要があります。これらのピースの向きを使用して、交差角度を計算します。

手順は次のとおりです。

  1. 各ポリラインフィーチャの属性テーブル内に一意の識別子があることを確認してください。これは、ポリラインのいくつかの幾何学的属性を交差点テーブルに結合するために後で使用されます。

  2. Geoprocessing | Intersectはポイントを取得します(出力に必要なポイントを必ず指定してください)。

  3. ジオプロセシング|バッファを使用すると、ポイントをわずかな量だけバッファリングできます。それが作る本当にバッファ内の各ラインの部分が曲がらないように、小さな。

  4. ジオプロセシング|クリップ(2回適用)は、元のポリラインレイヤーをバッファーのみに制限します。これにより、出力用の新しいデータセットが生成されるため、後続の操作では元のデータが変更されません(これは良いことです)。

    図

    これが起こる概略図です。水色と水色で表示されている2つのポリラインレイヤーが、暗い交点を生成しています。これらのポイントの周りに小さなバッファーが黄色で表示されます。濃い青と赤のセグメントは、元のフィーチャをこれらのバッファーにクリップした結果を示しています。アルゴリズムの残りの部分は、暗いセグメントで機能します。(ここでは表示されませんが、小さな赤いポリラインが共通点で2つの青いラインと交差し、2つの青いポリラインの周りのバッファーのように見えます。実際には、赤と青の交差する2つのオーバーラップポイントの周りの2つのバッファーです。 、この図には全部で5つのバッファーが表示されています。)

  5. AddFieldツールを使用して、これらのクリップされたレイヤーのそれぞれに4つの新しいフィールドを作成します:[X0]、[Y0]、[X1]、および[Y1]。それらはポイント座標を保持するので、それらを倍にし、多くの精度を与えます。

  6. ジオメトリの計算(新しい各フィールドヘッダーを右クリックして呼び出されます)を使用すると、クリップされた各ポリラインの開始点と終了点のx座標とy座標を計算できます。これらを[X0]、[Y0]、[X1] 、および[Y1]、それぞれ。これはクリップされたレイヤーごとに行われるため、8つの計算が必要です。

  7. AddFieldツールを使用して、交点レイヤーに新しい[角度]フィールドを作成します。

  8. 共通のオブジェクト識別子に基づいて、クリップされたテーブルを交差点テーブルに結合します。(結合は、レイヤー名を右クリックして[結合とリレート]を選択することにより実行されます。)

    この時点で、ポイント交差テーブルには9つの新しいフィールドがあります。2つは[X0]など、もう1つは[Angle]という名前です。 結合されたテーブルの1つに属する[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)は2番目のポリラインの方向ベクトルです。それらの長さrおよびra(ピタゴラスの定理で計算)は、それらを単位ベクトルに正規化するために使用されます。(長さがゼロの場合、クリッピングは正の長さのフィーチャを生成するため、問題はないはずです。)ウェッジ積dx dya-dyのサイズdxa(rとraによる除算後)は、角度のサインです。(通常の内積ではなくウェッジ積を使用すると、ゼロに近い角度の数値精度が向上します。)最後に、角度はラジアンから度に変換されます。結果は0〜90になります。最後まで三角法が回避されることに注意してください。このアプローチでは、信頼性が高く、計算が簡単な結果が得られる傾向があります。

一部のポイントは、交差レイヤーに複数回表示される場合があります。もしそうなら、彼らはそれらに関連付けられた複数の角度を取得します。

このソリューションでのバッファリングとクリッピングは比較的高価です(ステップ3および4):数百万の交差点が関係する場合、この方法は望ましくありません。(a)交差点の近傍内の各ポリラインに沿って2つの連続する点を見つけるプロセスを単純化し、(b)バッファリングが非常に基本的であり、GISで簡単に実行できるため、追加のライセンスは不要であるため、推奨しました基本的なArcMapレベルの上にあり、通常は正しい結果を生成します。(他の「ジオプロセシング」操作はそれほど信頼できないかもしれません。)


1
これは機能しますが、コードブロック内のフィールド名を参照できないため、関数でコードをラップし、フィールド名を引数として使用して呼び出す必要があります。
mvexel

@mvその観察をありがとう。Pythonの代わりにVBSを使用することもできます。VBS はコードブロック内のフィールド名解析します。
whuber

1
関数ラッパーを使用すると、実際には魅力のように機能しました。ArcGIS 10では、Pythonを使用する場合、変数のエイリアスを作成する必要はなく、フィールド参照に結合テーブル名を追加することができます!table1.x0!
mvexel

6

Pythonスクリプトを作成する必要があると思います。

ジオプロセシングツールとarcpyを使用して実行できます。

あなたに役立つ主なツールとアイデアは次のとおりです。

  1. Intersectツールを使用して、2つのポリライン(それらをPLINE_FC1、PLINE_FC2と呼びます)フィーチャクラス(結果としてポイントフィーチャが必要-POINT_FC)の交差を作成します。ポイントPOINT_FCのPLINE_FC1、PLINE_FC2のIDがあります。
  2. ポイントでラインを分割ツールを使用して、PLINE_FC1をPOINT_FCで分割。結果として、ポリラインが分割されます。その主な利点は、そのようなラインの最初/最後の頂点を取得して、次/前の頂点と比較し(座標差)、角度を計算できることです。そのため、交差点で線の角度が決まります。ここには1つの問題があります。出力がどのように書き込まれるかを理解するには、このツールを手動で数回実行する必要があります。つまり、ポリラインを取り、それを分割し、2つの結果のポリラインを出力に書き込み、次のポリラインに進んで繰り返します。または、この部分(分割の結果)が異なるメモリフィーチャクラスに書き込まれ、出力に追加される場合があります。これが主な問題です-分割後に各ポリラインの最初の部分のみをフィルタリングできるように出力がどのように書き込まれるかを理解する。別の可能な解決策は、すべての結果分割ポリラインをループ処理することですSearchCursorおよびtake最初に検出されたもののみ(ソースポリラインPLINE_FC1のIDによる)。
  3. 角度を取得するためには、使用してアクセス結果ポリラインのvertecesにする必要がありますarcpyを。結果の角度をポイントに書き込みます(POINT_FC)。
  4. PLINE_FC2に対して手順2〜3を繰り返します。
  5. 角度属性(POINT_FC内)を減算し、結果を取得します。

ステップ2のコーディングは非常に難しいかもしれません(一部のツールではArcInfoライセンスが必要です)。次に、すべてのポリラインの頂点を分析することもできます(交差後にIDでグループ化)。

これを行う方法は次のとおりです。

  1. 最初の交差点POINT_FCを取ります。座標を取得(point_xpoint_y
  2. そのIDにより、PLINE_FC1からそれぞれのソースポリラインを取得します。
  3. 最初の(vert0_xvert0_y)および2番目の(vert1_xvert1_y)頂点を取ります。
  4. 最初の頂点について、この頂点と交差点の間の線の接線を計算します。 tan0 = (point_y - vert0_y) / (point_x - vert0_x)
  5. 2番目の頂点についても同じことを計算します。 tan1 = (vert1_y - point_y) / (vert1_x - point_x)
  6. tan1がに等しい場合tan2、交点が間にあるラインの2つの頂点が見つかり、このラインの交角を計算できます。それ以外の場合は、次の頂点のペア(2番目、3番目)などに進む必要があります。
  7. すべての交点に対して手順1〜6を繰り返します。
  8. 2番目のポリラインフィーチャクラスPLINE_FC2に対して手順1〜7を繰り返します。
  9. PLINE_FC1およびPLINE_FC2から角度属性を減算し、結果を取得します。

1

最近、私は自分でそれをやろうとしていました。

私の手掛かり機能は、交差点から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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.