arcpy를 사용하여 shapefile의 모든 다각형에 대해 지향적 버퍼를 만들고 싶습니다. 방향을 말하면 버퍼의 방향을 제한하는 두 개의 각도 a1과 a2가 있습니다. 이것은 아래 그래프에 표시됩니다.
어떤 아이디어?
arcpy를 사용하여 shapefile의 모든 다각형에 대해 지향적 버퍼를 만들고 싶습니다. 방향을 말하면 버퍼의 방향을 제한하는 두 개의 각도 a1과 a2가 있습니다. 이것은 아래 그래프에 표시됩니다.
어떤 아이디어?
답변:
이 답변은 질문을 더 큰 맥락에 배치하고, 특징의 쉐이프 파일 표현 ( "벡터"또는 "라인 스트링"으로 표시)에 적용 할 수있는 효율적인 알고리즘을 설명하고 해당 응용 프로그램의 몇 가지 예를 보여 주며 사용 또는 이식을위한 작업 코드를 제공합니다. GIS 환경.
이것은 형태 확장 의 예입니다 . 일반적으로, 팽창은 한 지역의 지점을 그들의 이웃으로 퍼지게합니다. 그들이 바람을 points 지점의 수집은 "팽창"입니다. GIS의 적용은 불의 확산, 문명의 이동, 식물의 확산 등을 모델링하는 것입니다.
수학적으로 매우 유용한 (하지만 유용한) 일반성에서 팽창은 리만 ( Riemannian) 매니 폴드 (예 : 평면, 구 또는 타원체) 에 점 집합을 분산시킵니다 . 이 지점에서 접선 다발 의 부분 집합에 의해 확산이 규정된다 . 이것은 각 점마다 벡터 세트 (방향과 거리)가 주어진다는 것을 의미합니다 (이것을 "이웃"이라고 부릅니다). 이러한 각 벡터는 기준점에서 시작 하는 측지 경로를 나타 냅니다. 기준점은 이러한 모든 경로의 끝에 "확산"됩니다. (이미지 처리에 일반적으로 사용되는 "확장"에 대한 훨씬 제한적인 정의는 Wikipedia 기사를 참조하십시오 . 확산 기능은 지수 맵이라고합니다. 차동 지오메트리에서.)
피처의 "버퍼링" 은 이러한 확장의 가장 간단한 예 중 하나입니다. 일정한 반경의 디스크 (버퍼 반경)는 피처의 각 지점 주위에 (적어도 개념적으로) 생성됩니다. 이 디스크들의 결합은 버퍼입니다.
이 질문은 주어진 각도 범위 내에서만 (즉, 원형 섹터 내에서) 확산이 발생할 수있는 약간 더 복잡한 확장의 계산을 요구합니다. 이는 구부러진 곡면 을 둘러싸 지 않는 형상 (구 또는 타원체의 작은 형상 또는 평면의 모든 형상) 에만 적합합니다 . 우리가 비행기에서 일할 때 모든 섹터를 같은 방향으로 향하게하는 것도 의미가 있습니다. (우리가 바람에 의한 화재 확산을 모델링하는 경우, 우리는 섹터가 바람에 맞춰 지길 원하며, 크기도 바람 속도에 따라 다를 수 있습니다. 이것이 제가 제공 한 팽창의 일반적인 정의에 대한 동기입니다. ) (타원체와 같은 곡면에서는 일반적으로 모든 섹터를 "같은"방향으로 향하게하는 것은 불가능합니다.)
다음과 같은 상황에서는 팽창을 계산하기가 비교적 쉽습니다.
이 기능은 (우리가 확장시키고있다,라는면에 지도 기능의를 희망지도가 매우 정확하다).
확장은 일정합니다 . 피처의 모든 지점에서 확산은 동일한 방향의 합동적인 이웃 내에서 발생합니다.
이 공통된 이웃은 볼록합니다. 볼록 함은 계산을 크게 단순화하고 속도를 높입니다.
이 질문은 그러한 특수한 상황에 적합합니다. 원점 (원래 디스크의 중심)이 기준점에있는 원형 섹터로 임의의 다각형을 확장하도록 요청합니다. 해당 섹터가 180도를 넘지 않으면 볼록합니다. (더 큰 섹터는 항상 두 개의 볼록 섹터로 반으로 나눌 수 있습니다. 두 개의 작은 확장의 결합으로 원하는 결과를 얻을 수 있습니다.)
평면에서 확산을 수행하는 유클리드 계산을 수행하기 때문에 확장 근방을 해당 점 으로 변환 하여 점을 확장 할 수 있습니다 . (이 작업을 수행하려면 이웃에 원점이 필요합니다.이는 기준점에 해당합니다. 예를 들어,이 질문에서 섹터의 원점은 이들이 형성되는 원의 중심입니다. 이 기원은 부문의 경계에 있습니다. 표준 GIS 버퍼링 작업에서 이웃은 중심에 원점이있는 원입니다. 이제 원점은 원의 내부에 있습니다. 원점을 변경하면 계산이 크게 달라지지는 않습니다. 원점을 변경하면 전체 팽창이 바뀌기 만하지만 자연 현상을 모델링하는 데에는 큰 영향을 줄 수 있습니다. sector
아래 코드 의 함수는 원점을 지정하는 방법을 보여줍니다.)
선분 을 확장하는 것은 까다로울 수 있지만 볼록한 이웃의 경우 신중하게 선택한 평행 사변형과 함께 두 끝점의 팽창 합집합으로 팽창을 만들 수 있습니다. (공간의 이익을 위해 나는 이와 같은 수학적 주장을 증명하기 위해 잠시 멈추지 않을 것이지만, 통찰력있는 운동이기 때문에 독자들이 자신의 증명을 시도하도록 권장한다.) 여기에 세 개의 섹터 (분홍색으로 표시)를 사용한 그림이있다. 그들은 단위 반지름을 가지고 있으며 그들의 각도는 제목에 나와 있습니다. 선 세그먼트 자체의 길이는 2이며 가로이며 검은 색으로 표시됩니다.
평행 사변형은 세그먼트 에서 가능한 한 멀리 떨어진 분홍색 점을 세로 방향으로 만 찾아서 찾을 수 있습니다 . 세그먼트에 평행 한 선을 따라 두 개의 하위 지점과 두 개의 상위 지점이 제공됩니다. 4 개의 점을 평행 사변형 (파란색으로 표시)으로 결합하면됩니다. 오른쪽에서, 섹터 자체가 선 세그먼트 (진정한 다각형이 아님) 인 경우에도 이것이 어떻게 의미가 있는지 확인하십시오. 세그먼트의 모든 점은 북쪽에서 동쪽으로 171도 방향으로 이동하여 거리 범위에 해당합니다. 이 끝점 세트는 표시된 평행 사변형입니다. 이 계산의 세부 사항은 아래 코드에 buffer
정의 된 함수에 나타납니다 dilate.edges
.
폴리 라인을 확장하기 위해 점과 선분을 이루는 팽창의 합집합을 형성합니다. 마지막 두 줄은 dilate.edges
이 루프 를 수행합니다.
다각형을 확장하려면 다각형의 내부와 경계의 확장을 포함해야합니다. (이 주장은 팽창 근방에 대한 몇 가지 가정을합니다. 하나는 모든 근방에 점 (0,0)이 포함되어 있기 때문에 다각형이 팽창 내에 포함되도록 보장합니다. 가변 근방의 경우에는 내부의 팽창도 가정합니다 다각형의 점은 경계 점의 확장을 넘어 확장되지 않습니다 (이는 일정한 이웃의 경우입니다).
이것이 어떻게 작동하는지에 대한 몇 가지 예 를 살펴 보겠습니다 . 먼저 비논리 (세부 정보를 공개하도록 선택)와 원 (문제의 그림과 일치하도록 선택)을 사용하십시오. 예제는 계속해서 동일한 세 이웃을 사용하지만 반경 1/3로 줄었습니다.
이 그림에서 다각형의 내부는 회색이고 점 확장 (섹터)은 분홍색이며 가장자리 확장 (평행도)은 파란색입니다.
"원"은 실제로 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의 출현은 세부적인 이웃을 통한 많은 인물 또는 복잡한 인물의 팽창이 상당한 시간이 걸릴 수 있음을 의미합니다! 큰 문제를 해결하기 전에 작은 문제에 대한 타이밍을 벤치마킹하십시오. 이러한 상황에서 래스터 기반 솔루션을 찾을 수 있습니다 ( 구현하기 가 훨씬 쉬우 며 기본적으로 단일 이웃 계산 만 필요).
이것은 꽤 광범위하지만 당신은 할 수 있습니다 :