Çakışan yeni çakışmayan çokgenlere patlıyor musunuz?


10

Birden çok şekilde örtüşen çokgenler göz önüne alındığında, bu özelliklerden, diğerleriyle çakışmayan tüm çokgenleri yinelemeli olarak dışa aktarmak istiyorum.

Ürün, bir araya getirildiğinde orijinali oluşturan çakışma olmayan bir dizi özellik olacaktır.

Ürünler daha sonra Bölgesel İstatistiklere girdi olarak kullanılabilir ve bu, her çokgenin Bölgesel İstatistiklerini yinelemekten çok daha hızlı olacaktır.

Bu başarılı olmadan ArcPy kodlamak çalışıyorum.

Bunu yapmak için kod zaten var mı?


Verileri topolojik olarak doğru bir kümeye 'düzleştirmek' demek istiyor musunuz?
nagytech

@Geoist ZonalStats örtüşmeyen çokgenler gerektirir. Çakışan bir koleksiyonunuz olduğunda, bariz ancak verimsiz çözüm, poliler üzerinde döngü yapmak ve bölgesel istatistikleri tek tek hesaplamaktır. Çakışmayan polislerin bir alt kümesini seçmek, onlara zonalstat uygulamak ve yinelemek daha verimli olacaktır. Soru, bu tür seçimlerin nasıl verimli bir şekilde yapılacağını soruyor.
whuber

whuber - Bence @Geoist, giriş çokgenlerinin kesişim noktalarından bir dizi örtüşmeyen çokgen oluşturmayı öneriyor. Bu resme bakın - (yorumlarda resim yayınlayamıyor musunuz?). Giriş solda. Tüm bölge, her biri diğerleriyle kesişen üç çokgenle kaplıdır. Üst üste binmeyen tek alt kümeler tektonlardır ve bunlar Gotanuki'nin birliğin alanı doldurma gereksinimini karşılamaz. Bence Geoist, sağda, zonalstatlar için geçerli olan kesişmeyen bölgeler kümesini oluşturmayı öneriyor
Llaves

Son ürünün ne olması gerektiği konusunda biraz karışıklık olduğunu düşünüyorum. Bir örnek verebilir misiniz? Benim yorumum, çıktının üst üste binmeyen çokgenlerin bir seçimi olmasını istiyor - kalan çokgenleri atarken veya çözerken. Bir veya daha fazla özellik sınıfıyla mı çalışıyorsunuz?
Aaron

1
@Gotanuki gibi geliyor bana, üst üste gelen çokgenler içeren bir çokgen özellik sınıfından yalnızca örtüşmeyen çokgenler içeren minimum sayıda özellik sınıfı oluşturmak istiyor
PolyGeo

Yanıtlar:


14

Bu bir grafik renklendirme problemidir .

Bir grafik boyamanın, bir kenarı paylaşan iki köşe de aynı renge sahip olmayacak şekilde bir grafiğin köşelerine bir renk ataması olduğunu hatırlayın. Özellikle, grafiğin (soyut) köşeleri çokgenlerdir. Kesiştiği zaman (çokgenler olarak) iki köşe (yönlendirilmemiş) kenara bağlanır. Çokgenlerin ( k ) ayrık koleksiyonlarının bir dizisi olan soruna herhangi bir çözüm alırsak ve dizideki her koleksiyona benzersiz bir renk atarsak, grafiğin k- rengini elde etmiş oluruz . Küçük bir k bulmak istenir .

Bu sorun oldukça zor ve keyfi grafikler için çözülmemiş durumda. Kodlaması kolay olan yaklaşık bir çözümü düşünün. Sıralı bir algoritma yapmalıdır. Welsh-Powell algoritması, köşelerin dereceye göre azalan düzenine dayanan açgözlü bir çözümdür. Orijinal çokgenlerin diline çevrilen, önce çokgenleri üst üste geldikleri diğer çokgenlerin sayısına göre azalan sırada sıralayın. Sırayla çalışarak, ilk çokgene başlangıç ​​rengi verin. Birbirini izleyen her adımda, bir sonraki çokgeni mevcut bir renkle renklendirmeyi deneyin: yani, olmayan bir renk seçinbu çokgenin komşuları tarafından zaten kullanılıyor. (Kullanılabilir renkler arasında seçim yapmanın birçok yolu vardır; en az kullanılanı deneyin veya rastgele birini seçin.) Bir sonraki çokgen mevcut bir renkle renklendirilemiyorsa, yeni bir renk oluşturun ve bununla renklendirin.

Az sayıda renkle bir renklendirme elde ettikten sonra, zonalstatları renge göre renklendirin: inşaat ile, belirli bir rengin iki çokgeninin örtüşmemesi garanti edilir.


İşte içinde örnek kod R. (Python kodu çok farklı olmaz.) İlk olarak, gösterilen yedi çokgen arasında örtüşmeleri açıklıyoruz.

Yedi çokgen Haritası

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)

Yani, 1 ve 2 poligonları üst üste bindirilir ve 2 ve 3, 3 ve 4, ..., 1 ve 7 poligonları da üst üste gelir.

Köşeleri azalan dereceye göre sıralayın:

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))]

(Ham) ardışık renklendirme algoritması, üst üste binen herhangi bir çokgen tarafından kullanılmayan en eski rengi kullanır:

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
}

Veri yapılarını ( colorsve color.next) başlatın ve algoritmayı uygulayın:

colors <- rep(0, length(vertices))
color.next <- 1
temp <- sapply(v, color)

Çokgenleri renge göre gruplara ayırın:

split(vertices, colors)

Bu örnekteki çıktı dört renk kullanır:

