Jak utworzyć zorientowany bufor za pomocą arcpy?

9

Chciałbym utworzyć bufor zorientowany dla każdego wielokąta w moim pliku kształtu za pomocą arcpy. Przez orientację rozumiem, że mam dwa kąty a1 i a2, które ograniczają kierunek bufora. Przedstawiono to na poniższym wykresie: wprowadź opis zdjęcia tutaj

Jakieś pomysły?

WAF
źródło
3
Potrzebne byłyby więcej informacji o kątach. Z jakiej osi mierzysz kąty? CW czy CCW? Jak zlokalizować każdy kąt na wielokącie? Z jakimi wielobokami mamy do czynienia? (Koło nie jest wielokątem.)
Paul
1
+1 do @Paul ale myślałem krąg był wielobok dopóki nie przeczytałem ten .
PolyGeo
+1 też! Użyłem koła, aby łatwo zilustrować problem. Wieloboki są wynikiem segmentacji w eCognition, po której następuje klasyfikacja w celu identyfikacji klasy. Kąty a1 i a2 pochodzą od kąta azymutu oświetlenia segmentowanego obrazu satelitarnego. W tym przykładzie kąt azymutu byłby równy 0, a1 i a2 równy 0 +/- 15 ° (arbitralnie ustalony na 15 °).
WAF,
2
@PolyGeo „Wielokąt” jest używany w GIS nieco inaczej niż w matematyce. Tutaj odnosi się do cyfrowej reprezentacji (dwuwymiarowego) regionu lub jego zamknięcia . Regiony są zwykle (ale nie zawsze) reprezentowane przez aproksymacje wielokątne , ale - ponieważ wiemy, że nasze reprezentacje komputerowe są jedynie aproksymacjami - porzucamy „aproksymację” i po prostu używamy „wielokąta”.
whuber

Odpowiedzi:

20

Podsumowanie

Ta odpowiedź umieszcza pytanie w szerszym kontekście, opisuje efektywny algorytm mający zastosowanie do reprezentacji plików kształtów funkcji (jako „wektorów” lub „linii” punktów), pokazuje niektóre przykłady jego zastosowania i podaje kod roboczy do używania lub przenoszenia do środowisko GIS.

tło

To jest przykład rozszerzenia morfologicznego. Zasadniczo dylatacja „rozciąga” punkty regionu na ich sąsiedztwa; zbiór punktów, w których kończą, to „dylatacja”. Aplikacje w GIS są liczne: modelowanie rozprzestrzeniania się ognia, przemieszczania się cywilizacji, rozprzestrzeniania się roślin i wiele innych.

Matematycznie iw bardzo wielkiej (ale użytecznej) ogólności dylatacja rozkłada zbiór punktów w rozmaitości Riemanniana (np. Płaszczyzna, kula lub elipsoida). Rozłożenie jest określone przez podzbiór wiązki stycznej w tych punktach. Oznacza to, że w każdym z punktów podany jest zestaw wektorów (kierunków i odległości) (nazywam to „sąsiedztwem”); każdy z tych wektorów opisuje ścieżkę geodezyjną rozpoczynającą się w punkcie bazowym. Punkt bazowy jest „rozciągnięty” na końce wszystkich tych ścieżek. (Aby uzyskać znacznie bardziej ograniczoną definicję „dylatacji”, która jest tradycyjnie stosowana w przetwarzaniu obrazu, zobacz artykuł w Wikipedii . Funkcja rozprzestrzeniania jest znana jako mapa wykładnicza w geometrii różnicowej).

„Buforowanie” cechy jest jednym z najprostszych przykładów takiej dylatacji: dysk o stałym promieniu (promień bufora) jest tworzony (przynajmniej koncepcyjnie) wokół każdego punktu cechy. Połączenie tych dysków jest buforem.

To pytanie wymaga obliczenia nieco bardziej skomplikowanej dylatacji, w której rozprzestrzenianie jest dozwolone tylko w określonym zakresie kątów (to znaczy w sektorze kołowym). Ma to sens tylko w przypadku elementów, które nie obejmują wyraźnie zakrzywionej powierzchni (takich jak małe elementy na kuli lub elipsoidzie lub jakiekolwiek elementy w płaszczyźnie). Kiedy pracujemy w płaszczyźnie, sensowne jest również zorientowanie wszystkich sektorów w tym samym kierunku. (Gdybyśmy jednak modelowali rozprzestrzenianie się ognia przez wiatr, chcielibyśmy, aby sektory były zorientowane na wiatr, a ich rozmiary również mogą się różnić w zależności od prędkości wiatru: to jedna z motywacji do ogólnej definicji dylatacji, którą podałem. ) (Na zakrzywionych powierzchniach, takich jak elipsoida, ogólnie niemożliwe jest zorientowanie wszystkich sektorów w „tym samym” kierunku).

W następujących okolicznościach dylatacja jest stosunkowo łatwa do obliczenia:

  • Obiekt znajduje się w płaszczyźnie (czyli rozszerzamy mapę obiektu i mam nadzieję, że mapa jest dość dokładna).

  • Dylatacja będzie stała : rozprzestrzenianie się w każdym punkcie cechy nastąpi w przystających dzielnicach o tej samej orientacji.

  • To wspólne sąsiedztwo jest wypukłe. Wypukłość znacznie upraszcza i przyspiesza obliczenia.

To pytanie mieści się w takich wyspecjalizowanych okolicznościach: prosi o dylatację dowolnych wielokątów przez sektory kołowe, których początki (środki dysków, z których pochodzą) znajdują się w punktach bazowych. Pod warunkiem, że sektory te nie przekraczają 180 stopni, będą wypukłe. (Większe sektory można zawsze podzielić na pół na dwa wypukłe sektory; połączenie dwóch mniejszych rozszerzeń da pożądany rezultat.)


Realizacja

Ponieważ wykonujemy euklidesowe obliczeń - robi rozprzestrzenia się w samolocie - możemy rozszerzać punkt jedynie poprzez tłumaczenia sąsiedztwo dylatacje do tego punktu. (Aby to zrobić, sąsiedztwo potrzebuje pochodzeniaktóry będzie odpowiadał punktowi bazowemu. Na przykład źródłem sektorów w tym pytaniu jest środek koła, z którego zostały utworzone. To pochodzenie dzieje się na granicy sektora. W standardowej operacji buforowania GIS sąsiedztwo jest kołem, którego początkiem jest środek; teraz pochodzenie leży we wnętrzu koła. Wybór źródła nie jest wielką sprawą obliczeniową, ponieważ zmiana pochodzenia jedynie przesuwa całą dylatację, ale może to być duża sprawa w zakresie modelowania zjawisk naturalnych. sectorFunkcja w kodzie poniżej ilustruje, w jaki sposób można określić pochodzenie).

Dylatacja odcinka linii może być trudna, ale dla wypukłego sąsiedztwa możemy stworzyć dylatację jako połączenie dylatacji dwóch punktów końcowych wraz ze starannie wybranym równoległobokiem. (W interesie przestrzeni kosmicznej nie zatrzymam się, aby udowodnić takie twierdzenia matematyczne, ale zachęcę czytelników do wypróbowania własnych dowodów, ponieważ jest to wnikliwe ćwiczenie.) Oto ilustracja wykorzystująca trzy sektory (pokazane na różowo). Mają promienie jednostkowe, a ich kąty podano w tytułach. Sam segment linii ma długość 2, jest poziomy i jest pokazany na czarno:

Rozszerzenia segmentów

Równolegogramy można znaleźć, lokalizując różowe punkty, które są jak najdalej od segmentu tylko w kierunku pionowym . Daje to dwa dolne punkty i dwa górne punkty wzdłuż linii równoległych do segmentu. Musimy tylko połączyć cztery punkty w równoległobok (pokazany na niebiesko). Zwróć uwagę, po prawej, jak to ma sens, nawet jeśli sam sektor jest tylko segmentem liniowym (a nie prawdziwym wielokątem): tam każdy punkt na tym segmencie został przetłumaczony w kierunku 171 stopni na wschód od północy na odległość w zakresie od 0 do 1. Zestawem tych punktów końcowych jest pokazany równoległobok. Szczegóły tego obliczenia pojawiają się w bufferfunkcji zdefiniowanej dilate.edgesw poniższym kodzie.

Aby rozszerzyć polilinię , tworzymy połączenie rozszerzeń punktów i segmentów, które ją tworzą. Dwie ostatnie linie dilate.edgeswykonują tę pętlę.

Dylatacja wielokąta wymaga uwzględnienia jego wnętrza wraz z rozszerzeniem jego granicy. (To twierdzenie przyjmuje pewne założenia dotyczące sąsiedztwa dylatacji. Jednym z nich jest to, że wszystkie sąsiedztwa zawierają punkt (0,0), co gwarantuje, że wielokąt jest objęty dylatacją. W przypadku dzielnic zmiennych zakłada również, że dylatacja dowolnego wnętrza punkt wielokąta nie będzie rozciągał się poza rozszerzeniem punktów granicznych. Dotyczy to stałych sąsiedztw.)

Spójrzmy na kilka przykładów tego, jak to działa, najpierw z nonagonem (wybranym w celu ujawnienia szczegółów), a następnie z kółkiem (wybranym w celu dopasowania do ilustracji w pytaniu). W przykładach nadal będą używane te same trzy dzielnice, ale zmniejszone do promienia 1/3.

Rozszerzenia nonagonu

Na tej figurze wnętrze wielokąta jest szare, rozszerzenia punktowe (sektory) są różowe, a rozszerzenia krawędzi (równoległoboki) są niebieskie.

Rozszerzenia koła

„Koło” to tak naprawdę zaledwie 60-gram, ale ładnie zbliża się do koła.


Wydajność

Gdy element podstawowy jest reprezentowany przez N punktów, a sąsiedztwo dylatacji przez M punktów, algorytm ten wymaga wysiłku O (N M) . Następnie należy uprościć bałagan wierzchołków i krawędzi w złączu, co może wymagać wysiłku O (N M log (N M)): o to należy poprosić GIS; nie powinniśmy tego programować.

Wysiłek obliczeniowy można poprawić do O (M + N) dla wypukłych elementów bazowych (ponieważ można dowiedzieć się, jak poruszać się po nowej granicy, odpowiednio łącząc listy wierzchołków opisujących granice dwóch oryginalnych kształtów). Nie wymagałoby to również późniejszego czyszczenia.

Gdy sąsiedztwo dylatacji powoli zmienia rozmiar i / lub orientację w miarę przesuwania się wokół elementu podstawowego, rozszerzenie dylatacji krawędzi można ściśle przybliżyć od wypukłego kadłuba połączenia dylatacji jego punktów końcowych. Jeśli dwa sąsiedztwa dylatacyjne mają punkty M1 i M2, można to znaleźć przy wysiłku O (M1 + M2) przy użyciu algorytmu opisanego w Shamos i Preparata, Geometria obliczeniowa . Dlatego pozwalając K = M1 + M2 + ... + M (N) być całkowitą liczbą wierzchołków w sąsiedztwie dylatacji N, możemy obliczyć dylatację w czasie O (K * log (K)).

Dlaczego mielibyśmy zmierzyć się z takim uogólnieniem, jeśli wszystko, czego chcemy, to prosty bufor? W przypadku dużych obiektów na ziemi sąsiedztwo dylatacji (takie jak dysk), które w rzeczywistości ma stały rozmiar, może mieć różną wielkość na mapie, na której wykonywane są te obliczenia. W ten sposób uzyskujemy sposób wykonywania obliczeń, które są dokładne dla elipsoidy, przy jednoczesnym korzystaniu ze wszystkich zalet geometrii euklidesowej.


Kod

Przykłady zostały opracowane przy użyciu tego Rprototypu, który można łatwo przenieść na ulubiony język (Python, C ++ itp.). Pod względem struktury przypomina analizę opisaną w tej odpowiedzi i dlatego nie wymaga osobnego wyjaśnienia. Komentarze wyjaśniają niektóre szczegóły.

