ArcPyを使用して、ポリゴンを* n *個の等しい数のグループに分割しますか?


10

私の仕事の仕事の1つは、小包をグループに分割することです。これらのグループは、不動産所有者と話すためにエージェントによって使用されます。目標は、お互いに近い区画をグループ化することでエージェントの仕事を簡単にすることと、仕事を均等に分配するように区画を等しい数に分割することです。エージェントの数は、カップルから10以上に変動する可能性があります。

現在、私はこのタスクを手動で実行していますが、可能であればプロセスを自動化したいと考えています。私はさまざまなArcGISツールを調査しましたが、私のニーズに合うものはないようです。near_analysisポリゴンを使用および選択するスクリプト(Pythonで)を試しましたが、かなりランダムであり、準正しい結果を得るまでに時間がかかり、最初からすべてを手動で行った場合よりも修正に時間がかかります。

このタスクを自動化する信頼できる方法はありますか?

結果の例(うまくいけば、黄色で表示される除算なし):

分割された区画


ロケーション-アロケーション分析を調べましたか?help.arcgis.com/en/arcgisdesktop/10.0/help/index.html#/...

グループ化分析(空間統計)を試しましたか?
FelixIP、2015年

私が使用している実際の手順の疑似コードも投稿しました。それがgis.stackexchange.com/questions/123289/…に
FelixIP

@crmackey回答へのリンクに感謝しますが、リンクされたコード(ポリゴンの分割)をどのように調整してこの問題(ポリゴンのグループ化)に合わせることができるかわかりません。
師部2015年

回答:


4

オリジナルセット:

ここに画像の説明を入力してください

それの疑似コピー(TOCのCNTRLドラッグ)を作成し、クローンを使用して1対多に空間結合します。この場合、距離500mを使用しました。出力テーブル:

ここに画像の説明を入力してください

  1. このテーブルから、PAR_ID = PAR_ID_1-簡単なレコードを削除します。

  2. テーブルを反復処理し、その上のレコードの(PAR_ID、PAR_ID_1)=(PAR_ID_1、PAR_ID)であるレコードを削除します。それほど簡単ではありません。acrpyを使用してください。

集水重心を計算します(UniqID = PAR_ID)。それらはノードまたはネットワークです。空間結合テーブルを使用して、それらを線で接続します。これは、このフォーラムのどこかで必ず取り上げられている別のトピックです。

ここに画像の説明を入力してください

以下のスクリプトでは、ノードテーブルが次のようになっていると想定しています。 ここに画像の説明を入力してください

MUIDが小包に由来する場合、P2013は要約するフィールドです。この場合、カウントのみの場合= 1。[rcvnode]-定義されたグループ/クラスターの最初のノードのNODERECに等しいグループIDを格納するスクリプト出力。

重要なフィールドが強調表示されたテーブル構造をリンクします

ここに画像の説明を入力してください

Timesはリンク/エッジの重み、つまりノードからノードへの移動コストを格納します。この場合は1に等しいため、すべての隣人への移動コストは同じです。[fi]と[ti]は、接続されているノードの連番です。このテーブルにデータを入力するには、リンク元のノードとリンク先のノードを割り当てる方法についてこのフォーラムを検索してください。

自分のワークベンチmxd用にカスタマイズされたスクリプト。変更して、フィールドとソースの名前をハードコード化する必要があります。

import arcpy, traceback, os, sys,time
import itertools as itt
scriptsPath=os.path.dirname(os.path.realpath(__file__))
os.chdir(scriptsPath)
import COMMON
sys.path.append(r'C:\Users\felix_pertziger\AppData\Roaming\Python\Python27\site-packages')
import networkx as nx
RATIO = int(arcpy.GetParameterAsText(0))

try:
    def showPyMessage():
        arcpy.AddMessage(str(time.ctime()) + " - " + message)
mxd = arcpy.mapping.MapDocument("CURRENT")
theT=COMMON.getTable(mxd)

ノードレイヤーを検索

theNodesLayer = COMMON.getInfoFromTable(theT,1)
theNodesLayer = COMMON.isLayerExist(mxd,theNodesLayer)

