Arcpy kullanarak yönlendirilmiş bir tampon nasıl oluşturulur?


9

Arcpy kullanarak şekil dosyamdaki her çokgen için yönlendirilmiş bir tampon oluşturmak istiyorum. Yönlendirmeli olarak tamponun yönünü sınırlayan iki a1 ve a2 açısına sahip olduğumu kastediyorum. Bu, aşağıdaki grafikte gösterilmiştir: resim açıklamasını buraya girin

Herhangi bir fikir?


3
Kişinin açılar hakkında daha fazla bilgiye ihtiyacı vardır. Açıları hangi eksenden ölçüyorsunuz? CW veya CCW? Çokgendeki her bir açıyı nasıl bulursunuz? Ne tür çokgenlerle uğraşıyoruz? (Bir daire çokgen değildir.)
Paul

1
@ Paul ama I 1 Okuduğum kadar bir çember, bir poligon olduğunu düşündüm bu .
PolyGeo

+1 de! Sorunu kolayca göstermek için daire kullandım. Çokgenler, eCognition'daki bir segmentasyonun ve ardından bir sınıfı tanımlamak için bir sınıflamanın sonucudur. A1 ve a2 açıları, bölümlere ayrılmış uydu görüntüsünün aydınlatma azimut açısından türetilir. Örnekte, azimut açısı 0, a1 ve a2'ye 0 +/- 15 ° 'ye eşit olacaktır (keyfi olarak 15 °' ye sabitlenir).
WAF

2
@PolyGeo "Poligon" CBS'de matematikten biraz farklı kullanılır. Burada , (iki boyutlu) bir bölgenin dijital olarak temsil edilmesini veya kapatılmasını ifade eder . Bölgeler tipik olarak (ancak her zaman değil) çokgen yaklaşımlarla temsil edilir , ancak - bilgisayar temsillerimizin yalnızca yaklaşık olduğunu bildiğimiz için - "yaklaşım" ı düşer ve sadece "çokgen" kullanırız.
whuber

Yanıtlar:


20

özet

Bu cevap, soruyu daha büyük bir bağlama yerleştirir, özelliklerin şekil dosyası temsili için geçerli olan etkili bir algoritma açıklar (noktaların "vektörleri" veya "linestrings" olarak), uygulamasının bazı örneklerini gösterir ve kullanım veya bir CBS ortamı.

Arka fon

Bu morfolojik genişlemeye bir örnektir . Genel olarak, bir genişleme bir bölgenin noktalarını mahallelerine "yayar"; kurdukları noktaların toplanması "genişleme" dir. CBS'deki uygulamalar çoktur: yangının yayılmasını, medeniyetlerin hareketini, bitkilerin yayılmasını ve çok daha fazlasını modellemek.

Matematiksel olarak ve çok büyük (ama kullanışlı) bir genellikte, bir dilatasyon Riemann manifoldunda (düzlem, küre veya elipsoid gibi) bir dizi noktaya yayılır . Yayılma, bu noktalarda teğet demetinin bir alt kümesi tarafından belirlenir . Bu, noktaların her birinde bir dizi vektörün (yönler ve mesafeler) verildiği anlamına gelir (buna "mahalle" diyorum); bu vektörlerin her biri temel noktasında başlayan bir jeodezik yolu açıklar . Temel nokta tüm bu yolların uçlarına "yayılır". (Geleneksel olarak görüntü işlemede kullanılan "dilatasyon" un çok daha sınırlı tanımı için Wikipedia makalesine bakın . Yayma fonksiyonu üstel harita olarak bilinir diferansiyel geometride.)

Bir özelliğin "tamponlanması" , böyle bir genişlemenin en basit örneklerinden biridir: özelliğin her noktası etrafında sabit yarıçaplı bir disk (tampon yarıçapı) oluşturulur (en azından kavramsal olarak). Bu disklerin birleşimi arabellektir.

