arcpy를 사용하여 방향 버퍼를 만드는 방법은 무엇입니까?


9

arcpy를 사용하여 shapefile의 모든 다각형에 대해 지향적 버퍼를 만들고 싶습니다. 방향을 말하면 버퍼의 방향을 제한하는 두 개의 각도 a1과 a2가 있습니다. 이것은 아래 그래프에 표시됩니다. 여기에 이미지 설명을 입력하십시오

어떤 아이디어?


3
각도에 대한 자세한 정보가 필요합니다. 각도를 측정하는 축은 무엇입니까? CW 또는 CCW? 다각형에서 각 각도를 어떻게 찾습니까? 우리는 어떤 종류의 다각형을 다루고 있습니까? (원은 다각형이 아닙니다.)
Paul

1
@ 폴하지만 +1 내가 읽을 때까지 원이 다각형라고 생각 .
PolyGeo

+1도! 문제를 쉽게 설명하기 위해 동그라미를 사용했습니다. 다각형은 eCognition에서 분류 후 클래스를 식별하기위한 분류의 결과입니다. 각도 a1 및 a2는 세그먼트 화 된 위성 이미지의 조명 방위각으로부터 도출된다. 이 예에서 방위각은 0, a1 및 a2는 0 +/- 15 °와 동일합니다 (임의로 15 °로 고정).
WAF

2
@PolyGeo "Polygon"은 GIS에서 수학과 약간 다르게 사용됩니다. 여기서는 (2 차원) 영역 또는 해당 폐쇄디지털 표현나타냅니다 . 영역은 일반적으로 다각형 근사치로 표현되지만 항상 그런 것은 아니지만 컴퓨터 표현이 근사치라는 것을 알고 있으므로 "근사치"를 삭제하고 "다각형"만 사용합니다.
whuber

답변:


20

요약

이 답변은 질문을 더 큰 맥락에 배치하고, 특징의 쉐이프 파일 표현 ( "벡터"또는 "라인 스트링"으로 표시)에 적용 할 수있는 효율적인 알고리즘을 설명하고 해당 응용 프로그램의 몇 가지 예를 보여 주며 사용 또는 이식을위한 작업 코드를 제공합니다. GIS 환경.

배경

이것은 형태 확장 의 예입니다 . 일반적으로, 팽창은 한 지역의 지점을 그들의 이웃으로 퍼지게합니다. 그들이 바람을 points 지점의 수집은 "팽창"입니다. GIS의 적용은 불의 확산, 문명의 이동, 식물의 확산 등을 모델링하는 것입니다.

수학적으로 매우 유용한 (하지만 유용한) 일반성에서 팽창은 리만 ( Riemannian) 매니 폴드 (예 : 평면, 구 또는 타원체) 에 점 집합을 분산시킵니다 . 이 지점에서 접선 다발 의 부분 집합에 의해 확산이 규정된다 . 이것은 각 점마다 벡터 세트 (방향과 거리)가 주어진다는 것을 의미합니다 (이것을 "이웃"이라고 부릅니다). 이러한 각 벡터는 기준점에서 시작 하는 측지 경로를 나타 냅니다. 기준점은 이러한 모든 경로의 끝에 "확산"됩니다. (이미지 처리에 일반적으로 사용되는 "확장"에 대한 훨씬 제한적인 정의는 Wikipedia 기사를 참조하십시오 . 확산 기능은 지수 맵이라고합니다. 차동 지오메트리에서.)

피처의 "버퍼링" 은 이러한 확장의 가장 간단한 예 중 하나입니다. 일정한 반경의 디스크 (버퍼 반경)는 피처의 각 지점 주위에 (적어도 개념적으로) 생성됩니다. 이 디스크들의 결합은 버퍼입니다.