リンク層を取得する

    theLinksLayer = COMMON.getInfoFromTable(theT,9)
    theLinksLayer = COMMON.isLayerExist(mxd,theLinksLayer)
    arcpy.SelectLayerByAttribute_management(theLinksLayer, "CLEAR_SELECTION")        
    linksFromI=COMMON.getInfoFromTable(theT,14)
    linksToI=COMMON.getInfoFromTable(theT,13)
    G=nx.Graph()
    arcpy.AddMessage("Adding links to graph")
    with arcpy.da.SearchCursor(theLinksLayer, (linksFromI,linksToI,"Times")) as cursor:
            for row in cursor:
                (f,t,c)=row
                G.add_edge(f,t,weight=c)
            del row, cursor
    pops=[]
    pops=arcpy.da.TableToNumPyArray(theNodesLayer,("P2013"))
    length0=nx.all_pairs_shortest_path_length(G)
    nNodes=len(pops)
    aBmNodes=[]
    aBig=xrange(nNodes)
    host=[-1]*nNodes
    while True:
            RATIO+=-1
            if RATIO==0:
                    break
            aBig = filter(lambda x: x not in aBmNodes, aBig)
            p=itt.combinations(aBig, 2)
            pMin=1000000
            small=[]
            for a in p:
                    S0,S1=0,0
                    for i in aBig:
                            p=pops[i][0]
                            p0=length0[a[0]][i]
                            p1=length0[a[1]][i]
                            if p0<p1:
                                    S0+=p
                            else:
                                    S1+=p
                    if S0!=0 and S1!=0:
                            sMin=min(S0,S1)                        
                            sMax=max(S0,S1)
                            df=abs(float(sMax)/sMin-RATIO)
                            if df<pMin:
                                    pMin=df
                                    aBest=a[:]
                                    arcpy.AddMessage('%s %i %i' %(aBest,sMax,sMin))
                            if df<0.005:
                                    break
            lSmall,lBig,S0,S1=[],[],0,0
            arcpy.AddMessage ('Ratio %i' %RATIO)
            for i in aBig:
                    p0=length0[aBest[0]][i]
                    p1=length0[aBest[1]][i]
                    if p0<p1:
                            lSmall.append(i)
                            S0+=p0
                    else:
                            lBig.append(i)
                            S1+=p1
            if S0<S1:
                    aBmNodes=lSmall[:]
                    for i in aBmNodes:
                            host[i]=aBest[0]
                    for i in lBig:
                            host[i]=aBest[1]
            else:
                    aBmNodes=lBig[:]
                    for i in aBmNodes:
                            host[i]=aBest[1]
                    for i in lSmall:
                            host[i]=aBest[0]

    with arcpy.da.UpdateCursor(theNodesLayer, "rcvnode") as cursor:
            i=0
            for row in cursor:
                    row[0]=host[i]
                    cursor.updateRow(row)
                    i+=1

            del row, cursor
except:
    message = "\n*** PYTHON ERRORS *** "; showPyMessage()
    message = "Python Traceback Info: " + traceback.format_tb(sys.exc_info()[2])[0]; showPyMessage()
    message = "Python Error Info: " +  str(sys.exc_type)+ ": " + str(sys.exc_value) + "\n"; showPyMessage()

6グループの出力例:

ここに画像の説明を入力してください

サイトパッケージNETWORKX http://networkx.github.io/documentation/development/install.htmlが必要です

スクリプトは、必要な数のクラスターをパラメーターとして受け取ります(上記の例では6)。これは、ノードとリンクテーブルを使用して、移動エッジの等しい重み/距離でグラフを作成します(Times = 1)。すべてのノードの組み合わせを2で考慮し、2つの近隣グループの[P2013]の合計を計算します。必要な比率が達成された場合、たとえば最初の反復で(6-1)/ 1、比率減少ターゲット、つまり4などが1まで続きます。開始点は非常に重要であるため、「終了」ノードが一番上にあることを確認してくださいノードテーブルの(ソート?)出力例の最初の3つのグループを参照してください。これは、次の反復ごとに「分岐切断」を回避するのに役立ちます。