Bu soru, yayılmanın sadece belirli bir açı aralığında (yani dairesel bir sektör içinde) gerçekleşmesine izin verildiği biraz daha karmaşık bir genişlemenin hesaplanmasını ister. Bu sadece kayda değer kavisli yüzey içermeyen özellikler (küre veya elipsoid üzerindeki küçük özellikler veya düzlemdeki herhangi bir özellik gibi) için mantıklıdır . Düzlemde çalışırken, tüm sektörleri aynı yönde yönlendirmek de anlamlıdır. (Ancak yangının rüzgar tarafından yayılmasını modelliyor olsaydık, sektörlerin rüzgarla yönlendirilmesini isterdik ve boyutları rüzgar hızına göre değişebilir: verdiğim dilatasyonun genel tanımı için bir motivasyon. (Elipsoid gibi kavisli yüzeylerde genel olarak tüm sektörleri "aynı" yönde yönlendirmek imkansızdır.)

Aşağıdaki durumlarda dilatasyonun hesaplanması nispeten kolaydır:

  • Özellik düzlemdedir (yani, özelliğin bir haritasını genişletiyoruz ve umarım harita oldukça doğrudur).

  • Dilatasyon sabit olacaktır : özelliğin her noktasına yayılma, aynı yönelime sahip uygun mahallelerde gerçekleşecektir.

  • Bu ortak mahalle dışbükeydir. Konveksite hesaplamaları büyük ölçüde basitleştirir ve hızlandırır.

Bu soru bu gibi özel durumlarda uymaktadır: keyfi çokgenlerin kökenleri (geldikleri disklerin merkezleri) temel noktalarda bulunan dairesel sektörler tarafından genişletilmesini istemektedir. Bu sektörlerin 180 dereceden fazla olmaması koşuluyla dışbükey olacaktır. (Daha büyük sektörler her zaman ikiye dışbükey sektörlere bölünebilir; daha küçük iki genişlemenin birleşmesi istenen sonucu verecektir.)


uygulama

Öklid hesaplamaları yaptığımız için - düzlemdeki yayılımı yapıyoruz - bir noktayı sadece dilatasyon mahallesini o noktaya çevirerek genişletebiliriz. (Bunu yapabilmek için mahallenin bir kökene ihtiyacı vartemel noktaya karşılık gelecektir. Örneğin, bu sorudaki sektörlerin kökeni, oluşturuldukları çemberin merkezidir. Bu köken sektörün sınırları üzerindedir. Standart CBS tamponlama operasyonunda mahalle, merkezinde kökeni olan bir dairedir; şimdi kökeni dairenin iç kısmında yatıyor. Bir kökeni seçmek hesaplama açısından çok önemli değildir, çünkü kökeni değiştirmek sadece tüm genişlemeyi kaydırır, ancak doğal olayları modellemek açısından büyük bir sorun olabilir. Aşağıdaki sectorkoddaki işlev, bir kaynağın nasıl belirtilebileceğini gösterir.)

Bir çizgi segmentini genişletmek zor olabilir, ancak dışbükey bir mahalle için, dikkatlice seçilmiş bir paralelkenarla birlikte iki uç noktanın genişlemesinin birleşimi olarak genişlemeyi oluşturabiliriz. (Mekânın ilgisi için böyle matematiksel iddiaları kanıtlamak için duraklamayacağım, ancak okuyucuyu kendi kanıtlarını denemeye teşvik edeceğim, çünkü bu anlayışlı bir alıştırmadır.) İşte üç sektör (pembe olarak gösterilen) kullanan bir örnek. Birim yarıçapları vardır ve açıları başlıklarda verilmiştir. Çizgi parçasının kendisi 2 uzunluğuna sahiptir, yataydır ve siyah olarak gösterilir:

Segment dilatasyonları

Paralelkenar, segmentten mümkün olduğunca uzak olan pembe noktaların yalnızca dikey yönde konumlandırılmasıyla bulunur . Bu, segmente paralel çizgiler boyunca iki alt nokta ve iki üst nokta verir. Sadece dört noktayı bir paralelkenar (mavi ile gösterilen) olarak birleştirmeliyiz. Sağda, sektörün kendisi sadece bir çizgi segmenti olsa bile (gerçek bir çokgen değil) bunun nasıl anlamlı olduğuna dikkat edin: orada, segmentteki her nokta bir mesafe aralığı için kuzeyde 171 derece doğuya çevrildi. Bu uç noktaların kümesi gösterilen paralelkenardır. Bu hesaplamanın ayrıntıları aşağıdaki kodda buffertanımlanan işlevde görünür dilate.edges.

Bir çoklu hattı genişletmek için , onu oluşturan noktaların ve segmentlerin genişlemelerinin birleşimini oluştururuz. Son iki satır dilate.edgesbu döngüyü gerçekleştirir.

Bir çokgenin genişletilmesi için çokgenin iç kısmı ve sınırının genişletilmesi gerekir. (Bu iddia, dilatasyon mahallesi hakkında bazı varsayımlar yapar. Birincisi, tüm mahallelerin çokgenin dilatasyona dahil edilmesini garanti eden (0,0) noktasını içermesidir. Değişken mahallelerde, herhangi bir iç mekanın dilatasyonunun da olduğunu varsayar. Çokgenin noktası, dilatasyon sınırlarının ötesine uzanmayacaktır. Bu durum sabit mahalleler için geçerlidir.)

Bunun nasıl çalıştığına dair bazı örneklere bakalım , önce bir nonagon (ayrıntıyı ortaya çıkarmak için seçildi) ve daha sonra bir daire (sorudaki şekle uydurmak için seçildi). Örnekler aynı üç mahalleyi kullanmaya devam edecek, ancak 1/3 yarıçapına küçülecektir.

Nonagonun dilatasyonları

Bu şekilde çokgenin içi gri, nokta genişlemeleri (sektörler) pembe ve kenar genişlikleri (paralelkenarlar) mavidir.

Bir dairenin genişlemeleri

"Daire" gerçekten sadece 60-gon, ama bir daireye güzel bir şekilde yaklaşıyor.


Verim

Temel özellik N noktaları ve dilatasyon komşuları M noktaları ile temsil edildiğinde, bu algoritma O ( NM) çabası gerektirir. Bunu, sendikadaki O (N M log (N M)) çaba gerektirebilecek köşelerin ve kenarların karmaşasını basitleştirerek takip etmeliyiz: bu, CBS'den yapmasını isteyen bir şeydir; bunu programlamamız gerekmiyor.

Hesaplama çabası, dışbükey temel özellikler için O (M + N) olarak geliştirilebilir (çünkü orijinal iki şeklin sınırlarını tanımlayan köşe listelerini uygun bir şekilde birleştirerek yeni sınırın etrafında nasıl seyahat edebileceğinizi öğrenebilirsiniz). Bunun daha sonra temizlenmesi gerekmez.

Taban özelliği etrafında ilerledikçe dilatasyon mahallesi yavaşça boyut ve / veya yön değiştirdiğinde, kenarın dilatasyonu, uç noktalarındaki dilatasyonların birleşiminin dışbükey gövdesinden yakın bir şekilde tahmin edilebilir. İki dilatasyon mahallesinde M1 ve M2 noktaları varsa, bu, Shamos & Preparata, Hesaplamalı Geometri'de açıklanan bir algoritma kullanılarak O (M1 + M2) çabasıyla bulunabilir . Bu nedenle, K = M1 + M2 + ... + M (N), N dilatasyon mahallelerindeki toplam köşe sayısı sayısı olarak, dilatasyonu O (K * log (K)) zamanında hesaplayabiliriz.

İstediğimiz tek şey basit bir tamponsa neden böyle bir genellemeyle uğraşmak istiyoruz? Dünyadaki büyük özellikler için, gerçekte sabit boyuta sahip bir dilatasyon mahallesi (disk gibi), bu hesaplamaların yapıldığı haritada değişen boyutlara sahip olabilir. Böylece Öklid geometrisinin tüm avantajlarından yararlanmaya devam ederken elipsoid için doğru hesaplamalar yapmanın bir yolunu elde ederiz .


kod

Örnekler, Ren sevdiğiniz dile (Python, C ++, vb.) Kolayca taşınabilen bu prototiple üretilmiştir . Yapıda, bu cevapta rapor edilen analize paraleldir ve bu nedenle ayrı bir açıklamaya gerek yoktur. Yorumlar bazı ayrıntıları açıklar.

(Trigonometrik hesaplamaların yalnızca normal çokgenler olan örnek özellikleri ve sektörleri oluşturmak için kullanıldığını belirtmek ilginç olabilir . Dilatasyon hesaplamalarının hiçbir kısmı herhangi bir trigonometri gerektirmez.)

#
# Dilate the vertices of a polygon/polyline by a shape.
#
dilate.points <- function(p, q) {
  # Translate a copy of `q` to each vertex of `p`, resulting in a list of polygons.
  pieces <- apply(p, 1, function(x) list(t(t(q)+x)))
  lapply(pieces, function(z) z[[1]]) # Convert to a list of matrices
}
#
# Dilate the edges of a polygon/polyline `p` by a shape `q`. 
# `p` must have at least two rows.
#
dilate.edges <- function(p, q) {
  i <- matrix(c(0,-1,1,0), 2, 2)       # 90 degree rotation
  e <- apply(rbind(p, p[1,]), 2, diff) # Direction vectors of the edges
  # Dilate a single edge from `x` to `x+v` into a parallelogram
  # bounded by parts of the dilation shape that are at extreme distances
  # from the edge.
  buffer <- function(x, v) {
    y <- q %*% i %*% v # Signed distances orthogonal to the edge
    k <- which.min(y)  # Find smallest distance, then the largest *after* it
    l <- (which.max(c(y[-(1:k)], y[1:k])) + k-1) %% length(y)[1] + 1
    list(rbind(x+q[k,], x+v+q[k,], x+v+q[l,], x+q[l,])) # A parallelogram
  }
  # Apply `buffer` to every edge.
  quads <- apply(cbind(p, e), 1, function(x) buffer(x[1:2], x[3:4]))
  lapply(quads, function(z) z[[1]]) # Convert to a list of matrices
}
#----------------------- (This ends the dilation code.) --------------------------#
#
# Display a polygon and its point and edge dilations.
# NB: In practice we would submit the polygon, its point dilations, and edge 
#     dilations to the GIS to create and simplify their union, producing a single
#     polygon.  We keep the three parts separate here in order to illustrate how
#     that polygon is constructed.
#
display <- function(p, d.points, d.edges, ...) {
  # Create a plotting region covering the extent of the dilated figure.
  x <- c(p[,1], unlist(lapply(c(d.points, d.edges), function(x) x[,1])))
  y <- c(p[,2], unlist(lapply(c(d.points, d.edges), function(x) x[,2])))
  plot(c(min(x),max(x)), c(min(y),max(y)), type="n", asp=1, xlab="x", ylab="y", ...)
  # The polygon itself.
  polygon(p, density=-1, col="#00000040")
  # The dilated points and edges.
  plot.list <- function(l, c) lapply(l, function(p) 
                  polygon(p, density=-1, col=c, border="#00000040"))
  plot.list(d.points, "#ff000020")
  plot.list(d.edges, "#0000ff20")
  invisible(NULL) # Doesn't return anything
}
#
# Create a sector of a circle.
# `n` is the number of vertices to use for approximating its outer arc.
#
sector <- function(radius, arg1, arg2, n=1, origin=c(0,0)) {
  t(cbind(origin, radius*sapply(seq(arg1, arg2, length.out=n), 
                  function(a) c(cos(a), sin(a)))))
}
#
# Create a polygon represented as an array of rows.
#
n.vertices <- 60 # Inscribes an `n.vertices`-gon in the unit circle.
angles <- seq(2*pi, 0, length.out=n.vertices+1)
angles <- angles[-(n.vertices+1)]
polygon.the <- cbind(cos(angles), sin(angles))
if (n.vertices==1) polygon.the <- rbind(polygon.the, polygon.the)
#
# Dilate the polygon in various ways to illustrate.
#
system.time({
  radius <- 1/3
  par(mfrow=c(1,3))
  q <- sector(radius, pi/12, 2*pi/3, n=120)
  d.points <- dilate.points(polygon.the, q)
  d.edges <- dilate.edges(polygon.the, q)
  display(polygon.the, d.points, d.edges, main="-30 to 75 degrees")

  q <- sector(radius, pi/3, 4*pi/3, n=180)
  d.points <- dilate.points(polygon.the, q)
  d.edges <- dilate.edges(polygon.the, q)
  display(polygon.the, d.points, d.edges, main="-150 to 30 degrees")

  q <- sector(radius, -9/20*pi, -9/20*pi)
  d.points <- dilate.points(polygon.the, q)
  d.edges <- dilate.edges(polygon.the, q)
  display(polygon.the, d.points, d.edges, main="171 degrees")
})

Bu örnek (N = 60 ve M = 121 (sol), M = 181 (orta) ve M = 2 (sağ)) için hesaplama süresi dörtte bir saniyeydi. Ancak, bunların çoğu ekran içindi. Tipik olarak, bu Rkod yaklaşık N M = 1,5 milyon / saniye işlemektedir (gösterilen tüm örnek hesaplamaları yapmak için sadece 0,002 saniye sürer). Bununla birlikte, MN ürününün görünümü, ayrıntılı bir mahalle aracılığıyla birçok figürün veya karmaşık figürlerin genişlemesini önemli ölçüde zaman alabilir, bu nedenle dikkat edin! Büyük bir sorunla başa çıkmadan önce daha küçük sorunların zamanlamasını kıyaslayın. Bu gibi durumlarda, raster tabanlı bir çözüme bakılabilir ( uygulanması çok daha kolaydır, esasen tek bir mahalle hesaplaması gerektirir).


Vay canına, bu çok detaylı ve büyüleyici. Daha azını beklemiyordum.
Paul

1

Bu oldukça geniş, ancak şunları yapabilirsiniz:

  1. orijinal çokgeni arabelleğe al
  2. çokgen sınırında oluşturulacak "yönlendirilmiş" ışınların başlangıç ​​noktasını bulun (bir çeşit teğet noktası mı?)
  3. soruna yapılan yorumlarda tartışılan açıyı kullanarak bu noktadan arabellek ötesine bir mesafeye bir çizgi oluşturun / uzatın.
  4. bu çizgiyle arabellek ve orijinal çokgen arasında kesişir. Bu muhtemelen 3) ile aynı anda yapılabilir.
  5. ortaya çıkan çokgen kümesinden yeni "yönlendirilmiş tampon" çokgeni ayıklayın

