Tôi quan tâm đến chiều rộng tối đa của một đa giác, ví dụ như một cái hồ, theo hướng đông tây. Các hộp giới hạn sẽ chỉ giúp trong các đa giác đơn giản chứ không phải trong các đa giác lõm phức tạp.
Tôi quan tâm đến chiều rộng tối đa của một đa giác, ví dụ như một cái hồ, theo hướng đông tây. Các hộp giới hạn sẽ chỉ giúp trong các đa giác đơn giản chứ không phải trong các đa giác lõm phức tạp.
Câu trả lời:
Điều này có thể yêu cầu một số kịch bản trong bất kỳ nền tảng GIS.
Phương pháp hiệu quả nhất (không có triệu chứng) là quét đường thẳng đứng: nó yêu cầu sắp xếp các cạnh theo tọa độ y tối thiểu của chúng và sau đó xử lý các cạnh từ dưới cùng (tối thiểu y) đến đầu (tối đa y), cho nhật ký O (e * ( e)) thuật toán khi các cạnh e có liên quan.
Thủ tục, mặc dù đơn giản, nhưng đáng ngạc nhiên là khó khăn để có được đúng trong mọi trường hợp. Đa giác có thể khó chịu: chúng có thể có các mối nguy hiểm, mảnh, lỗ, bị ngắt kết nối, có các đỉnh trùng lặp, chạy các đỉnh dọc theo các đường thẳng và có ranh giới không bị phá hủy giữa hai thành phần liền kề. Dưới đây là một ví dụ thể hiện nhiều đặc điểm này (và hơn thế nữa):
Chúng tôi sẽ đặc biệt tìm kiếm (các) phân đoạn ngang có chiều dài tối đa nằm hoàn toàn trong phạm vi đóng của đa giác. Chẳng hạn, điều này giúp loại bỏ sự nguy hiểm giữa x = 20 và x = 40 phát ra từ lỗ giữa x = 10 và x = 25. Sau đó, thật đơn giản để chỉ ra rằng ít nhất một trong các đoạn ngang có chiều dài tối đa giao nhau ít nhất một đỉnh. (Nếu có những giải pháp giao nhau không có đỉnh, họ sẽ nằm trong nội thất của một số hình bình hành bao bọc ở phía trên và phía dưới của giải pháp mà làm giao nhau ít nhất một đỉnh. Điều này cho chúng ta một phương tiện để tìm tất cả các giải pháp.)
Theo đó, quá trình quét dòng phải bắt đầu bằng các đỉnh thấp nhất và sau đó di chuyển lên trên (nghĩa là hướng tới các giá trị y cao hơn) để dừng tại mỗi đỉnh. Tại mỗi điểm dừng, chúng tôi tìm thấy bất kỳ cạnh mới nào phát ra từ độ cao đó; loại bỏ bất kỳ cạnh nào chấm dứt từ bên dưới ở độ cao đó (đây là một trong những ý tưởng chính: nó đơn giản hóa thuật toán và loại bỏ một nửa khả năng xử lý tiềm năng); và cẩn thận xử lý bất kỳ cạnh nào nằm hoàn toàn ở độ cao không đổi (các cạnh ngang).
Ví dụ, hãy xem xét trạng thái khi đạt đến mức y = 10. Từ trái sang phải, chúng tôi tìm thấy các cạnh sau:
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
Trong bảng này, (x.min, y.min) là tọa độ của điểm cuối thấp hơn của cạnh và (x.max, y.max) là tọa độ của điểm cuối trên của nó. Ở cấp độ này (y = 10), cạnh thứ nhất bị chặn trong phần bên trong của nó, cạnh thứ hai bị chặn ở dưới cùng, v.v. Một số cạnh chấm dứt ở cấp độ này, chẳng hạn như từ (10.0) đến (10,10), không được đưa vào danh sách.
Để xác định vị trí của các điểm bên trong và các điểm bên ngoài, hãy tưởng tượng bắt đầu từ cực bên trái - dĩ nhiên nằm ngoài đa giác - và di chuyển theo chiều ngang sang phải. Mỗi lần chúng ta đi qua một cạnh không nằm ngang , chúng ta luân phiên chuyển từ bên ngoài sang bên trong và phía sau. (Đây là một ý tưởng quan trọng khác.) Tuy nhiên, tất cả các điểm trong bất kỳ cạnh ngang nào được xác định là nằm trong đa giác, không có vấn đề gì. (Việc đóng một đa giác luôn bao gồm các cạnh của nó.)
Tiếp tục ví dụ, đây là danh sách tọa độ x được sắp xếp trong đó các cạnh không nằm ngang bắt đầu tại hoặc vượt qua đường y = 10:
x.array 6.7 10 20 48 60 63.3 65 90
interior 1 0 1 0 1 0 1 0
(Lưu ý rằng x = 40 không có trong danh sách này.) Các giá trị của interior
mảng đánh dấu các điểm cuối bên trái của các phân đoạn bên trong: 1 chỉ định một khoảng bên trong, 0 một khoảng bên ngoài. Do đó, 1 đầu tiên chỉ ra khoảng từ x = 6,7 đến x = 10 nằm trong đa giác. 0 tiếp theo cho biết khoảng từ x = 10 đến x = 20 nằm ngoài đa giác. Và do đó, nó tiến hành: mảng xác định bốn khoảng riêng biệt như bên trong đa giác.
Một số trong các khoảng này, chẳng hạn như một từ x = 60 đến x = 63.3, không giao nhau với bất kỳ đỉnh nào: kiểm tra nhanh đối với tọa độ x của tất cả các đỉnh với y = 10 sẽ loại bỏ các khoảng đó.
Trong quá trình quét, chúng tôi có thể theo dõi độ dài của các khoảng này, giữ lại dữ liệu liên quan đến (các) khoảng thời gian dài tối đa được tìm thấy cho đến nay.
Lưu ý một số ý nghĩa của phương pháp này. Một đỉnh hình chữ "v", khi gặp phải, là gốc của hai cạnh. Do đó, hai công tắc xảy ra khi đi qua nó. Những công tắc đó hủy bỏ. Bất kỳ "v" lộn ngược nào thậm chí không được xử lý, bởi vì cả hai cạnh của nó đều bị loại bỏ trước khi bắt đầu quét từ trái sang phải. Trong cả hai trường hợp, một đỉnh như vậy không chặn một đoạn ngang.
Nhiều hơn hai cạnh có thể chia sẻ một đỉnh: điều này được minh họa ở (10.0), (60,5), (25, 20) và - mặc dù rất khó để nói - tại (20,10) và (40 , 10). (Đó là vì dangle đi (20,10) -> (40,10) -> (40,0) -> (40, -50) -> (40, 10) -> (20, 10). Lưu ý rằng đỉnh ở (40,0) cũng nằm trong phần bên của cạnh khác ... thật khó chịu.) Thuật toán này xử lý các tình huống đó tốt.
Một tình huống khó khăn được minh họa ở phía dưới cùng: tọa độ x của các phân đoạn không nằm ngang có
30, 50
Điều này khiến mọi thứ ở bên trái của x = 30 được coi là bên ngoài, mọi thứ trong khoảng từ 30 đến 50 là bên trong và mọi thứ sau 50 sẽ trở lại bên ngoài. Đỉnh tại x = 40 thậm chí không bao giờ được xem xét trong thuật toán này.
Đây là những gì đa giác trông giống như ở cuối quét. Tôi hiển thị tất cả các khoảng bên trong chứa đỉnh có màu xám đậm, bất kỳ khoảng thời gian dài tối đa nào có màu đỏ và tô màu các đỉnh theo tọa độ y của chúng. Khoảng thời gian tối đa là 64 đơn vị dài.
Các tính toán hình học duy nhất có liên quan là tính toán nơi các cạnh cắt nhau theo đường ngang: đó là phép nội suy tuyến tính đơn giản. Tính toán cũng cần thiết để xác định phân đoạn bên trong nào chứa các đỉnh: đây là các phép xác định giữa , dễ dàng tính toán với một vài bất đẳng thức. Sự đơn giản này làm cho thuật toán mạnh mẽ và phù hợp cả cho các biểu diễn tọa độ nguyên và dấu phẩy động.
Nếu tọa độ là địa lý , thì các đường ngang thực sự nằm trên các vòng tròn vĩ độ. Độ dài của chúng không khó để tính toán: chỉ cần nhân độ dài Euclide của chúng với cosin của vĩ độ của chúng (trong một mô hình hình cầu). Do đó, thuật toán này thích ứng độc đáo với tọa độ địa lý. (Để xử lý việc quấn quanh kinh tuyến + -180, trước tiên người ta có thể cần tìm một đường cong từ cực nam đến cực bắc không đi qua đa giác. đường cong, thuật toán này sẽ tìm chính xác phân đoạn ngang tối đa.)
Sau đây là R
mã được thực hiện để thực hiện các tính toán và tạo các minh họa.
#
# 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)
Đây là một giải pháp dựa trên raster. Thật nhanh chóng (tôi đã thực hiện tất cả các công việc từ đầu đến cuối trong 14 phút), không yêu cầu kịch bản, chỉ mất một vài thao tác và rất chính xác.
Bắt đầu với một đại diện raster của đa giác. Cái này sử dụng lưới 550 hàng và 1200 cột:
Trong biểu diễn này, các ô màu xám (bên trong) có giá trị 1 và tất cả các ô khác là NoData.
Tính toán tích lũy dòng chảy theo hướng từ tây sang đông bằng cách sử dụng các giá trị ô đơn vị cho lưới trọng lượng (lượng "lượng mưa"):
Tích lũy thấp là tối, tăng đến tích lũy cao nhất trong màu vàng sáng.
Một cực đại vùng (sử dụng đa giác cho lưới và tích lũy luồng cho các giá trị) xác định (các) ô trong đó luồng là lớn nhất. Để hiển thị những thứ này, tôi phải phóng to về phía dưới bên phải:
Các ô màu đỏ đánh dấu các đầu của dòng tích lũy cao nhất: chúng là các điểm cuối bên phải của các đoạn bên trong có độ dài tối đa của đa giác.
Để tìm các phân đoạn này, đặt tất cả trọng lượng tại các ô màu đỏ và chạy dòng chảy ngược!
Dải màu đỏ gần phía dưới đánh dấu hai hàng ô: bên trong chúng nằm ở đoạn ngang có chiều dài tối đa. Sử dụng biểu diễn này như là để phân tích thêm hoặc chuyển đổi nó thành hình dạng đa tuyến (hoặc đa giác).
Có một số lỗi riêng biệt được thực hiện với một đại diện raster. Nó có thể được giảm bằng cách tăng độ phân giải, với một số chi phí trong thời gian tính toán.
Một khía cạnh thực sự tốt đẹp của phương pháp này là thông thường chúng ta tìm thấy các giá trị cực đoan của mọi thứ như là một phần của quy trình công việc lớn hơn, trong đó một số mục tiêu cần phải đạt được: đặt một đường ống hoặc sân bóng đá, tạo ra bộ đệm sinh thái, v.v. Quá trình này bao gồm sự đánh đổi. Do đó, đường ngang dài nhất có thể không phải là một phần của giải pháp tối ưu. Thay vào đó, chúng ta có thể quan tâm để biết những dòng gần như dài nhất sẽ nằm ở đâu. Điều này rất đơn giản: thay vì chọn luồng tối đa của vùng, hãy chọn tất cả các ô gần với mức tối đa của vùng. Trong ví dụ này, zonal max bằng 744 (số lượng cột được kéo dài bởi đoạn nội thất dài nhất). Thay vào đó, hãy chọn tất cả các ô trong phạm vi tối đa 5%:
Chạy dòng chảy từ đông sang tây tạo ra bộ sưu tập các phân đoạn ngang này:
Đây là bản đồ các địa điểm mà phạm vi đông-tây không bị gián đoạn là 95% hoặc lớn hơn phạm vi đông-tây tối đa ở bất cứ đâu trong đa giác.
Đồng ý. Tôi đã có một ý tưởng khác (tốt hơn) ( ý tưởng - 2 ). Nhưng tôi cho rằng sẽ tốt hơn nếu được nhận ra là tập lệnh python, không phải là SQL-querry. Một lần nữa ở đây là trường hợp phổ biến, không chỉ EW.
Bạn sẽ cần một hộp giới hạn cho đa giác và góc phương vị (A) làm hướng đo của bạn. Giả sử rằng độ dài của các cạnh BBox là LA và LB. Khoảng cách tối đa có thể (MD) trong đa giác là : MB = (LA^2 * LB^2)^(1/2)
, vì vậy giá trị tìm kiếm (V) không lớn hơn MB : V <= MB
.
Tôi không chắc chắn rằng câu trả lời của Fetzer là những gì bạn muốn làm, nhưng vì vậy st_box2d có thể thực hiện công việc.
Ý tưởng của SS_Rebelious N ° 1 sẽ hoạt động trong nhiều trường hợp nhưng không phải cho một số đa giác lõm.
Tôi nghĩ rằng bạn phải tạo các đường lw nhân tạo có điểm theo các cạnh khi các đường tạo đỉnh vượt qua biên của đa giác nếu có khả năng đường đông-tây.
Để làm điều này, bạn có thể thử tạo đa giác 4 nút trong đó độ dài của đường cao, tạo đa giác P * là chồng chéo trước với đa giác ban đầu của bạn và xem liệu min (y1) và max (y2) có để lại một số dòng x không khả năng. (trong đó y1 là tập hợp điểm giữa góc trên bên trái và góc trên bên phải và y2 tập hợp y giữa góc dưới bên trái và dưới cùng bên phải của đa giác 4 nút của bạn). Điều này không dễ dàng gì, tôi hy vọng bạn sẽ tìm thấy các công cụ psql để giúp bạn!
Tôi có một ý tưởng-№1 ( Chỉnh sửa: đối với trường hợp phổ biến, không chỉ hướng EW và với một số hạn chế được mô tả trong các nhận xét). Tôi sẽ không cung cấp mã, chỉ là một khái niệm. "Hướng x" thực sự là một góc phương vị, được tính bằng ST_Azimuth. Các bước đề xuất là:
Hãy xem câu hỏi của tôi và câu trả lời từ Evil Genius.
Hy vọng rằng đa giác hồ của bạn có một số điểm, bạn có thể tạo các đường trên các điểm này với góc phương vị (khía cạnh, hướng địa lý). Chọn độ dài của các dòng đủ lớn (phần ST_MakePoint), để bạn có thể tính được dòng ngắn nhất giữa hai dòng xa nhất.
Đây là một ví dụ:
Ví dụ cho thấy chiều rộng tối đa của đa giác. Tôi chọn ST_ShortestLine (dòng màu đỏ) cho phương pháp này. ST_MakeLine sẽ tăng giá trị (đường màu xanh) và điểm cuối của đường (phía dưới bên trái) sẽ chạm vào đường màu xanh của đa giác. Bạn phải tính khoảng cách với trọng tâm của các dòng (trợ giúp) đã tạo.
Một ý tưởng cho đa giác không đều hoặc lõm cho phương pháp này. Có thể bạn phải giao cắt đa giác với raster.