mxdから機能するスクリプトのカスタマイズ:

  1. COMMONをインポートする必要はありません。theNodesLayer、theLinksLayer、linksFromI、linksToIが指定された自分の環境テーブルを読み取るのは私自身のものです。関連する行を独自のノードとリンク層の名前に置き換えます。
  2. フィールドP2013には、テナントの数や区画面積など、何でも格納できることに注意してください。もしそうなら、ポリゴンをクラスタリングして、ほぼ同じ数の人などを保持できます。

実際には、ノードとリンクのレイヤーは単なる視覚的なものです。開始ノードと終了ノードがすでに割り当てられているため、空間結合のクリーンアップされたテーブルは、リンクテーブルを簡単に置き換えることができます。ポリゴンテーブルは簡単にノードテーブルとして機能し、ReceivingNodeフィールドを追加して、そこから連番を「リンク」[FromI]と[ToI]に転送するだけです。
FelixIP 2015年

これはよさそうだ。答えてくれてありがとう。理由だけでなく、理由も詳しく説明していただけますか?あなたのコードへのコメントは膨大になるでしょう。
Emil Brundage 2015年

質問に対する私の以前のコメントのハイパーリンクをたどってください。これが「なぜ」という意味であるかどうか、私はアプローチを説明しようとしました。Qへの回答を投稿した後、スクリプトを強制終了しようとするレコードの順序をランダムに変更したため、開始ノードの重要性に関するコメントを取り下げます。何も起こらず、それでも妥当な結果が得られました。
FelixIP、2015年

空間結合テーブルをクリーンアップするには、PAR_ID = PAR_ID_1を削除するだけで十分です。これは、NETWORKXの無向グラフのエッジ/リンク[0,2]がedge [2,0]と等しいためです。更新されたスクリプトを投稿できますが、それが私の評判に影響を与えるかどうかは
わかり

@EmilBrundageを見て、なぜgis.stackexchange.com/questions/165057/…の
FelixIP

2

目標を達成するには、「グループ分析」ツールを使用する必要があります。このツールは、@ phloemが指摘した「空間統計」ツールボックスの優れたツールです。ただし、データと問題に適応するようにツールを微調整する必要があります。私はあなたが投稿したのと同じようなシナリオを作成し、目標に近い応答を得ました。

ヒント:ArcGIS 10.2を使用してツールを実行したところ、不足しているpythonパッケージ「six」について不満がありました。だから、あなたはそれが最初にインストールされていることを確認してくださいリンク

手順:

  1. 一意の値を保持するフィールドをポリゴンクラスに追加します
  2. 名前が「SameGroup」などのShortタイプの別のフィールドを追加します。
  3. すべての行でこのフィールドに1を割り当てるフィールド計算機。1行を2に変更するだけです。 追加されたフィールド

  4. 「グループ分析」ツールのパラメーターを次のように設定します。 グループ分析

「Number of Neighbours」パラメーターを必要に応じて変更してみてください。

結果のスナップショット:

入力ポリゴンの例

グループ分析の結果


2
以前にグループ分析を調べました。それは空間を扱いますが、私の知る限りではありません。ドキュメンテーションを読んで、あなたの例を見て、そして私自身のテストを実行することからの私のすべての経験は、同じ数のポリゴンによってグループ化することを許可しません。
Emil Brundage、2015年

平等にする必要があるのはなぜですか(エージェントにはコース外)。しかし、その制約を追加すると、なぜ空間的関係に基づいてデータをクラスター化(グループ化)するのでしょうか。
Farid Cheraghi、2015年

1
ボスがそう言っているからです。また、移動時間を最小限に抑えます。
Emil Brundage、2015年

1

基本的には、同じサイズのクラスタリング手法が必要なので、このキーワードでWebを検索できます。私にとって、答えの 1つにPythonの実装があるstats.SEには良い答えがあります。arcpyに慣れている場合は、データで使用できるはずです。

最初にポリゴンの重心のXとYを計算する必要があります。次に、これらの座標をスクリプトに入力し、.daカーソルを使用してそれらの属性テーブルを更新できます。


あなたの提供するリンクは正しい軌道にあるようですが、それは基本的に私が理解できない言語で書かれています。スクリプトの場合、入力が何なのかわからないし、何が起こっているのかを正確に理解するためにコーディングを解読することもできません。説明はほとんどありません。
Emil Brundage