이 질문은 주어진 각도 범위 내에서만 (즉, 원형 섹터 내에서) 확산이 발생할 수있는 약간 더 복잡한 확장의 계산을 요구합니다. 이는 구부러진 곡면둘러싸 지 않는 형상 (구 또는 타원체의 작은 형상 또는 평면의 모든 형상) 에만 적합합니다 . 우리가 비행기에서 일할 때 모든 섹터를 같은 방향으로 향하게하는 것도 의미가 있습니다. (우리가 바람에 의한 화재 확산을 모델링하는 경우, 우리는 섹터가 바람에 맞춰 지길 원하며, 크기도 바람 속도에 따라 다를 수 있습니다. 이것이 제가 제공 한 팽창의 일반적인 정의에 대한 동기입니다. ) (타원체와 같은 곡면에서는 일반적으로 모든 섹터를 "같은"방향으로 향하게하는 것은 불가능합니다.)

다음과 같은 상황에서는 팽창을 계산하기가 비교적 쉽습니다.

  • 이 기능은 (우리가 확장시키고있다,라는면에 지도 기능의를 희망지도가 매우 정확하다).

  • 확장은 일정합니다 . 피처의 모든 지점에서 확산은 동일한 방향의 합동적인 이웃 내에서 발생합니다.

  • 이 공통된 이웃은 볼록합니다. 볼록 함은 계산을 크게 단순화하고 속도를 높입니다.

이 질문은 그러한 특수한 상황에 적합합니다. 원점 (원래 디스크의 중심)이 기준점에있는 원형 섹터로 임의의 다각형을 확장하도록 요청합니다. 해당 섹터가 180도를 넘지 않으면 볼록합니다. (더 큰 섹터는 항상 두 개의 볼록 섹터로 반으로 나눌 수 있습니다. 두 개의 작은 확장의 결합으로 원하는 결과를 얻을 수 있습니다.)


이행

평면에서 확산을 수행하는 유클리드 계산을 수행하기 때문에 확장 근방을 해당 점 으로 변환 하여 점을 확장 할 수 있습니다 . (이 작업을 수행하려면 이웃에 원점이 필요합니다.이는 기준점에 해당합니다. 예를 들어,이 질문에서 섹터의 원점은 이들이 형성되는 원의 중심입니다. 이 기원은 부문의 경계에 있습니다. 표준 GIS 버퍼링 작업에서 이웃은 중심에 원점이있는 원입니다. 이제 원점은 원의 내부에 있습니다. 원점을 변경하면 계산이 크게 달라지지는 않습니다. 원점을 변경하면 전체 팽창이 바뀌기 만하지만 자연 현상을 모델링하는 데에는 큰 영향을 줄 수 있습니다. sector아래 코드 의 함수는 원점을 지정하는 방법을 보여줍니다.)

선분 을 확장하는 것은 까다로울 수 있지만 볼록한 이웃의 경우 신중하게 선택한 평행 사변형과 함께 두 끝점의 팽창 합집합으로 팽창을 만들 수 있습니다. (공간의 이익을 위해 나는 이와 같은 수학적 주장을 증명하기 위해 잠시 멈추지 않을 것이지만, 통찰력있는 운동이기 때문에 독자들이 자신의 증명을 시도하도록 권장한다.) 여기에 세 개의 섹터 (분홍색으로 표시)를 사용한 그림이있다. 그들은 단위 반지름을 가지고 있으며 그들의 각도는 제목에 나와 있습니다. 선 세그먼트 자체의 길이는 2이며 가로이며 검은 색으로 표시됩니다.

세그먼트 확장

평행 사변형은 세그먼트 에서 가능한 한 멀리 떨어진 분홍색 점을 세로 방향으로 만 찾아서 찾을 수 있습니다 . 세그먼트에 평행 한 선을 따라 두 개의 하위 지점과 두 개의 상위 지점이 제공됩니다. 4 개의 점을 평행 사변형 (파란색으로 표시)으로 결합하면됩니다. 오른쪽에서, 섹터 자체가 선 세그먼트 (진정한 다각형이 아님) 인 경우에도 이것이 어떻게 의미가 있는지 확인하십시오. 세그먼트의 모든 점은 북쪽에서 동쪽으로 171도 방향으로 이동하여 거리 범위에 해당합니다. 이 끝점 세트는 표시된 평행 사변형입니다. 이 계산의 세부 사항은 아래 코드에 buffer정의 된 함수에 나타납니다 dilate.edges.

폴리 라인을 확장하기 위해 점과 선분을 이루는 팽창의 합집합을 형성합니다. 마지막 두 줄은 dilate.edges이 루프 를 수행합니다.

다각형을 확장하려면 다각형의 내부와 경계의 확장을 포함해야합니다. (이 주장은 팽창 근방에 대한 몇 가지 가정을합니다. 하나는 모든 근방에 점 (0,0)이 포함되어 있기 때문에 다각형이 팽창 내에 포함되도록 보장합니다. 가변 근방의 경우에는 내부의 팽창도 가정합니다 다각형의 점은 경계 점의 확장을 넘어 확장되지 않습니다 (이는 일정한 이웃의 경우입니다).

이것이 어떻게 작동하는지에 대한 몇 가지 예살펴 보겠습니다 . 먼저 비논리 (세부 정보를 공개하도록 선택)와 원 (문제의 그림과 일치하도록 선택)을 사용하십시오. 예제는 계속해서 동일한 세 이웃을 사용하지만 반경 1/3로 줄었습니다.

nonagon의 팽창

이 그림에서 다각형의 내부는 회색이고 점 확장 (섹터)은 분홍색이며 가장자리 확장 (평행도)은 파란색입니다.

원의 팽창

"원"은 실제로 60-gon이지만 원에 가깝습니다.


공연

기본 피처가 N 점으로 표시되고 팽창 이웃은 M 점으로 표시되면이 알고리즘에는 O (N M) 노력 이 필요합니다 . O (N M log (N M)) 노력이 필요할 수있는 노조의 꼭지점과 모서리의 혼란을 단순화하여 후속 조치 를 취해야합니다. 이는 GIS에 요청하는 것입니다. 우리는 그것을 프로그래밍 할 필요가 없습니다.

볼록한 베이스 피처에 대해 계산 노력을 O (M + N)로 개선 할 수 있습니다 (원래 두 모양의 경계를 설명하는 정점 목록을 적절히 병합하여 새 경계를 이동하는 방법을 알아낼 수 있기 때문에). 후속 청소도 필요하지 않습니다.

베이스 피처를 중심으로 진행 하면서 팽창 인접 영역의 크기 및 / 또는 방향이 서서히 변할 때, 끝점의 팽창 조합의 볼록 껍질로부터 가장자리의 팽창을 근사화 할 수 있습니다. 두 확장 영역에 M1 및 M2 점이있는 경우 이는 Shamos & Preparata, Computational Geometry에 설명 된 알고리즘을 사용하여 O (M1 + M2) 노력으로 찾을 수 있습니다 . 따라서 K = M1 + M2 + ... + M (N)을 N 팽창 근방의 정점 수로두면 O (K * log (K)) 시간의 팽창을 계산할 수 있습니다.

원하는 것이 단순한 버퍼라면 왜 그러한 일반화를 다루고 싶습니까? 지구상의 대형 지형지 물의 경우 실제로 일정한 크기를 갖는 팽창 이웃 (예 : 디스크)은 이러한 계산이 수행되는지도에서 크기가 다를 수 있습니다. 따라서 우리 는 타원체에 대해 정확한 계산을 하면서 유클리드 기하학의 모든 장점을 계속 누릴 수있는 방법을 얻습니다 .


암호

예제는이 R프로토 타입 으로 제작 되었으며 원하는 언어 (Python, C ++ 등)로 쉽게 이식 할 수 있습니다. 구조적으로이 답변에보고 된 분석과 유사하므로 별도의 설명이 필요하지 않습니다. 의견은 세부 사항 중 일부를 명확하게합니다.

삼각법 계산은 규칙적인 다각형 인 예제 피처와 섹터를 만드는 데만 사용 됩니다 . 팽창 계산의 일부에는 삼각법이 필요하지 않습니다.

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

N = 60 및 M = 121 (왼쪽), M = 181 (중간) 및 M = 2 (오른쪽) 인이 예제의 계산 시간 (마지막 그림부터)은 1/4 초입니다. 그러나 대부분이 디스플레이 용이었습니다. 일반적으로이 R코드는 초당 NM = 150 만 개를 처리합니다 (표시된 모든 예제 계산을 수행하기 위해 0.002 초 정도 소요). 그럼에도 불구하고, 제품 MN의 출현은 세부적인 이웃을 통한 많은 인물 또는 복잡한 인물의 팽창이 상당한 시간이 걸릴 수 있음을 의미합니다! 큰 문제를 해결하기 전에 작은 문제에 대한 타이밍을 벤치마킹하십시오. 이러한 상황에서 래스터 기반 솔루션을 찾을 수 있습니다 ( 구현하기 가 훨씬 쉬우 며 기본적으로 단일 이웃 계산 만 필요).


와우, 그것은 매우 상세하고 매력적입니다. 나는 더 이상 기대하지 않았다.
Paul

1

이것은 꽤 광범위하지만 당신은 할 수 있습니다 :

  1. 원래 다각형을 버퍼링
  2. 다각형 경계에 생성 될 "지향 된"광선의 원점을 찾습니다 (어떤 종류의 탄젠트 포인트?)
  3. 질문에 대한 의견에서 논의 된 각도를 사용하여 해당 지점에서 버퍼 너머까지의 거리로 라인을 생성 / 확장하십시오.
  4. 해당 라인을 버퍼 및 원래 다각형과 교차시킵니다. 이것은 아마도 확장을위한 적절한 인수로 3)과 동시에 수행 될 수 있습니다.
  5. 결과 다각형 세트에서 새로운 "지향 버퍼"다각형을 추출합니다.

나는 OP 가 원의 섹터에 의한 각 모양 의 형태 적 확장 이라는 의미에서 "지향 버퍼"를 의미한다고 생각합니다 . (이 설명은 즉시 래스터 솔루션을 제공하지만 셰이프 파일은 벡터 형식이므로 벡터 솔루션이 바람직합니다. 까다 롭습니다.)
whuber

OP가 그 점을 분명히하기를 바랍니다. 나는 항상 가장 안전한 것은 아니지만 그래픽을 바탕으로 내 생각에 들어갔다. 어쨌든 계산 된 위치를 기준으로 불규칙한 모양의 셀을 배치 할 수는 있지만 (격자 편집에서 ... 오래된 느낌입니다!) 벡터 솔루션이 더 깨끗하고 개선 될 것이라고 생각합니다 .Arc의 벡터 기능 . 일반적인 방법은 데이터 모델에 관계없이 유사합니다. 래스터 쪽에서 사용자를 위해 조금 더 코딩 할 수 있습니다.
Roland

실제로 래스터 측에는 코딩이 필요하지 않습니다 :-). 주변이 적절하게 정의 된 초점 통계를 포함하여 여러 가지 방법으로 수행 할 수 있습니다. 더 깨끗하고 정확한 벡터 솔루션이 여기에 동의합니다. 매우 크거나 복잡한 데이터 세트의 경우 래스터 솔루션이 빠르지 만 항상 두 가지 방법을 아는 것이 좋습니다.
whuber

focalstats 에 대해 엉망 이지만 OP의 모양 + 각도가 단일 이웃 으로 결합하기 어려운지 확실하지 않았습니다 .
Roland

해당 지역 만 해당 부문 만 설명하면되므로 쉽게 할 수 있습니다.
whuber
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.