여러 방식으로 겹치는 여러 다각형이 주어지면 다른 지형과 겹치지 않는 모든 다각형을 반복적으로 이러한 기능에서 내보내고 싶습니다.
이 제품은 서로 합치면 원본을 구성하는 겹침이없는 여러 기능입니다.
그런 다음이 제품을 구역 통계에 대한 입력으로 사용할 수 있으며 이는 각 다각형에서 구역 통계를 반복하는 것보다 훨씬 빠릅니다.
ArcPy에서 이것을 성공적으로 코딩하려고 노력했습니다.
이를 수행하는 코드가 이미 존재합니까?
여러 방식으로 겹치는 여러 다각형이 주어지면 다른 지형과 겹치지 않는 모든 다각형을 반복적으로 이러한 기능에서 내보내고 싶습니다.
이 제품은 서로 합치면 원본을 구성하는 겹침이없는 여러 기능입니다.
그런 다음이 제품을 구역 통계에 대한 입력으로 사용할 수 있으며 이는 각 다각형에서 구역 통계를 반복하는 것보다 훨씬 빠릅니다.
ArcPy에서 이것을 성공적으로 코딩하려고 노력했습니다.
이를 수행하는 코드가 이미 존재합니까?
답변:
이것은 그래프 색상 문제 입니다.
그래프 색상은 가장자리를 공유하는 두 정점이 동일한 색상을 갖지 않도록 그래프의 정점에 색상을 할당하는 것입니다. 구체적으로, 그래프의 (추상) 정점은 다각형입니다. 두 개의 정점은 교차 할 때마다 (다각형으로) (무 방향) 모서리에 연결됩니다. 우리가 문제에 대한 해결책을 취하면 (즉, k의 분리 된 다각형 컬렉션 시퀀스) 시퀀스의 각 컬렉션에 고유 한 색상을 할당 하면 그래프 의 k 색을 얻습니다. . 작은 k 를 찾는 것이 바람직합니다 .
이 문제는 매우 어렵고 임의의 그래프에서는 해결되지 않은 상태입니다. 코딩하기 쉬운 대략적인 솔루션을 고려하십시오. 순차적 인 알고리즘이해야합니다. 웨일즈-포웰 알고리즘은 정점의 차수 순서에 따른 탐욕스러운 솔루션입니다. 원래 다각형의 언어로 번역 된 다음, 다각형을 겹치는 다른 다각형 수의 내림차순으로 정렬합니다. 순서대로 작업하려면 첫 번째 다각형에 초기 색을 지정하십시오. 연속되는 각 단계에서 기존 색상으로 다음 다각형에 색상을 지정하십시오. 즉, 그렇지 않은 색상을 선택하십시오.해당 다각형의 이웃에 이미 사용되었습니다. (사용 가능한 색상 중에서 선택하는 방법은 여러 가지가 있습니다. 가장 많이 사용되지 않은 색상을 선택하거나 무작위로 다른 색상을 선택하십시오.) 다음 다각형에 기존 색상으로 색상을 지정할 수없는 경우 새 색상을 만들어 그 색상으로 색상을 지정하십시오.
적은 수의 색상으로 채색을 완료 한 후에는 색상별로 구역 별 통계를 수행합니다. 구성에 따라 주어진 색상의 두 다각형이 겹치지 않도록 보장 할 수 있습니다.
의 샘플 코드는 다음과 같습니다 R
. (파이썬 코드는 크게 다르지 않을 것입니다.) 먼저, 우리는 표시된 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)
이 예제의 출력은 네 가지 색상을 사용합니다.
$`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가 추천 한 방법론은 새로운 방향으로 나아가도록 영감을 주었으며, 여기에는 두 가지 기능이있는 아크 피 솔루션이 있습니다. countOverlaps라고하는 첫 번째는 "overlaps"와 "ovlpCount"라는 두 개의 필드를 만들어 폴리와 겹친 각 폴리에 대해 기록하고 오버랩이 발생한 횟수를 기록합니다. 두 번째 함수 인 explodeOverlaps는 겹치지 않는 폴리 그룹마다 고유 한 정수를 제공하는 세 번째 필드 인 "expl"을 만듭니다. 그런 다음이 필드를 기반으로 새 fc를 내보낼 수 있습니다. countOverlaps 도구가 그 자체로 유용 할 수 있다고 생각하기 때문에 프로세스는 두 가지 기능으로 나뉩니다. 꽤 예비 적이므로 코드의 부패 (와 부주의 한 명명 규칙)를 용서하십시오. 또한 "idName"이 field는 고유 ID를 가진 필드를 나타냅니다 (정수 ID로만 테스트). 이 문제에 접근하는 데 필요한 프레임 워크를 제공해 주셔서 감사합니다.
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)
에 해당합니다 degrees
. 물론 코드는 내 솔루션에서 당연한 것으로 여겨지는 대부분의 GIS 분석을 반영하기 때문에 더 길어집니다. 그래프 이론적 계산을 캡슐화하는 것이 좋습니다. 따라서 더 나은 색상 알고리즘을 찾으면 쉽게 연결할 수 있습니다.
시간이 오래 걸렸지 만이 코드를 내 응용 프로그램에 사용했으며 제대로 작동했습니다. 감사합니다. 나는 그것을 업데이트하여 업데이트를 위해 라인에 적용하고 (허용 오차가있는) 라인에 적용하고 속도를 크게 올렸다.
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)
이 경우 일반적으로 다음 방법을 사용합니다.
결과가 원하는 결과가 될 것이라고 생각하며 중복 횟수를 계산할 수도 있습니다. 성능면에서 당신에게 더 좋을지 모르겠습니다.