0

こんにちは、私は以前にこれと同様の問題を抱えていたので、私はそれをいくつか与えましたが、最初から別のことを始めたことはありませんでしたが、それは私が考えていた

入力形状

入力形状

入力形状にフィッシュネットを作成できると思っていました

魚網 入力形状の交差を持つフィッシュネットは、

エリアへの入力

次に、新しく処理されたポリゴン内のこれらの区画の面積を計算できます

スクリプトの開始時に、面積入力ポリゴン/ n番目の等しいサイズが必要

次に、区画に関連付けられている区画を認識させるために区画を関連付ける方法が必要になります。

次に、小包を合計する行カーソルを通過できます

ルールは

*最後の1つの夏との境界線を共有します*合計されていません*等しい面積として計算された値を超えると、ステップバックしてこれがグループになります*プロセスがもう一度やり直します*最後のグループは残った小包の合計

小包間の関係を確立するのは難しいかもしれませんが、これが完了したら、それを自動化することは可能だと思います


これが私の問題とどう関係しているのか理解できません。フィッシュネットでポリゴンをカットすることは、ポリゴンを空間的に同じ数でグループ化することと何が関係していますか?カウントではなく、エリアに集中しているように見えます。パーセルポリゴンの面積(サイズ)は要因ではありません。小包の大きさに関わらず、話し合うのは1人の不動産所有者だけです。赤が農村地域で広がっている私の例を参照してください。オレンジは都市なので、全体の範囲がはるかに狭いのです。
Emil Brundage

こんにちは、あなた、ごめんなさい、あなたの質問を全部読んでしまいました。私はradouxjuの投稿が行くべき道かもしれないと思うが、リンクは私の頭の上に少し行きます。ポリゴンをポイントに変換することは論理的であり、それらをグループ化します。空間要素定義することができます道路と次のポイントへのポイントからの距離として、道路システムを導入する方法があるかもしれません
ジャック・ウォーカー


0

これは、ポイントイベントに対する私の解決策です。常に機能するという保証はありません...

  1. ポイントイベントレイヤー(layer1を呼び出す)で、x(double)、y(double)、uniqueid(long integer)の列を追加します。
  2. レイヤー1の属性テーブルを開きます。xのx座標点、yのy座標点、一意のIDのFIDを計算します
  3. 空間統計ツールの実行>クラスタのマッピング>グループ化分析
    • layer1を入力フィーチャとして設定する
    • uniqueidを一意のフィールドIDとして設定する
    • グループの数を定義します(10とします)
    • 分析フィールドのxとyを選択します
    • 空間制約には「NO_SPATIAL_CONSTRAINT」を選択してください
    • OKをクリックします
  4. 空間統計ツールの実行>地理的分布の測定> Mean Center
    • #3からの出力を入力フィーチャクラスとして選択します
    • ケースフィールドとしてSS_Groupを選択
    • OKをクリックします
  5. Open Network Analyst> Location Allocation Tool
    • #4の出力をファシリティとしてロード
    • layer1を需要地点としてロード
    • 属性を開いて設定
      • 容量範囲を最大化する問題タイプ
      • 10として選択する施設(上記の#3から)
      • レイヤー1のフィーチャの総数を選択する施設で割ったデフォルトの容量(切り上げ)(つまり、145のフィーチャと10の施設/エリアの場合、15に設定)
      • OKをクリックします
        • 解決する
        • 需要地点は、ほぼ10の地理的クラスターに均等に分散する必要があります

私はあなたの方法のステップ5で立ち往生しています。Network Analystエクステンションをチェックアウトし、Network Analystツールバーを追加しました。しかし、ほとんどがグレー表示され、「ロケーション割り当てツール」が表示されません。10.1を使用しています。
Emil Brundage

0

最初に道路を使用してネットワークデータセットを作成する必要があります。私はこの提案された方法を試しましたが、これまでのところ、入力フィールドにX、Y座標とk平均を使用して、グループ化(ステップ3)で同じことを単独で実行した方が運が良かったです(完璧ではありませんが、私より速くて近いです)必要)。私は他の人のコメントやフィードバックを受け入れています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.