동서 방향으로 호수와 같은 다각형의 최대 너비에 관심이 있습니다. 경계 상자는 단순한 다각형에만 도움이되고 복잡한 오목 다각형에는 도움이되지 않습니다.
동서 방향으로 호수와 같은 다각형의 최대 너비에 관심이 있습니다. 경계 상자는 단순한 다각형에만 도움이되고 복잡한 오목 다각형에는 도움이되지 않습니다.
답변:
이를 위해서는 모든 GIS 플랫폼에서 일부 스크립팅이 필요합니다.
가장 효율적인 방법은 (직각적으로) 수직선 스윕입니다. 최소 y 좌표로 가장자리를 정렬 한 다음 O (e * log ()에 대해 가장자리 (최소 y)에서 위쪽 (최대 y)까지 가장자리를 처리해야합니다. e) e 모서리가 관련된 알고리즘 .
절차는 간단하지만 모든 경우에 올바르게 적용하기는 놀랍습니다. 다각형은 불쾌 할 수 있습니다. 매달린 것, 은색, 구멍, 연결 끊기, 정점 복제, 직선을 따라 정점 실행, 인접한 두 구성 요소 사이의 경계가 해소 될 수 있습니다. 다음은 이러한 많은 특성을 보여주는 예입니다.
우리는 구체적으로 폴리곤 의 폐쇄 내에있는 최대 길이의 수평 세그먼트를 추구 할 것이다 . 예를 들어, 이것은 x = 10과 x = 25 사이의 구멍에서 나오는 x = 20과 x = 40 사이의 댕글 링을 제거합니다. 그 후, 최대 길이의 수평 세그먼트 중 적어도 하나가 적어도 하나의 정점과 교차한다는 것을 나타내는 것이 간단하다. (어떤 정점을 교차하지 솔루션이있는 경우, 그들은 솔루션이 상단과 하단에 경계 일부 평행 사변형의 내부에 거짓말을한다 할 적어도 하나 개의 정점을 교차. 이것은 우리가 찾을 수있는 수단을 제공 하는 모든 솔루션을.)
따라서 라인 스윕은 가장 낮은 정점으로 시작한 다음 위쪽으로 (즉, 더 높은 y 값으로) 이동하여 각 정점에서 중지해야합니다. 매 정거장마다 해당 높이에서 위쪽으로 튀어 나오는 새로운 모서리가 발견됩니다. 해당 고도에서 아래에서 끝나는 모든 모서리를 제거합니다 (이는 핵심 아이디어 중 하나입니다. 알고리즘을 단순화하고 잠재적 인 처리의 절반을 제거합니다). 그리고 일정한 높이 (수평 가장자리)에 완전히 놓여있는 가장자리를주의해서 처리하십시오.
예를 들어, y = 10 레벨에 도달 한 상태를 고려하십시오. 왼쪽에서 오른쪽으로 다음과 같은 가장자리를 찾습니다.
x.min x.max y.min y.max
[1,] 10 0 0 30
[2,] 10 24 10 20
[3,] 20 24 10 20
[4,] 20 40 10 10
[5,] 40 20 10 10
[6,] 60 0 5 30
[7,] 60 60 5 30
[8,] 60 70 5 20
[9,] 60 70 5 15
[10,] 90 100 10 40
이 표에서 (x.min, y.min)은 가장자리의 아래쪽 끝점의 좌표이고 (x.max, y.max)는 위쪽 끝점의 좌표입니다. 이 수준 (y = 10)에서 첫 번째 가장자리는 내부에서 가로 채고 두 번째 가장자리는 아래쪽에서 가로 채됩니다. (10,0)에서 (10,10)과 같이이 수준에서 끝나는 일부 모서리 는 목록에 포함 되지 않습니다.
내부 점과 외부 점의 위치를 결정하려면 다각형 바깥에있는 가장 왼쪽에서 시작하여 수평으로 오른쪽으로 이동하는 것을 상상해보십시오. 수평이 아닌 가장자리 를 교차 할 때마다 외부에서 내부 및 후면으로 교대로 전환됩니다. (이것은 또 다른 핵심 아이디어입니다.) 그러나 수평 가장자리 내의 모든 점은 다각형 내부에있는 것으로 결정됩니다. 다각형의 폐쇄에는 항상 모서리가 포함됩니다.
예제를 계속 진행하면 다음은 수평 이 아닌 모서리가 y = 10 선에서 시작하거나 교차하는 x 좌표의 정렬 된 목록입니다 .
x.array 6.7 10 20 48 60 63.3 65 90
interior 1 0 1 0 1 0 1 0
x = 40은이 목록에 없습니다. interior
배열 값 은 내부 세그먼트의 왼쪽 끝점을 표시합니다. 1은 내부 간격을, 0은 외부 간격을 나타냅니다. 따라서 처음 1은 x = 6.7에서 x = 10까지의 간격이 다각형 안에 있음을 나타냅니다. 다음 0은 x = 10에서 x = 20까지의 간격 이 다각형 외부에 있음을 나타냅니다 . 배열은 다각형 내부에서와 같이 4 개의 개별 간격을 식별합니다.
x = 60에서 x = 63.3의 간격과 같은 일부 간격은 정점과 교차하지 않습니다. y = 10 인 모든 정점의 x 좌표에 대한 빠른 검사는 이러한 간격을 제거합니다.
스캔하는 동안 지금까지 찾은 최대 길이 간격에 대한 데이터를 유지하면서 이러한 간격의 길이를 모니터링 할 수 있습니다.
이 접근 방식의 의미에 유의하십시오. "v"모양의 꼭짓점은 두 가장자리의 원점입니다. 따라서 두 개의 스위치가 교차 할 때 발생합니다. 그 스위치는 취소됩니다. 왼쪽에서 오른쪽으로 스캔하기 전에 양쪽 가장자리가 모두 제거되므로 거꾸로 된 "v"도 처리되지 않습니다. 두 경우 모두, 이러한 정점은 수평 세그먼트를 차단하지 않습니다.
두 개 이상의 모서리가 꼭지점을 공유 할 수 있습니다. 이는 (10,0), (60,5), (25, 20)에 설명되어 있지만 (20,10)과 (40)에서는 말하기 어렵지만 , 10). (매달려 있기 때문에 (20,10)-> (40,10)-> (40,0)-> (40, -50)-> (40, 10)-> (20, 10). (40,0)의 정점이 다른 가장자리의 내부에도 어떻게 나타나는지 주목하십시오.
까다로운 상황이 맨 아래에 설명되어 있습니다. 수평이 아닌 세그먼트의 x 좌표
30, 50
이것은 x = 30의 왼쪽에있는 모든 것을 외부로 간주하고, 30에서 50 사이의 모든 것을 내부로, 50 이후의 모든 것을 다시 외부로 만듭니다. x = 40의 정점은이 알고리즘에서도 고려되지 않습니다.
다음은 스캔이 끝날 때 다각형이 어떻게 보이는지입니다. 모든 정점 포함 내부 간격을 짙은 회색으로, 최대 길이 간격을 빨간색으로 표시하고, 정점의 y 좌표에 따라 색상을 지정합니다. 최대 간격은 64 단위입니다.
관련된 기하학적 계산 은 모서리가 수평선과 교차하는 위치를 계산하는 것입니다. 이는 단순한 선형 보간입니다. 어떤 정점을 포함하는 내부 세그먼트를 결정하려면 계산이 필요합니다. 이러한 정점 은 두 개의 불평등으로 쉽게 계산 되는 중간 성 결정입니다. 이 단순성은 알고리즘을 정수 및 부동 소수점 좌표 표현 모두에 강력하고 적합하게 만듭니다.
좌표가 지리적 인 경우 수평선은 실제로 위도의 원에 있습니다. 그들의 길이는 계산하기 어렵지 않습니다. 단지 유클리드 길이에 위도의 코사인 (구형 모델)을 곱하면됩니다. 따라서이 알고리즘은 지리적 좌표에 잘 맞습니다. (+ -180 자오선을 감싸는 것을 처리하려면 먼저 다각형을 통과하지 않는 남극에서 북극까지의 곡선을 찾아야합니다. 모든 x 좌표를 그에 대한 수평 변위로 다시 표현한 후 이 알고리즘은 최대 수평 세그먼트를 올바르게 찾습니다.)
다음은 R
계산을 수행하고 그림을 생성하기 위해 구현 된 코드입니다.
#
# Plotting functions.
#
points.polygon <- function(p, ...) {
points(p$v, ...)
}
plot.polygon <- function(p, ...) {
apply(p$e, 1, function(e) lines(matrix(e[c("x.min", "x.max", "y.min", "y.max")], ncol=2), ...))
}
expand <- function(bb, e=1) {
a <- matrix(c(e, 0, 0, e), ncol=2)
origin <- apply(bb, 2, mean)
delta <- origin %*% a - origin
t(apply(bb %*% a, 1, function(x) x - delta))
}
#
# Convert polygon to a better data structure.
#
# A polygon class has three attributes:
# v is an array of vertex coordinates "x" and "y" sorted by increasing y;
# e is an array of edges from (x.min, y.min) to (x.max, y.max) with y.max >= y.min, sorted by y.min;
# bb is its rectangular extent (x0,y0), (x1,y1).
#
as.polygon <- function(p) {
#
# p is a list of linestrings, each represented as a sequence of 2-vectors
# with coordinates in columns "x" and "y".
#
f <- function(p) {
g <- function(i) {
v <- p[(i-1):i, ]
v[order(v[, "y"]), ]
}
sapply(2:nrow(p), g)
}
vertices <- do.call(rbind, p)
edges <- t(do.call(cbind, lapply(p, f)))
colnames(edges) <- c("x.min", "x.max", "y.min", "y.max")
#
# Sort by y.min.
#
vertices <- vertices[order(vertices[, "y"]), ]
vertices <- vertices[!duplicated(vertices), ]
edges <- edges[order(edges[, "y.min"]), ]
# Maintaining an extent is useful.
bb <- apply(vertices <- vertices[, c("x","y")], 2, function(z) c(min(z), max(z)))
# Package the output.
l <- list(v=vertices, e=edges, bb=bb); class(l) <- "polygon"
l
}
#
# Compute the maximal horizontal interior segments of a polygon.
#
fetch.x <- function(p) {
#
# Update moves the line from the previous level to a new, higher level, changing the
# state to represent all edges originating or strictly passing through level `y`.
#
update <- function(y) {
if (y > state$level) {
state$level <<- y
#
# Remove edges below the new level from state$current.
#
current <- state$current
current <- current[current[, "y.max"] > y, ]
#
# Adjoin edges at this level.
#
i <- state$i
while (i <= nrow(p$e) && p$e[i, "y.min"] <= y) {
current <- rbind(current, p$e[i, ])
i <- i+1
}
state$i <<- i
#
# Sort the current edges by x-coordinate.
#
x.coord <- function(e, y) {
if (e["y.max"] > e["y.min"]) {
((y - e["y.min"]) * e["x.max"] + (e["y.max"] - y) * e["x.min"]) / (e["y.max"] - e["y.min"])
} else {
min(e["x.min"], e["x.max"])
}
}
if (length(current) > 0) {
x.array <- apply(current, 1, function(e) x.coord(e, y))
i.x <- order(x.array)
current <- current[i.x, ]
x.array <- x.array[i.x]
#
# Scan and mark each interval as interior or exterior.
#
status <- FALSE
interior <- numeric(length(x.array))
for (i in 1:length(x.array)) {
if (current[i, "y.max"] == y) {
interior[i] <- TRUE
} else {
status <- !status
interior[i] <- status
}
}
#
# Simplify the data structure by retaining the last value of `interior`
# within each group of common values of `x.array`.
#
interior <- sapply(split(interior, x.array), function(i) rev(i)[1])
x.array <- sapply(split(x.array, x.array), function(i) i[1])
print(y)
print(current)
print(rbind(x.array, interior))
markers <- c(1, diff(interior))
intervals <- x.array[markers != 0]
#
# Break into a list structure.
#
if (length(intervals) > 1) {
if (length(intervals) %% 2 == 1)
intervals <- intervals[-length(intervals)]
blocks <- 1:length(intervals) - 1
blocks <- blocks - (blocks %% 2)
intervals <- split(intervals, blocks)
} else {
intervals <- list()
}
} else {
intervals <- list()
}
#
# Update the state.
#
state$current <<- current
}
list(y=y, x=intervals)
} # Update()
process <- function(intervals, x, y) {
# intervals is a list of 2-vectors. Each represents the endpoints of
# an interior interval of a polygon.
# x is an array of x-coordinates of vertices.
#
# Retains only the intervals containing at least one vertex.
between <- function(i) {
1 == max(mapply(function(a,b) a && b, i[1] <= x, x <= i[2]))
}
is.good <- lapply(intervals$x, between)
list(y=y, x=intervals$x[unlist(is.good)])
#intervals
}
#
# Group the vertices by common y-coordinate.
#
vertices.x <- split(p$v[, "x"], p$v[, "y"])
vertices.y <- lapply(split(p$v[, "y"], p$v[, "y"]), max)
#
# The "state" is a collection of segments and an index into edges.
# It will updated during the vertical line sweep.
#
state <- list(level=-Inf, current=c(), i=1, x=c(), interior=c())
#
# Sweep vertically from bottom to top, processing the intersection
# as we go.
#
mapply(function(x,y) process(update(y), x, y), vertices.x, vertices.y)
}
scale <- 10
p.raw = list(scale * cbind(x=c(0:10,7,6,0), y=c(3,0,0,-1,-1,-1,0,-0.5,0.75,1,4,1.5,0.5,3)),
scale *cbind(x=c(1,1,2.4,2,4,4,4,4,2,1), y=c(0,1,2,1,1,0,-0.5,1,1,0)),
scale *cbind(x=c(6,7,6,6), y=c(.5,2,3,.5)))
#p.raw = list(cbind(x=c(0,2,1,1/2,0), y=c(0,0,2,1,0)))
#p.raw = list(cbind(x=c(0, 35, 100, 65, 0), y=c(0, 50, 100, 50, 0)))
p <- as.polygon(p.raw)
results <- fetch.x(p)
#
# Find the longest.
#
dx <- matrix(unlist(results["x", ]), nrow=2)
length.max <- max(dx[2,] - dx[1,])
#
# Draw pictures.
#
segment.plot <- function(s, length.max, colors, ...) {
lapply(s$x, function(x) {
col <- ifelse (diff(x) >= length.max, colors[1], colors[2])
lines(x, rep(s$y,2), col=col, ...)
})
}
gray <- "#f0f0f0"
grayer <- "#d0d0d0"
plot(expand(p$bb, 1.1), type="n", xlab="x", ylab="y", main="After the Scan")
sapply(1:length(p.raw), function(i) polygon(p.raw[[i]], col=c(gray, "White", grayer)[i]))
apply(results, 2, function(s) segment.plot(s, length.max, colors=c("Red", "#b8b8a8"), lwd=4))
plot(p, col="Black", lty=3)
points(p, pch=19, col=round(2 + 2*p$v[, "y"]/scale, 0))
points(p, cex=1.25)
다음은 래스터 기반 솔루션입니다. 빠릅니다 (14 분 안에 모든 작업을 처음부터 끝까지 완료했습니다).
다각형의 래스터 표현으로 시작하십시오. 이것은 550 행과 1200 열의 그리드를 사용합니다.
이 표현에서 회색 (내부) 셀의 값은 1이고 다른 모든 셀은 NoData입니다.
웨이트 그리드의 단위 셀 값 ( "강우량")을 사용하여 서쪽에서 동쪽으로의 흐름 누적을 계산합니다 .
낮은 축적은 어둡고 밝은 노랑에서 가장 높은 축적으로 증가합니다.
구역 최대 값 (그리드에 다각형을 사용하고 값에 흐름 누적을 사용)은 흐름이 가장 큰 셀을 식별합니다. 이를 보여주기 위해 오른쪽 하단으로 확대해야했습니다.
적혈구는 흐름의 가장 높은 축적의 끝을 표시합니다. 이들은 다각형의 최대 길이 내부 세그먼트의 가장 오른쪽 끝점입니다.
이 세그먼트를 찾으려면 모든 무게를 적혈구에 놓고 흐름을 거꾸로 실행하십시오!
바닥 근처의 빨간색 줄은 두 줄의 셀을 나타냅니다. 그 안에는 최대 길이의 가로 세그먼트가 있습니다. 추가 표현을 위해이 표현을있는 그대로 사용하거나 폴리 라인 (또는 다각형) 모양으로 변환하십시오.
래스터 표현으로 인한 이산화 오류가 있습니다. 계산 시간에 어느 정도의 비용으로 해상도를 높여서 줄일 수 있습니다.
이 접근법의 한 가지 좋은 점은 일반적으로 파이프 라인이나 축구장 배치, 생태 버퍼 생성 등과 같은 목표를 달성해야하는 더 큰 워크 플로의 일부로 극한의 가치를 발견한다는 것입니다. 이 과정에는 절충이 포함됩니다. 따라서 가장 긴 수평선은 최적 솔루션의 일부가 아닐 수 있습니다. 여기서 우리는 알고 대신에 관심이 있습니다 거의 긴 줄이 거짓말을 것입니다. 이는 구역 최대 흐름을 선택하는 것이 아니라 구역 최대에 가까운 모든 셀을 선택하십시오. 이 예에서 구역 최대 값은 744 (가장 긴 내부 세그먼트에 걸쳐있는 열 수)와 같습니다. 대신, 최대 값의 5 % 이내의 모든 셀을 선택하겠습니다 :
동쪽에서 서쪽으로 흐름을 실행하면 다음과 같은 가로 세그먼트 모음이 생성됩니다.
이것은 중단되지 않은 동서 범위가 다각형 내 어디에서나 최대 동서 범위보다 95 % 이상 큰 위치의 맵입니다.
확인. 나는 또 다른 (더 나은) 아이디어를 가지고 있습니다 ( idea-№2 ). 그러나 SQL 쿼리가 아닌 파이썬 스크립트로 실현하는 것이 낫다고 생각합니다. 여기서도 EW뿐만 아니라 일반적인 경우가 있습니다.
다각형의 경계 상자와 측정 방향으로 방위각 (A)이 필요합니다. BBox 모서리의 길이가 LA 및 LB라고 가정하십시오. 다각형 내 최대 거리 (MD)는 다음 MB = (LA^2 * LB^2)^(1/2)
과 같습니다 . 따라서 탐색 값 (V)은 MB :보다 크지 않습니다 V <= MB
.
Fetzer의 대답이 당신이하고 싶은 일인지 확실하지 않지만 st_box2d가 일을 할 수 있습니다.
SS_Rebelious의 아이디어 N ° 1은 많은 경우 작동하지만 일부 오목 다각형에는 적용되지 않습니다.
동서 선 가능성이있는 경우 정점으로 만든 선이 다각형의 경계를 통과 할 때 가장자리를 따르는 인공 lw 선을 만들어야한다고 생각합니다.
이를 위해 선 길이가 높은 4 개의 노드 다각형을 만들고 앞의 다각형과 겹치는 다각형 P *를 만들고 min (y1)과 max (y2)가 일부 x 선을 남기는 지 확인하십시오 가능성. (여기서 y1은 왼쪽 상단 코넷과 오른쪽 상단 모서리 사이의 포인트 세트이고, y2는 4 개의 노드 다각형의 왼쪽 하단과 오른쪽 하단 모서리 사이의 y 세트입니다). 이것은 당신이 당신을 도울 psql 도구를 찾을 수 있기를 쉽지 않습니다!
나는 아이디어 -№1 ( 편집 : EW 방향뿐만 아니라 주석에 설명 된 몇 가지 제한 사항이있는 일반적인 경우). 코드를 제공하지 않고 개념 만 제공합니다. "x 방향"은 실제로 방위각이며 ST_Azimuth에 의해 계산됩니다. 제안 된 단계는 다음과 같습니다.
내 질문 과 Evil Genius 의 답변 을 살펴보십시오 .
호수 폴리곤에 많은 점이 있기를 바랍니다. 이러한 점에 방위각 (가로, 지리적 방향)으로 선을 만들 수 있습니다. 가장 먼 두 선 사이의 가장 짧은 선을 계산할 수 있도록 충분히 큰 선 길이 (ST_MakePoint 부분)를 선택하십시오.
예를 들면 다음과 같습니다.
이 예는 다각형의 최대 너비를 보여줍니다. 이 방법으로 ST_ShortestLine (빨간색 선)을 선택합니다. ST_MakeLine은 값 (파란색 선)을 증가시키고 선의 끝점 (왼쪽 아래)은 다각형의 파란색 선에 닿습니다. 작성된 (도움말) 선의 중심을 사용하여 거리를 계산해야합니다.
이 접근법에 대한 불규칙하거나 오목한 다각형에 대한 아이디어. 다각형을 래스터와 교차시켜야 할 수도 있습니다.