การกระจายการซ้อนทับกับรูปหลายเหลี่ยมที่ไม่ทับซ้อนใหม่หรือไม่


10

ด้วยรูปหลายเหลี่ยมหลายรูปที่ซ้อนทับกันหลายวิธีฉันต้องการส่งออกจากรูปหลายเหลี่ยมเหล่านี้ทั้งหมดที่ไม่ทับซ้อนกับรูปอื่น ๆ ซ้ำแล้วซ้ำอีก

ผลิตภัณฑ์จะเป็นคุณสมบัติจำนวนหนึ่งโดยไม่มีการทับซ้อนกันซึ่งเมื่อรวมเข้าด้วยกันจะทำให้เป็นของแท้

ผลิตภัณฑ์นั้นสามารถนำมาใช้เป็นข้อมูลเข้าสู่ Zonal Statistics และมันจะเร็วกว่าการทำซ้ำ Zonal Statistics มากกว่ารูปหลายเหลี่ยมแต่ละรูป

ฉันได้ลองรหัสนี้ใน ArcPy ไม่สำเร็จ

รหัสสำหรับการทำเช่นนี้มีอยู่แล้ว?


คุณหมายถึงคุณต้องการ 'แบน' ข้อมูลลงในชุดที่ถูกต้องทอพอโลยีหรือไม่?
nagytech

@Geoist ZonalStats ต้องการรูปหลายเหลี่ยมที่ไม่ทับซ้อนกัน เมื่อคุณมีคอลเลกชันที่ทับซ้อนกันโซลูชันที่เห็นได้ชัด แต่ไม่มีประสิทธิภาพคือการวนซ้ำ polys และคำนวณสถิติแต่ละส่วน มันจะมีประสิทธิภาพมากกว่าในการเลือกชุดย่อยของ polys ที่ไม่ทับซ้อนกันใช้ zonalstats กับมันและทำซ้ำ คำถามถามว่าจะเลือกอย่างมีประสิทธิภาพได้อย่างไร
whuber

whuber - ฉันคิดว่า @Geoist แนะนำให้สร้างชุดรูปหลายเหลี่ยมที่ไม่ทับซ้อนกันจากจุดตัดของรูปหลายเหลี่ยมที่ป้อนเข้า ดูภาพนี้- (ไม่สามารถโพสต์ภาพในความคิดเห็นได้) อินพุตอยู่ทางซ้าย พื้นที่ทั้งหมดถูกปกคลุมด้วยรูปหลายเหลี่ยมสามรูปซึ่งแต่ละส่วนจะตัดกันทั้งสองส่วน ชุดย่อยที่ไม่ทับซ้อนเพียงชุดเดียวคือซิงเกิลตันและชุดย่อยเหล่านี้ไม่เป็นไปตามข้อกำหนดของ Gotanuki ที่สหภาพเติมพื้นที่ ฉันคิดว่า Geoist กำลังแนะนำให้สร้างชุดของพื้นที่ที่ไม่ตัดกันทางด้านขวาซึ่งใช้ได้สำหรับ zonalstats
Llaves

ฉันคิดว่ามีความสับสนเกี่ยวกับผลิตภัณฑ์ขั้นสุดท้ายที่ควรจะเป็น คุณช่วยยกตัวอย่างได้ไหม การตีความของฉันคือคุณต้องการให้ผลลัพธ์เป็นรูปหลายเหลี่ยมที่ไม่ทับซ้อนกัน - ในขณะที่ทิ้งหรือยุบรูปหลายเหลี่ยมที่เหลืออยู่ คุณทำงานกับคลาสฟีเจอร์หนึ่งคลาสหรือไม่?
แอรอน

1
ฟังดูแล้วเหมือน @gotanuki ต้องการสร้างจำนวนคลาสฟีเจอร์ขั้นต่ำที่มีรูปหลายเหลี่ยมที่ไม่ทับซ้อนกันจากคลาสฟีเจอร์รูปหลายเหลี่ยมพร้อมรูปหลายเหลี่ยมที่ซ้อนทับกัน
PolyGeo

คำตอบ:


14

นี่คือปัญหากราฟสี

โปรดจำไว้ว่าการระบายสีกราฟเป็นการกำหนดสีให้กับจุดยอดของกราฟในลักษณะที่ไม่มีจุดยอดสองอันที่แบ่งขอบจะมีสีเดียวกัน โดยเฉพาะจุดยอด (นามธรรม) ของกราฟคือรูปหลายเหลี่ยม จุดยอดสองจุดเชื่อมต่อกับขอบ (undirected) เมื่อใดก็ตามที่พวกเขาตัดกัน (เป็นรูปหลายเหลี่ยม) หากเราใช้วิธีการแก้ปัญหาใด ๆ - ซึ่งเป็นลำดับของ (พูดk ) คอลเลกชัน disjoint ของรูปหลายเหลี่ยม - และกำหนดสีที่ไม่ซ้ำกันให้กับแต่ละคอลเลกชันในลำดับนั้นเราจะได้รับk -กราฟของสี . เป็นที่พึงประสงค์ที่จะหาขนาดเล็กk

