Tôi muốn tạo một bộ đệm định hướng cho mọi đa giác trong shapefile của mình bằng arcpy. Theo định hướng tôi có nghĩa là tôi có hai góc a1 và a2 ràng buộc hướng của bộ đệm. Điều này được thể hiện trong biểu đồ dưới đây:
Có ý kiến gì không?
Tôi muốn tạo một bộ đệm định hướng cho mọi đa giác trong shapefile của mình bằng arcpy. Theo định hướng tôi có nghĩa là tôi có hai góc a1 và a2 ràng buộc hướng của bộ đệm. Điều này được thể hiện trong biểu đồ dưới đây:
Có ý kiến gì không?
Câu trả lời:
Câu trả lời này đặt câu hỏi vào ngữ cảnh lớn hơn, mô tả một thuật toán hiệu quả áp dụng cho biểu diễn các tính năng của shapefile (như "vectơ" hoặc "linestrings" của các điểm), hiển thị một số ví dụ về ứng dụng của nó và đưa ra mã làm việc để sử dụng hoặc chuyển vào một môi trường GIS.
Đây là một ví dụ về sự giãn nở hình thái. Nói chung, một sự giãn nở "lan truyền" các điểm của một khu vực vào các khu vực lân cận của họ; bộ sưu tập các điểm mà chúng cuộn lên là "sự giãn nở". Các ứng dụng trong GIS có rất nhiều: mô hình hóa sự lan truyền của lửa, sự di chuyển của các nền văn minh, sự lan rộng của thực vật và nhiều hơn nữa.
Về mặt toán học, và nói chung là rất lớn (nhưng hữu ích), sự giãn nở lan truyền một tập hợp các điểm trong đa tạp Riemannian (như mặt phẳng, hình cầu hoặc ellipsoid). Sự lây lan được quy định bởi một tập hợp con của bó tiếp tuyến tại các điểm này. Điều này có nghĩa là tại mỗi một điểm, một tập các vectơ (hướng và khoảng cách) được đưa ra (tôi gọi đây là "vùng lân cận"); mỗi vectơ này mô tả một đường trắc địa bắt đầu tại điểm gốc của nó. Điểm cơ bản là "trải" đến cuối của tất cả các đường dẫn này. (Để biết định nghĩa hạn chế hơn về "sự giãn nở" thường được sử dụng trong xử lý ảnh, hãy xem bài viết Wikipedia . Hàm lan truyền được gọi là bản đồ theo cấp số nhân trong hình học vi phân.)
"Bộ đệm" của một tính năng là một trong những ví dụ đơn giản nhất về sự giãn nở như vậy: một đĩa có bán kính không đổi (bán kính bộ đệm) được tạo ra (ít nhất là về mặt khái niệm) xung quanh mỗi điểm của tính năng. Sự kết hợp của các đĩa này là bộ đệm.
Câu hỏi này yêu cầu tính toán độ giãn phức tạp hơn một chút trong đó sự lan truyền chỉ được phép xảy ra trong một phạm vi góc nhất định (nghĩa là trong một khu vực hình tròn). Điều này chỉ có ý nghĩa đối với các tính năng không có bề mặt cong đáng kể (chẳng hạn như các đặc điểm nhỏ trên hình cầu hoặc ellipsoid hoặc bất kỳ tính năng nào trong mặt phẳng). Khi chúng ta đang làm việc trong mặt phẳng, việc định hướng tất cả các lĩnh vực theo cùng một hướng cũng có ý nghĩa. (Tuy nhiên, nếu chúng ta mô hình hóa sự lan truyền của lửa bằng gió, chúng ta sẽ muốn các khu vực được định hướng theo gió và kích thước của chúng cũng có thể thay đổi theo tốc độ gió: đó là một động lực cho định nghĩa chung về sự giãn nở mà tôi đã đưa ra. ) (Trên các bề mặt cong như hình elip, nói chung không thể định hướng tất cả các khu vực theo hướng "giống nhau".)
Trong các trường hợp sau, sự giãn nở tương đối dễ dàng để tính toán:
Tính năng này nằm trong mặt phẳng (nghĩa là chúng tôi đang mở rộng bản đồ của tính năng và hy vọng bản đồ này khá chính xác).
Sự giãn nở sẽ không đổi : sự lan rộng tại mọi điểm của đối tượng địa lý sẽ xảy ra trong các vùng lân cận có cùng hướng.
Khu phố chung này là lồi. Convexity đơn giản hóa rất nhiều và tăng tốc độ tính toán.
Câu hỏi này phù hợp với các trường hợp chuyên biệt như vậy: nó yêu cầu sự giãn nở của các đa giác tùy ý bởi các cung tròn có nguồn gốc (trung tâm của các đĩa mà chúng đến) được đặt tại các điểm cơ sở. Với điều kiện những khu vực đó không kéo dài quá 180 độ, chúng sẽ bị lồi. (Các lĩnh vực lớn hơn luôn có thể được chia làm một nửa thành hai khu vực lồi; sự kết hợp của hai độ giãn nhỏ hơn sẽ cho kết quả mong muốn.)
Bởi vì chúng tôi đang thực hiện các tính toán Euclide - thực hiện việc lan truyền trong mặt phẳng - chúng tôi có thể làm giãn một điểm chỉ bằng cách dịch vùng lân cận giãn nở đến điểm đó. (Để có thể làm điều này, khu phố cần có nguồn gốcđiều đó sẽ tương ứng với điểm cơ bản. Ví dụ, nguồn gốc của các lĩnh vực trong câu hỏi này là trung tâm của vòng tròn mà chúng được hình thành. Nguồn gốc này xảy ra để nằm trên ranh giới của ngành. Trong hoạt động đệm tiêu chuẩn của GIS, vùng lân cận là một vòng tròn có nguồn gốc ở trung tâm của nó; bây giờ nguồn gốc nằm trong phần bên trong của vòng tròn. Chọn một nguồn gốc không phải là một vấn đề lớn về mặt tính toán, bởi vì sự thay đổi nguồn gốc chỉ làm thay đổi toàn bộ sự giãn nở, nhưng nó có thể là một vấn đề lớn về mặt mô hình các hiện tượng tự nhiên. Các sector
chức năng trong các mã dưới đây minh họa cách một nguồn gốc có thể được xác định.)
Việc làm giãn một đoạn đường có thể khó khăn, nhưng đối với một vùng lân cận lồi, chúng ta có thể tạo ra sự giãn nở như là sự kết hợp của sự giãn nở của hai điểm cuối cùng với hình bình hành được chọn cẩn thận. (Vì lợi ích của không gian, tôi sẽ không tạm dừng để chứng minh các xác nhận toán học như thế này, nhưng khuyến khích người đọc thử bằng chứng của riêng họ vì đây là một bài tập sâu sắc.) Dưới đây là một minh họa sử dụng ba lĩnh vực (hiển thị màu hồng). Họ có bán kính đơn vị và góc của họ được đưa ra trong các tiêu đề. Đoạn đường có chiều dài 2, nằm ngang và được hiển thị màu đen:
Các hình bình hành được tìm thấy bằng cách định vị các điểm màu hồng càng xa càng tốt so với đoạn theo hướng dọc . Điều này cho hai điểm thấp hơn và hai điểm trên dọc theo đường thẳng song song với đoạn. Chúng ta chỉ cần nối bốn điểm thành một hình bình hành (hiển thị bằng màu xanh). Ở bên phải, điều này có ý nghĩa như thế nào ngay cả khi bản thân khu vực đó chỉ là một đoạn thẳng (và không phải là một đa giác thực sự): ở đó, mọi điểm trên đoạn đã được dịch theo hướng 171 độ về phía bắc cho một khoảng cách từ 0 đến 1. Tập hợp các điểm cuối này là hình bình hành được hiển thị. Các chi tiết của tính toán này xuất hiện trong buffer
hàm được định nghĩa trong dilate.edges
mã bên dưới.
Để làm giãn một đa tuyến , chúng ta tạo thành sự kết hợp của độ giãn của các điểm và các đoạn tạo thành nó. Hai dòng cuối cùng dilate.edges
thực hiện vòng lặp này.
Làm giãn một đa giác đòi hỏi bao gồm cả phần bên trong của đa giác cùng với sự giãn nở của ranh giới của nó. (Khẳng định này đưa ra một số giả định về vùng lân cận giãn nở. Một là tất cả các vùng lân cận đều chứa điểm (0,0), đảm bảo đa giác được bao gồm trong sự giãn nở của nó. điểm của đa giác sẽ không vượt ra ngoài sự giãn nở các điểm biên. Đây là trường hợp đối với các vùng lân cận không đổi.)
Chúng ta hãy xem xét một số ví dụ về cách thức hoạt động của nó, đầu tiên với một phi hình (được chọn để tiết lộ chi tiết) và sau đó với một vòng tròn (được chọn để khớp với hình minh họa trong câu hỏi). Các ví dụ sẽ tiếp tục sử dụng ba vùng lân cận giống nhau, nhưng được thu nhỏ lại thành bán kính 1/3.
Trong hình này, phần bên trong của đa giác có màu xám, độ giãn điểm (cung) là màu hồng và độ giãn cạnh (hình bình hành) có màu xanh.
"Vòng tròn" thực sự chỉ là 60 gon, nhưng nó gần đúng với một vòng tròn.
Khi tính năng cơ sở được biểu thị bằng N điểm và vùng lân cận giãn nở bằng điểm M, thuật toán này đòi hỏi nỗ lực O (N M) . Nó phải được theo dõi bằng cách đơn giản hóa sự lộn xộn của các đỉnh và cạnh trong liên kết, điều này có thể đòi hỏi nỗ lực O (N M log (N M)): đó là điều cần yêu cầu GIS thực hiện; chúng ta không nên lập trình điều đó.
Nỗ lực tính toán có thể được cải thiện thành O (M + N) cho các tính năng cơ sở lồi (vì bạn có thể tìm ra cách di chuyển xung quanh ranh giới mới bằng cách hợp nhất các danh sách các đỉnh mô tả ranh giới của hai hình dạng ban đầu). Điều này cũng không cần làm sạch sau này.
Khi vùng lân cận giãn nở từ từ thay đổi kích thước và / hoặc hướng khi bạn tiến triển xung quanh tính năng cơ sở, độ giãn của cạnh có thể được xác định gần đúng từ vỏ lồi của sự kết hợp các điểm cuối của nó. Nếu hai vùng lân cận giãn nở có điểm M1 và M2, điều này có thể được tìm thấy với nỗ lực O (M1 + M2) bằng thuật toán được mô tả trong Shamos & Prepata, Hình học tính toán . Do đó, đặt K = M1 + M2 + ... + M (N) là tổng số đỉnh trong vùng lân cận N, chúng ta có thể tính toán độ giãn nở trong thời gian O (K * log (K)).
Tại sao chúng ta muốn giải quyết một khái quát như vậy nếu tất cả những gì chúng ta muốn là một bộ đệm đơn giản? Đối với các đối tượng địa lý lớn trên trái đất, vùng lân cận giãn nở (như đĩa) có kích thước không đổi trong thực tế có thể có kích thước khác nhau trên bản đồ nơi các phép tính này được thực hiện. Do đó, chúng tôi có được một cách để thực hiện các phép tính chính xác cho ellipsoid trong khi tiếp tục tận hưởng tất cả các lợi thế của hình học Euclide.
Các ví dụ được tạo ra với R
nguyên mẫu này , có thể dễ dàng chuyển sang ngôn ngữ yêu thích của bạn (Python, C ++, v.v.). Về cấu trúc, nó tương đồng với phân tích được báo cáo trong câu trả lời này và do đó không cần giải thích riêng. Bình luận làm rõ một số chi tiết.
(Nó có thể là thú vị để lưu ý rằng tính toán lượng giác được sử dụng duy nhất để tạo ra các tính năng chẳng hạn - đó là đa giác thường xuyên -. Và các lĩnh vực Không một phần của phép tính giãn nở đòi hỏi bất kỳ lượng giá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")
})
Thời gian tính toán cho ví dụ này (từ hình cuối cùng), với N = 60 và M = 121 (trái), M = 181 (giữa) và M = 2 (phải), là một phần tư giây. Tuy nhiên, hầu hết điều này là dành cho màn hình. Thông thường, R
mã này sẽ xử lý khoảng N M = 1,5 triệu mỗi giây (chỉ mất 0,002 giây hoặc lâu hơn để thực hiện tất cả các tính toán ví dụ được hiển thị). Tuy nhiên, sự xuất hiện của sản phẩm M N ngụ ý sự giãn nở của nhiều số liệu hoặc số liệu phức tạp thông qua một vùng lân cận chi tiết có thể mất thời gian đáng kể, vì vậy hãy cẩn thận! Điểm chuẩn thời gian cho các vấn đề nhỏ hơn trước khi giải quyết một vấn đề lớn. Trong những trường hợp như vậy, người ta có thể tìm đến một giải pháp dựa trên raster ( dễ thực hiện hơn nhiều , về cơ bản chỉ cần một phép tính lân cận.)
Điều này khá rộng, nhưng bạn có thể: