複数の方法で重複する複数のポリゴンがある場合、これらの機能から、他のポリゴンと重複しないすべてのポリゴンを繰り返しエクスポートします。
製品は、重なり合わないでオリジナルを構成するいくつかの機能になります。
次に、積をゾーン統計への入力として使用できます。これは、各ポリゴンでゾーン統計を繰り返すよりもはるかに高速です。
私はこれをArcPyでコーディングしようとしましたが、成功しませんでした。
これを行うコードはすでに存在しますか?
複数の方法で重複する複数のポリゴンがある場合、これらの機能から、他のポリゴンと重複しないすべてのポリゴンを繰り返しエクスポートします。
製品は、重なり合わないでオリジナルを構成するいくつかの機能になります。
次に、積をゾーン統計への入力として使用できます。これは、各ポリゴンでゾーン統計を繰り返すよりもはるかに高速です。
私はこれをArcPyでコーディングしようとしましたが、成功しませんでした。
これを行うコードはすでに存在しますか?
回答:
これはグラフの色の問題です。
グラフの色付けは、エッジを共有する2つの頂点も同じ色にならないように、グラフの頂点に色を割り当てることを思い出してください。具体的には、グラフの(抽象的な)頂点はポリゴンです。2つの頂点は、(ポリゴンとして)交差するときはいつでも(無向)エッジに接続されます。この問題(ポリゴンの(たとえばk)の互いに素なコレクションのシーケンスです)に対して何らかの解決策を取り、シーケンス内の各コレクションに一意の色を割り当てると、グラフのk-カラーリングが得られます。 。小さなkを見つけることが望ましい。
この問題はかなり難しく、任意のグラフでは未解決のままです。コーディングが簡単な近似解を考えてみましょう。シーケンシャルアルゴリズムを実行する必要があります。ウェールズ-パウエルアルゴリズムは、頂点の次数による降順に基づく貪欲なソリューションです。元のポリゴンの言語に翻訳され、最初に、重なり合う他のポリゴンの数の降順でポリゴンを並べ替えます。順番に作業して、最初のポリゴンに初期色を与えます。それぞれの連続した段階では、既存の色で次のポリゴンに色を試してみてください。つまり、ある色を選択しませんそのポリゴンの近隣のいずれかですでに使用されています。(使用可能な色の中から選択する方法はたくさんあります。最も使用されていないものを試すか、ランダムに選択してください。)次のポリゴンを既存の色で着色できない場合は、新しい色を作成してそれで色を付けます。
少数の色でカラーリングが完了したら、ゾーンごとの色を色ごとに実行します。構成によって、特定の色の2つのポリゴンが重複しないことが保証されます。
これがのサンプルコードですR
。(Pythonコードはそれほど変わらないでしょう。)最初に、表示されている7つのポリゴン間のオーバーラップについて説明します。
edges <- matrix(c(1,2, 2,3, 3,4, 4,5, 5,1, 2,6, 4,6, 4,7, 5,7, 1,7), ncol=2, byrow=TRUE)
つまり、ポリゴン1と2が重なり、ポリゴン2と3、3と4、...、1と7も重なります。
頂点を降順でソートします。
vertices <- unique(as.vector(edges))
neighbors <- function(i) union(edges[edges[, 1]==i,2], edges[edges[, 2]==i,1])
nbrhoods <- sapply(vertices, neighbors)
degrees <- sapply(nbrhoods, length)
v <- vertices[rev(order(degrees))]
(粗)シーケンシャルカラーリングアルゴリズムは、重なり合うポリゴンでまだ使用されていない最も早い使用可能なカラーを使用します。
color <- function(i) {
n <- neighbors(i)
candidate <- min(setdiff(1:color.next, colors[n]))
if (candidate==color.next) color.next <<- color.next+1
colors[i] <<- candidate
}
データ構造(colors
およびcolor.next
)を初期化し、アルゴリズムを適用します。
colors <- rep(0, length(vertices))
color.next <- 1
temp <- sapply(v, color)
色に従ってポリゴンをグループに分割します。
split(vertices, colors)
この例の出力では、4つの色を使用しています。
$`1`
[1] 2 4
$`2`
[1] 3 6 7
$`3`
[1] 5
$`4`
[1] 1
ポリゴンを重複しない4つのグループに分割しました。この場合、解は最適ではありません({{3,6,5}、{2,4}、{1,7}}は、このグラフの3色です)。ただし、一般的に、それが取得するソリューションはそれほど悪くないはずです。
#whuberによって推奨された方法論は、私に新しい方向性をとるように促しました。これが、2つの関数での私の弧状の解決策です。1つ目はcountOverlapsと呼ばれ、「overlaps」と「ovlpCount」の2つのフィールドを作成して、各ポリゴンについて、どのポリゴンがそれとオーバーラップしたか、およびオーバーラップの数を記録します。2番目の関数explodeOverlapsは、3番目のフィールド「expl」を作成します。これは、重複しないポリゴンの各グループに一意の整数を提供します。ユーザーは、このフィールドに基づいて新しいfcをエクスポートできます。countOverlapsツールはそれ自体で有用であると私は思うので、プロセスは2つの関数に分けられます。コードのだらしさ(および不注意な命名規則)はすみません、それはかなり予備的なものですが、機能します。また、「idName」が fieldは、一意のIDを持つフィールドを表します(整数IDでのみテストされます)。この問題に取り組むために必要なフレームワークを提供してくれたwhuberに感謝します!
def countOverlaps(fc,idName):
intersect = arcpy.Intersect_analysis(fc,'intersect')
findID = arcpy.FindIdentical_management(intersect,"explFindID","Shape")
arcpy.MakeFeatureLayer_management(intersect,"intlyr")
arcpy.AddJoin_management("intlyr",arcpy.Describe("intlyr").OIDfieldName,findID,"IN_FID","KEEP_ALL")
segIDs = {}
featseqName = "explFindID.FEAT_SEQ"
idNewName = "intersect."+idName
for row in arcpy.SearchCursor("intlyr"):
idVal = row.getValue(idNewName)
featseqVal = row.getValue(featseqName)
segIDs[featseqVal] = []
for row in arcpy.SearchCursor("intlyr"):
idVal = row.getValue(idNewName)
featseqVal = row.getValue(featseqName)
segIDs[featseqVal].append(idVal)
segIDs2 = {}
for row in arcpy.SearchCursor("intlyr"):
idVal = row.getValue(idNewName)
segIDs2[idVal] = []
for x,y in segIDs.iteritems():
for segID in y:
segIDs2[segID].extend([k for k in y if k != segID])
for x,y in segIDs2.iteritems():
segIDs2[x] = list(set(y))
arcpy.RemoveJoin_management("intlyr",arcpy.Describe(findID).name)
if 'overlaps' not in [k.name for k in arcpy.ListFields(fc)]:
arcpy.AddField_management(fc,'overlaps',"TEXT")
if 'ovlpCount' not in [k.name for k in arcpy.ListFields(fc)]:
arcpy.AddField_management(fc,'ovlpCount',"SHORT")
urows = arcpy.UpdateCursor(fc)
for urow in urows:
idVal = urow.getValue(idName)
if segIDs2.get(idVal):
urow.overlaps = str(segIDs2[idVal]).strip('[]')
urow.ovlpCount = len(segIDs2[idVal])
urows.updateRow(urow)
def explodeOverlaps(fc,idName):
countOverlaps(fc,idName)
arcpy.AddField_management(fc,'expl',"SHORT")
urows = arcpy.UpdateCursor(fc,'"overlaps" IS NULL')
for urow in urows:
urow.expl = 1
urows.updateRow(urow)
i=1
lyr = arcpy.MakeFeatureLayer_management(fc)
while int(arcpy.GetCount_management(arcpy.SelectLayerByAttribute_management(lyr,"NEW_SELECTION",'"expl" IS NULL')).getOutput(0)) > 0:
ovList=[]
urows = arcpy.UpdateCursor(fc,'"expl" IS NULL','','','ovlpCount D')
for urow in urows:
ovVal = urow.overlaps
idVal = urow.getValue(idName)
intList = ovVal.replace(' ','').split(',')
for x in intList:
intList[intList.index(x)] = int(x)
if idVal not in ovList:
urow.expl = i
urows.updateRow(urow)
ovList.extend(intList)
i+=1
countOverlaps
するには:あなたnbrhoods <- sapply(vertices, neighbors); degrees <- sapply(nbrhoods, length)
は私のコードの2行に対応します:degrees
は重複数です。もちろん、私のソリューションで当然とされているGIS分析のほとんどを反映しているため、コードはより長くなります。つまり、最初に重複するポリゴンを特定し、最後にソリューションを使用してポリゴンデータセットを出力します。あなたが今までより良いカラーリングアルゴリズムを見つけた場合ので、それはプラグインに簡単だろう、グラフ理論計算をカプセル化するために良いでしょう。
しばらく前のことですが、このコードを自分のアプリケーションに使用しましたが、うまく機能しています。ありがとうございました。その一部を書き直して更新し、ラインに適用し(許容範囲で)、大幅にスピードアップしました(以下では、5,000万の交差するバッファーで実行しており、数時間しかかかりません)。
def ExplodeOverlappingLines(fc, tolerance, keep=True):
print('Buffering lines...')
idName = "ORIG_FID"
fcbuf = arcpy.Buffer_analysis(fc, fc+'buf', tolerance, line_side='FULL', line_end_type='FLAT')
print('Intersecting buffers...')
intersect = arcpy.Intersect_analysis(fcbuf,'intersect')
print('Creating dictionary of overlaps...')
#Find identical shapes and put them together in a dictionary, unique shapes will only have one value
segIDs = defaultdict(list)
with arcpy.da.SearchCursor(intersect, ['Shape@WKT', idName]) as cursor:
x=0
for row in cursor:
if x%100000 == 0:
print('Processed {} records for duplicate shapes...'.format(x))
segIDs[row[0]].append(row[1])
x+=1
#Build dictionary of all buffers overlapping each buffer
segIDs2 = defaultdict(list)
for v in segIDs.values():
for segID in v:
segIDs2[segID].extend([k for k in v if k != segID and k not in segIDs2[segID]])
print('Assigning lines to non-overlapping sets...')
grpdict = {}
# Mark all non-overlapping one to group 1
for row in arcpy.da.SearchCursor(fcbuf, [idName]):
if row[0] in segIDs2:
grpdict[row[0]] = None
else:
grpdict[row[0]] = 1
segIDs2sort = sorted(segIDs2.items(), key=lambda x: (len(x[1]), x[0])) #Sort dictionary by number of overlapping features then by keys
i = 2
while None in grpdict.values(): #As long as there remain features not assigned to a group
print(i)
ovset = set() # list of all features overlapping features within current group
s_update = ovset.update
for rec in segIDs2sort:
if grpdict[rec[0]] is None: #If feature has not been assigned a group
if rec[0] not in ovset: #If does not overlap with a feature in that group
grpdict[rec[0]] = i # Assign current group to feature
s_update(rec[1]) # Add all overlapping feature to ovList
i += 1 #Iterate to the next group
print('Writing out results to "expl" field in...'.format(fc))
arcpy.AddField_management(fc, 'expl', "SHORT")
with arcpy.da.UpdateCursor(fc,
[arcpy.Describe(fc).OIDfieldName, 'expl']) as cursor:
for row in cursor:
if row[0] in grpdict:
row[1] = grpdict[row[0]]
cursor.updateRow(row)
if keep == False:
print('Deleting intermediate outputs...')
for fc in ['intersect', "explFindID"]:
arcpy.Delete_management(fc)
この場合、私は通常、次の方法を使用します。
結果はあなたが望んだものになると思います、そしてあなたは重複の数を数えることさえできます。パフォーマンスの点であなたにとってより良いかどうかわからない。