(Ciekawe może być to, że obliczenia trygonometryczne są używane tylko do tworzenia przykładowych cech - które są regularnymi wielokątami - i sektorów. Żadna część obliczeń dylatacyjnych nie wymaga żadnej trygonometrii).

#
# 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")
})

Czas obliczeniowy dla tego przykładu (z ostatniej cyfry), przy N = 60 i M = 121 (po lewej), M = 181 (po środku) i M = 2 (po prawej), wynosił kwadrans. Jednak większość z nich dotyczyła wyświetlacza. Zazwyczaj ten Rkod będzie obsługiwał około N M = 1,5 miliona na sekundę (zajmuje to tylko 0,002 sekundy, aby wykonać wszystkie przedstawione przykładowe obliczenia). Niemniej jednak pojawienie się produktu M N oznacza rozszerzenie wielu postaci lub skomplikowanych postaci w szczegółowym sąsiedztwie, może zająć sporo czasu, więc uważaj! Porównać czas dla mniejszych problemów przed rozwiązaniem dużego. W takich okolicznościach można spojrzeć na rozwiązanie oparte na rastrze (które jest znacznie łatwiejsze do wdrożenia, wymagając zasadniczo tylko jednego obliczenia sąsiedztwa).

Whuber
źródło
Wow, to jest bardzo szczegółowe i fascynujące. Nie spodziewałem się mniej.
Paul
1

Jest to dość szerokie, ale możesz:

  1. buforuj oryginalny wielokąt
  2. znaleźć punkt początkowy „zorientowanych” promieni, które mają zostać utworzone na granicy wielokąta (jakiś punkt styczności?)
  3. utwórz / przedłuż linię od tego punktu do odległości poza buforem, używając kąta omówionego w komentarzach do pytania.
  4. przecinają tę linię z buforem i oryginalnym wielokątem. Można to prawdopodobnie zrobić w tym samym czasie co 3) z odpowiednimi argumentami do rozszerzenia.
  5. wyodrębnij nowy wielokąt „zorientowany bufor” z wynikowego zestawu wielokątów
Roland
źródło
Uważam, że PO oznacza „zorientowany bufor” w sensie morfologicznej dylatacji każdego kształtu przez sektor koła. (Ten opis natychmiast daje rozwiązanie rastrowe; ale ponieważ pliki kształtów są w formacie wektorowym, pożądane byłoby rozwiązanie wektorowe. Trudno to zrobić.)
whuber
Mam nadzieję, że PO wyjaśni ten punkt. Zacząłem myśleć w oparciu o grafikę, co nie zawsze jest najbezpieczniejsze. W każdym razie, chociaż można umieścić nieregularny kształt komórek w stosunku do obliczonej lokalizacji (zrobiłem to w gridedit ... Czuję się stary!), Pomyślałem, że rozwiązanie wektorowe byłoby czystsze / lepiej wykorzystywałby funkcje wektorowe Arc . Ogólna metoda jest prawdopodobnie analogiczna, niezależnie od modelu danych. Może nieco więcej kodu dla użytkownika po stronie rastrowej.
Roland
W rzeczywistości nie ma potrzeby kodowania po stronie rastrowej :-). Można to zrobić na kilka sposobów, w tym statystyki ogniskowej z odpowiednio zdefiniowanym sąsiedztwem. Zgadzam się, że preferowane jest tutaj rozwiązanie wektorowe: czystsze i bardziej precyzyjne. W przypadku bardzo dużych lub złożonych zestawów danych może to jednak zapaść się, podczas gdy rozwiązanie rastrowe będzie szybkie, dlatego zawsze warto wiedzieć, jak to zrobić w obie strony.
whuber
Nie marnuj czasu na ogniskowanie , ale nie byłem pewien, czy kształt + kąt OP będzie trudny do połączenia w jedno sąsiedztwo .
Roland
Okolica musi opisać tylko sektor, co jest łatwe do zrobienia .
whuber