ปัญหานี้ค่อนข้างยากและยังไม่มีการแก้ไขสำหรับกราฟตามอำเภอใจ พิจารณาโซลูชันโดยประมาณที่ง่ายต่อการเขียนโค้ด อัลกอริทึมแบบลำดับควรทำ อัลกอรึทึมของเวลส์ - พาวเวลล์เป็นวิธีการแก้ปัญหาที่เป็นไปตามการจัดลำดับของจุดยอดตามลำดับ แปลเป็นภาษาของรูปหลายเหลี่ยมดั้งเดิมอันดับแรกเรียงลำดับรูปหลายเหลี่ยมจากมากไปหาน้อยของรูปหลายเหลี่ยมอื่น ๆ ที่ซ้อนทับกัน การทำงานตามลำดับให้สีเริ่มต้นเป็นรูปหลายเหลี่ยมแรก ในแต่ละขั้นตอนต่อเนื่องให้ลองสีรูปหลายเหลี่ยมถัดไปด้วยสีที่มีอยู่นั่นคือเลือกสีที่ไม่ใช่ใช้แล้วโดยเพื่อนบ้านของรูปหลายเหลี่ยมนั้น (มีหลายวิธีให้เลือกระหว่างสีที่มีให้ลองใช้สีที่มีการใช้งานน้อยที่สุดหรือเลือกแบบสุ่ม) หากรูปหลายเหลี่ยมถัดไปไม่สามารถใช้สีที่มีอยู่ให้สร้างสีใหม่และใช้สีนั้น

เมื่อคุณประสบความสำเร็จในการทำสีด้วยสีจำนวนน้อยให้ดำเนินการสี zonalstats ตามสี: จากการก่อสร้างคุณรับรองได้ว่าไม่มีรูปหลายเหลี่ยมสองสีที่ทับซ้อนกัน


Rนี่คือตัวอย่างรหัสใน (รหัสไพ ธ อนจะไม่แตกต่างกันมาก) อันดับแรกเราอธิบายการทับซ้อนระหว่างรูปเจ็ดเหลี่ยมที่แสดง

แผนที่เจ็ดรูปหลายเหลี่ยม

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

รูปสี่เหลี่ยมของรูปหลายเหลี่ยม

มันแบ่งพาร์ติชันรูปหลายเหลี่ยมออกเป็นสี่กลุ่มที่ไม่ทับซ้อนกัน ในกรณีนี้การแก้ปัญหาไม่เหมาะสม ({{3,6,5}, {2,4}, {1,7}} เป็นสามสีสำหรับกราฟนี้) โดยทั่วไปแล้ววิธีการแก้ปัญหาที่ได้รับไม่ควรเลวร้ายเกินไป


ฉันไม่แน่ใจว่าคำถามนี้ตอบคำถามหรือว่าคำถามนั้นคืออะไร แต่เป็นคำตอบที่ดีไม่น้อยไปกว่านี้
nagytech

@ Geoist มีวิธีใดบ้างที่ฉันสามารถทำให้ภาพประกอบชัดเจนขึ้นหรืออธิบายปัญหาได้ดีขึ้น?
whuber

6

วิธีการที่แนะนำโดย #whuber เป็นแรงบันดาลใจให้ฉันไปสู่ทิศทางใหม่และนี่คือโซลูชัน arcpy ของฉันในสองฟังก์ชั่น ตัวแรกที่เรียกว่า countOverlaps สร้างสองฟิลด์ "เหลื่อม" และ "ovlpCount" เพื่อบันทึกสำหรับโพลีแต่ละอันที่ polys ซ้อนทับกับมันและเกิดการทับซ้อนกันกี่ครั้ง ฟังก์ชันที่สองคือ explodeOverlaps สร้างเขตข้อมูลที่สาม "expl" ซึ่งจะให้จำนวนเต็มที่ไม่ซ้ำกับแต่ละกลุ่มของ polys ที่ไม่ทับซ้อนกัน ผู้ใช้สามารถส่งออก fc ใหม่ตามฟิลด์นี้ กระบวนการแตกออกเป็นสองฟังก์ชั่นเพราะฉันคิดว่าเครื่องมือ countOverlaps สามารถพิสูจน์ได้ว่ามีประโยชน์ด้วยตัวเอง โปรดแก้ตัวความสะเพร่าของรหัส (และการตั้งชื่อแบบประมาท) เนื่องจากเป็นรหัสเบื้องต้น แต่ใช้งานได้ ตรวจสอบให้แน่ใจด้วยว่า "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

2
ในการเชื่อมต่อสิ่งนี้กับโซลูชันของฉัน: คุณcountOverlapsสอดคล้องกับสองบรรทัดnbrhoods <- sapply(vertices, neighbors); degrees <- sapply(nbrhoods, length)ในรหัสของฉัน: degreesคือจำนวนที่ทับซ้อนกัน แน่นอนว่ารหัสของคุณนั้นยาวกว่าเพราะมันสะท้อนการวิเคราะห์ GIS ส่วนใหญ่ที่ให้ไว้ในโซลูชันของฉัน: นั่นคือคุณระบุว่ารูปหลายเหลี่ยมซ้อนทับกันครั้งแรกและในตอนท้ายคุณใช้โซลูชันเพื่อส่งออกชุดข้อมูลรูปหลายเหลี่ยม มันเป็นความคิดที่ดีที่จะสรุปการคำนวณกราฟ - ทฤษฎีดังนั้นถ้าคุณเคยพบอัลกอริทึมการระบายสีที่ดีกว่ามันจะง่ายในการเสียบ
whuber