$`1`
[1] 2 4

$`2`
[1] 3 6 7

$`3`
[1] 5

$`4`
[1] 1

Çokgenlerin dört renklendirilmesi

Çokgenleri örtüşmeyen dört gruba ayırmıştır. Bu durumda çözüm optimal değildir ({{3,6,5}, {2,4}, {1,7}} bu grafik için üç renklidir). Genel olarak aldığı çözüm çok kötü olmamalıdır.


Bunun soruyu cevaplayıp cevaplamadığından veya sorunun ne olduğundan emin değilim, ama iyi bir cevap daha az.
nagytech

@Geoist Gösterimi daha net hale getirmenin veya sorunu daha iyi açıklamanın bir yolu var mı?
whuber

6

#Whuber tarafından önerilen metodoloji bana yeni bir yön vermem için ilham verdi ve işte iki fonksiyonda benim kavisli çözümüm. CountOverlaps olarak adlandırılan ilk alan, iki alan oluşturur: "örtüşmeler" ve "ovlpCount", birbiriyle çakışan her poli ve kaç çakışma meydana geldiğini kaydeder. İkinci işlev olan explodeOverlaps, çakışmayan her bir grup grubuna benzersiz bir tamsayı veren üçüncü bir alan olan "expl" oluşturur. Kullanıcı daha sonra bu alana göre yeni fc'leri dışa aktarabilir. CountOverlaps aracının kendi başına yararlı olabileceğini düşündüğüm için işlem iki işleve ayrılmıştır. Lütfen kodun özensizliğini (ve dikkatsiz adlandırma kuralını) affedin, çünkü oldukça ön, ama işe yarıyor. Ayrıca "idName" alanı benzersiz kimlikleri olan bir alanı temsil eder (yalnızca tamsayı kimlikleriyle test edilir). Bana bu soruna yaklaşmak için gerekli çerçeveyi sağladığınız için teşekkür ederim!

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
Bunu benim çözümüme bağlamak için: kodunuzdaki countOverlapsiki satıra karşılık gelir nbrhoods <- sapply(vertices, neighbors); degrees <- sapply(nbrhoods, length): degreesçakışma sayısıdır. Tabii ki kodunuz daha uzun, çünkü benim çözümümde verilen CBS analizlerinin çoğunu yansıtıyor: ilk önce hangi çokgenlerin çakıştığını ve sonunda çözümü çokgen veri kümeleri çıkarmak için kullandığınızı. Grafik-teorik hesaplamaları kapsüllemek iyi bir fikir olacaktır, bu yüzden daha iyi bir renklendirme algoritması bulursanız, takmak kolay olacaktır.
whuber

1

Kısa bir süre oldu, ama kendi uygulama için bu kodu kullandım ve harika çalışıyor - teşekkür ederim. Bir kısmını güncellemek, satırlara (toleranslı) uygulamak ve önemli ölçüde hızlandırmak için yeniden yazdım (aşağıda - 50 milyon kesişen tamponda çalıştırıyorum ve sadece birkaç saat sürüyor).

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

Bu durumlarda genellikle aşağıdaki yöntemi kullanırım:

  • Feature sınıfını bir UNION üzerinden geçirin; (Çokgenleri tüm kavşaklarında kırar)
  • X, Y ve Alan alanlarını ekleyin ve hesaplayın;
  • Sonucu X, Y, Alan alanlarıyla çözün.

Sonucun istediğiniz sonuç olacağına inanıyorum ve hatta çakışma sayısını sayabilirsiniz. Performans açısından sizin için daha iyi olup olmayacağını bilmiyorum.


2
bu yöntem sizi asgari seçimler dizisi veya örtüşmeyen orijinalin benzersiz özellik sınıfları olan istenen ürüne götürmez. ürünler bölgesel istatistiklere beslenecek ve bu nedenle her özelliğin orijinal geometrisinin korunması hayati önem taşımaktadır.
ndimhypervol

Haklısın özür dilerim. Soruyu iyi anlamadım. Bu durumda ve raster boyutuna bağlı olarak, raster normalde geçici nokta özellik sınıfına (her hücre bir nokta) dönüştürür ve çokgen katmanı arasında uzamsal birleşme gerçekleştiririm. Belki bu çok basit ve performans düşmanca bir yaklaşım ama çalışır ve çakışan çokgenler size herhangi bir sorun vermez.
Alexandre Neto

Bu uzamsal birleşimle ne demek istediğinizi doğru bir şekilde anlarsam, ikinci çözümünüz hala işe yaramaz, Alexandre, çünkü noktalar ve çokgenler arasında çoktan çoğa bir ilişki var. Ne olursa olsun, herhangi bir büyük raster için bu vektör tabanlı yaklaşım son derece verimsiz olacaktır ve büyük rasterler için yapılması imkansız olacaktır.
whuber

@whuber Çok yavaş bir süreç olma konusunda haklısınız (Bana 4284 x 3009 raster ve 2401 çokgenle, iki çekirdekli 2.8Ghz, vistalı 3Gb RAM ile yaklaşık yarım saatte söyleyin). Ama zaten test ettiğim gibi çalışıyor. Uzamsal Birleştirmede bire bir ilişki kullanmanız ve raster değerlerini toplamanız gerekir (ortalama, Toplam vb.). Sonuç, orijinaline benzer bir vektör çokgen katmanı olacaktır, ancak her bir çokgeni kesişen toplu raster değerlerine sahip yeni bir sütun olacaktır. Optimal bir çözüm olmamak daha az programlama becerisine (benim gibi :-)) sahip biri için yararlı olabilir.
Alexandre Neto
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.