OP'nin , bir dairenin bir kesimi tarafından her şeklin morfolojik genişlemesi anlamında "yönlendirilmiş bir tampon" anlamına geldiğine inanıyorum . (Bu açıklama hemen bir raster çözümü verir; ancak şekil dosyaları vektör biçiminde olduğundan, bir vektör çözeltisi istenir. Bunu yapmak zor.)
whuber

Umarım OP bu noktayı açıklığa kavuşturacaktır. Her zaman en güvenli olmayan grafiğe dayanan düşünce çizgime girdim. Her halükarda, bir hesaplanmış konuma göre düzensiz bir hücre şekli yerleştirebilse de (gridedit'te yaptım ... yaşlı hissediyorum!), Bir vektör çözümünün daha temiz / kaldıraç Arc'ın vektör fonksiyonlarını daha iyi olacağını düşünürdüm . Veri yöntemine bakılmaksızın genel yöntem muhtemelen benzerdir. Raster tarafında kullanıcı için belki biraz daha kodlama.
Roland

Aslında, tarama tarafında kodlamaya gerek yoktur :-). Uygun şekilde tanımlanmış bir mahalleye sahip odak istatistikleri dahil olmak üzere çeşitli şekillerde yapılabilir. Burada bir vektör çözümünün tercih edilebilir olduğunu kabul ediyorum: daha temiz ve daha kesin. Son derece büyük veya karmaşık veri kümeleri için, bir raster çözümü hızlı olurken, her zaman her iki şekilde de nasıl yapılacağını bilmeye değer.
whuber

Odak noktaları hakkında düşünmek çok kötü , ama OP'nin şeklinin + açısının tek bir mahallede birleştirilmesinin zor olup olmayacağından emin değildi .
Roland

Sadece sektör mahalle tarafından tanımlanmalıdır, ki bu kolay bir iştir .
whuber
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.