1

มันไม่นานมานี้ แต่ฉันใช้รหัสนี้สำหรับแอปพลิเคชันของฉันเองและใช้งานได้ดีมาก - ขอบคุณ ฉันเขียนส่วนหนึ่งใหม่เพื่ออัปเดตนำไปใช้กับบรรทัด (ด้วยความอดทน) และเพิ่มความเร็วขึ้นอย่างมาก (ด้านล่าง - ฉันใช้งานบนบัฟเฟอร์ที่ตัดกัน 50 ล้านและใช้เวลาเพียงไม่กี่ชั่วโมง)

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)

-3

ในกรณีนี้ฉันมักใช้วิธีต่อไปนี้:

  • ผ่านคลาสฟีเจอร์ผ่าน UNION (มันทำลายรูปหลายเหลี่ยมในจุดตัดทั้งหมด)
  • เพิ่มเขตข้อมูล X, Y และพื้นที่และคำนวณ
  • ละลายผลลัพธ์โดยฟิลด์ X, Y, พื้นที่

ฉันเชื่อว่าผลลัพธ์จะเป็นสิ่งที่คุณต้องการและคุณสามารถนับจำนวนของการทับซ้อนได้ ไม่ทราบว่าในแง่ของประสิทธิภาพจะดีกว่าสำหรับคุณหรือไม่


2
วิธีนี้จะไม่นำคุณไปสู่ผลิตภัณฑ์ที่ต้องการซึ่งเป็นชุดการเลือกขั้นต่ำหรือคลาสคุณลักษณะเฉพาะของต้นฉบับที่ไม่ทับซ้อนกัน ผลิตภัณฑ์จะถูกป้อนเข้าสู่สถิติเชิงพื้นที่ดังนั้นการรักษารูปทรงเรขาคณิตดั้งเดิมของแต่ละคุณสมบัติจึงมีความสำคัญ
ndimhypervol

คุณถูกต้องขอโทษ ฉันไม่เข้าใจคำถามนี้ดี ในกรณีนั้นและโดยทั่วไปขึ้นอยู่กับขนาดของแรสเตอร์ฉันจะแปลงแรสเตอร์เป็นคลาสคุณสมบัติจุดชั่วคราว (แต่ละจุดต่อเซลล์) และทำการเชื่อมต่อเชิงพื้นที่ระหว่างมันกับเลเยอร์รูปหลายเหลี่ยม อาจเป็นวิธีที่ง่ายและมีประสิทธิภาพที่ไม่เป็นมิตร แต่ใช้งานได้และรูปหลายเหลี่ยมที่ซ้อนกันจะไม่ทำให้คุณมีปัญหาใด ๆ
Alexandre Neto

ถ้าฉันเข้าใจอย่างถูกต้องว่าคุณหมายถึงอะไรโดยการเข้าร่วมเชิงพื้นที่การแก้ปัญหาครั้งที่สองของคุณยังไม่สามารถทำได้ Alexandre เพราะมีความสัมพันธ์แบบหลายต่อหลายจุดระหว่างจุดและรูปหลายเหลี่ยม ไม่ว่าสำหรับแรสเตอร์ที่มีขนาดใหญ่วิธีการแบบเวกเตอร์นี้จะไม่มีประสิทธิภาพมากและสำหรับแรสเตอร์ขนาดใหญ่มันเป็นไปไม่ได้ที่จะดำเนินการ
whuber

@whuber คุณถูกต้องเกี่ยวกับกระบวนการที่ช้ามาก (ใช้เวลาประมาณครึ่งชั่วโมงกับ 4284 x 3009 raster และ 2401 รูปหลายเหลี่ยมใน dualcore 2.8Ghz, 3Gb RAM พร้อม vista) แต่มันได้ผลเหมือนที่ฉันได้ทำการทดสอบแล้ว ในการเข้าร่วมเชิงพื้นที่คุณต้องใช้ความสัมพันธ์แบบหนึ่งต่อหนึ่งและรวมค่า raster (ตามค่าเฉลี่ย, ผลรวม, ฯลฯ ... ) ผลลัพธ์จะเป็นเวกเตอร์รูปหลายเหลี่ยมเลเยอร์คล้ายกับต้นฉบับ แต่มีคอลัมน์ใหม่พร้อมค่าแรสเตอร์รวมที่ตัดกันแต่ละรูปหลายเหลี่ยม ไม่ใช่การแก้ปัญหาที่ดีที่สุดซึ่งอาจเป็นประโยชน์สำหรับคนที่มีทักษะการเขียนโปรแกรมน้อยลง (เช่นฉัน :-))
Alexandre Neto
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.