ฉันต้องการสร้างบัฟเฟอร์เชิงสำหรับทุกรูปหลายเหลี่ยมใน shapefile ของฉันโดยใช้ arcpy โดยการวางเชิงฉันหมายความว่าฉันมีสองมุม a1 และ a2 ที่ จำกัด ทิศทางของบัฟเฟอร์ สิ่งนี้แสดงอยู่ในกราฟด้านล่าง:
ความคิดใด ๆ
ฉันต้องการสร้างบัฟเฟอร์เชิงสำหรับทุกรูปหลายเหลี่ยมใน shapefile ของฉันโดยใช้ arcpy โดยการวางเชิงฉันหมายความว่าฉันมีสองมุม a1 และ a2 ที่ จำกัด ทิศทางของบัฟเฟอร์ สิ่งนี้แสดงอยู่ในกราฟด้านล่าง:
ความคิดใด ๆ
คำตอบ:
คำตอบนี้วางคำถามไว้ในบริบทที่ใหญ่กว่าอธิบายอัลกอริทึมที่มีประสิทธิภาพที่ใช้กับการแสดงรูปร่างของคุณสมบัติ (เช่น "พาหะ" หรือ "linestrings" ของคะแนน) แสดงตัวอย่างของแอปพลิเคชันและให้รหัสการทำงานสำหรับการใช้หรือ สภาพแวดล้อม GIS
นี่คือตัวอย่างของการขยายก้าน โดยทั่วไปแล้วการขยาย "การแพร่กระจาย" จุดของภูมิภาคไปสู่ละแวกใกล้เคียง การสะสมคะแนนที่พวกเขาปิดท้ายคือ "การขยาย" แอปพลิเคชันใน GIS นั้นมีมากมาย: การจำลองการแพร่กระจายของไฟการเคลื่อนที่ของอารยธรรมการแพร่กระจายของพืชและอื่น ๆ อีกมากมาย
ในทางคณิตศาสตร์และในส่วนที่ดีมาก (แต่มีประโยชน์) การขยายการขยายชุดของคะแนนในหลายรีมันน์ (เช่นเครื่องบินทรงกลมหรือทรงรี) การแพร่กระจายถูกกำหนดโดยชุดย่อยของบันเดิลแทนเจนต์ที่จุดเหล่านี้ ซึ่งหมายความว่าในแต่ละจุดจะมีชุดของเวกเตอร์ (ทิศทางและระยะทาง) ให้ (ฉันเรียกสิ่งนี้ว่า "ย่าน") เวกเตอร์แต่ละตัวจะอธิบายเส้นทางทางภูมิศาสตร์ที่เริ่มต้นที่จุดฐาน จุดฐานคือ "การแพร่กระจาย" ไปยังจุดสิ้นสุดของเส้นทางเหล่านี้ทั้งหมด (สำหรับคำจำกัดความที่มากขึ้นของ "การขยายภาพ" ที่ใช้ตามอัตภาพในการประมวลผลภาพดูที่บทความ Wikipediaฟังก์ชั่นการแพร่กระจายเรียกว่าแผนที่แบบเอ็กซ์โปเนนเชียล ในเรขาคณิตเชิงอนุพันธ์)
"การบัฟเฟอร์"ของคุณสมบัติเป็นหนึ่งในตัวอย่างที่ง่ายที่สุดของการขยายดังกล่าว: ดิสก์ของรัศมีคงที่ (รัศมีบัฟเฟอร์) ถูกสร้างขึ้น (อย่างน้อยต้องเป็นแนวคิด) รอบแต่ละจุดของคุณลักษณะ การรวมกันของดิสก์เหล่านี้คือบัฟเฟอร์
คำถามนี้ถามถึงการคำนวณการขยายที่ซับซ้อนขึ้นเล็กน้อยซึ่งการแพร่กระจายได้รับอนุญาตให้เกิดขึ้นเฉพาะภายในช่วงมุมที่กำหนด (นั่นคือภายในเซกเตอร์แบบวงกลม) สิ่งนี้เหมาะสมสำหรับคุณสมบัติที่ไม่ทำให้พื้นผิวโค้งงอ (เช่นคุณสมบัติเล็ก ๆ บนทรงกลมหรือทรงรีหรือคุณลักษณะใด ๆ ในระนาบ) เมื่อเราทำงานบนเครื่องบินมันก็มีความหมายที่จะปรับทิศทางภาคทั้งหมดในทิศทางเดียวกัน (ถ้าเราจำลองการแพร่กระจายของไฟด้วยลมเราต้องการให้ภาคส่วนหันไปทางลมและขนาดของมันอาจแตกต่างกันตามความเร็วลมเช่นกัน: นั่นคือแรงจูงใจหนึ่งสำหรับนิยามทั่วไปของการขยายที่ฉันให้ ) (บนพื้นผิวโค้งเช่นรูปวงรีมันเป็นไปไม่ได้โดยทั่วไปที่จะปรับทิศทางทุกภาคส่วนในทิศทาง "เดียวกัน")
ในสถานการณ์ต่อไปนี้การขยายนั้นค่อนข้างง่ายในการคำนวณ
สถานที่อยู่ในระนาบ (นั่นคือเรากำลังขยายแผนที่ของสถานที่และหวังว่าแผนที่นั้นจะมีความแม่นยำพอสมควร)
การขยายจะคงที่ : การแพร่กระจายในทุก ๆ จุดของคุณลักษณะจะเกิดขึ้นภายในละแวกใกล้เคียงที่สอดคล้องกันของทิศทางเดียวกัน
ละแวกใกล้เคียงทั่วไปนี้นูน Convexity ช่วยลดความยุ่งยากและเพิ่มความเร็วในการคำนวณ
คำถามนี้เหมาะกับสถานการณ์พิเศษดังกล่าว: มันขอให้มีการขยายรูปหลายเหลี่ยมตามอำเภอใจโดยภาควงกลมที่มีต้นกำเนิด (ศูนย์กลางของดิสก์ที่พวกเขามา) ตั้งอยู่ที่จุดฐาน หากภาคส่วนเหล่านั้นไม่เกิน 180 องศาพวกมันจะนูน (ภาคที่ใหญ่กว่าสามารถแบ่งครึ่งออกเป็นสองส่วนได้โดยที่การรวมกันของการเจือจางที่มีขนาดเล็กกว่าทั้งสองจะให้ผลลัพธ์ที่ต้องการ
เนื่องจากเราทำการคำนวณแบบยุคลิด - ทำการแพร่กระจายในระนาบ - เราอาจขยายจุดโดยการแปลย่านขยายไปยังจุดนั้น (เพื่อให้สามารถทำสิ่งนี้ได้ย่านต้องมีจุดเริ่มต้นที่จะสอดคล้องกับจุดฐาน ตัวอย่างเช่นที่มาของภาคในคำถามนี้เป็นศูนย์กลางของวงกลมที่พวกเขาจะเกิดขึ้น ต้นกำเนิดนี้เกิดขึ้นกับขอบเขตของภาค ในการดำเนินการบัฟเฟอร์ GIS มาตรฐานย่านนั้นเป็นวงกลมที่มีจุดกำเนิดอยู่ตรงกลาง ตอนนี้ที่มาอยู่ในการตกแต่งภายในของวงกลม การเลือกแหล่งกำเนิดไม่ได้เป็นเรื่องใหญ่เพราะการเปลี่ยนแปลงของต้นกำเนิดเพียงเลื่อนการขยายทั้งหมด แต่อาจเป็นเรื่องใหญ่ในแง่ของการสร้างแบบจำลองปรากฏการณ์ทางธรรมชาติ sector
ฟังก์ชั่นในโค้ดด้านล่างแสดงให้เห็นว่าต้นกำเนิดสามารถระบุ.)
การขยายส่วนของเส้นตรงอาจเป็นเรื่องยุ่งยาก แต่สำหรับพื้นที่ใกล้เคียงแบบนูนเราสามารถสร้างการขยายได้โดยการรวมกันของการเจือจางจุดปลายทั้งสองพร้อมกับสี่เหลี่ยมด้านขนานที่ถูกเลือกอย่างระมัดระวัง (เพื่อประโยชน์ของพื้นที่ฉันจะไม่หยุดชั่วคราวเพื่อพิสูจน์การยืนยันทางคณิตศาสตร์เช่นนี้ แต่สนับสนุนให้ผู้อ่านพยายามพิสูจน์ตัวเองเพราะเป็นการออกกำลังกายที่ลึกซึ้ง) นี่คือภาพประกอบที่ใช้สามส่วน (แสดงเป็นสีชมพู) พวกเขามีหน่วยรัศมีและมุมของพวกเขาจะได้รับในชื่อเรื่อง ส่วนของเส้นตรงนั้นมีความยาว 2 เป็นแนวนอนและแสดงเป็นสีดำ:
สี่เหลี่ยมด้านขนานจะพบได้โดยการตั้งจุดสีชมพูที่ไกลที่สุดเท่าที่เป็นไปได้จากส่วนในทิศทางแนวตั้งเท่านั้น สิ่งนี้จะให้จุดที่ต่ำกว่าสองจุดและจุดบนสองจุดตามเส้นที่ขนานกับส่วน เราแค่ต้องรวมสี่จุดเป็นสี่เหลี่ยมด้านขนาน (แสดงด้วยสีน้ำเงิน) โปรดสังเกตว่าทางด้านขวามันมีความหมายอย่างไรแม้ว่าเซกเตอร์จะเป็นแค่ส่วนของเส้นตรง (และไม่ใช่รูปหลายเหลี่ยมที่แท้จริง): ที่นั่นทุกจุดในเซ็กเมนต์ได้รับการแปลในทิศตะวันออกของทิศเหนือ 171 องศาเป็นระยะทาง จาก 0 ถึง 1 ชุดของจุดสิ้นสุดเหล่านี้คือสี่เหลี่ยมด้านขนานที่แสดง รายละเอียดของการคำนวณนี้ปรากฏในbuffer
ฟังก์ชั่นที่กำหนดไว้dilate.edges
ในรหัสด้านล่าง
ในการขยายโพลีไลน์เราได้รวมตัวกันของการเจือจางของคะแนนและเซกเมนต์ที่ประกอบขึ้น สองบรรทัดสุดท้ายของdilate.edges
การวนซ้ำนี้
การขยายรูปหลายเหลี่ยมต้องการรวมถึงการตกแต่งภายในของรูปหลายเหลี่ยมพร้อมกับการขยายขอบเขต (การยืนยันนี้ทำให้สมมติฐานบางอย่างเกี่ยวกับการขยายเพื่อนบ้านหนึ่งคือที่อยู่อาศัยทั้งหมดมีจุด (0,0) ซึ่งรับประกันรูปหลายเหลี่ยมรวมอยู่ในการขยายในกรณีของย่านตัวแปรก็ถือว่าการขยายตัวของการตกแต่งภายในใด ๆ จุดของรูปหลายเหลี่ยมจะไม่ขยายออกไปเกินกว่าการขยายจุดขอบเขตนี่เป็นกรณีของย่านคงที่)
ลองมาดูตัวอย่างของวิธีการใช้งานครั้งแรกกับ nonagon (เลือกที่จะเปิดเผยรายละเอียด) และวงกลม (เลือกให้ตรงกับภาพประกอบในคำถาม) ตัวอย่างจะยังคงใช้สามละแวกใกล้เคียงเดียวกัน แต่หดเป็นรัศมี 1/3
ในรูปนี้การตกแต่งภายในของรูปหลายเหลี่ยมนั้นเป็นสีเทาการขยายจุด (ส่วน) เป็นสีชมพูและการขยายขอบ (สี่เหลี่ยมด้านขนาน) เป็นสีน้ำเงิน
"วงกลม" นั้นแท้จริงแล้วเป็นเพียง 60-gon แต่มันใกล้เคียงกับวงกลมมาก
เมื่อคุณสมบัติพื้นฐานถูกแสดงด้วยคะแนน N และละแวกใกล้เคียงการขยายด้วยคะแนน M อัลกอริทึมนี้ต้องใช้ความพยายามO (N M) มันจะต้องมีการติดตามด้วยการทำให้ความยุ่งเหยิงของจุดยอดและขอบในสหภาพง่ายขึ้นซึ่งอาจต้องใช้ความพยายาม O (N M log (N M)): นั่นเป็นสิ่งที่จะขอให้ GIS ทำ เราไม่ควรที่จะเขียนโปรแกรมนั้น
ความพยายามในการคำนวณสามารถปรับปรุงเป็น O (M + N) สำหรับคุณสมบัติฐานนูน (เพราะคุณสามารถหาวิธีเดินทางไปรอบ ๆ ขอบเขตใหม่ได้โดยการรวมรายการจุดยอดที่อธิบายขอบเขตของรูปร่างสองแบบดั้งเดิมอย่างเหมาะสม) สิ่งนี้จะไม่ต้องการการทำความสะอาดใด ๆ ในภายหลัง
เมื่อละแวกการขยายอย่างช้าๆเปลี่ยนขนาดและ / หรือการวางแนวในขณะที่คุณดำเนินการรอบคุณสมบัติฐานการขยายของขอบสามารถประมาณได้อย่างใกล้ชิดจากตัวเรือนูนของการรวมกันของจุดสิ้นสุดของจุดรวม หากทั้งสองละแวกใกล้เคียงที่มีการขยาย M1 และ M2 จุดนี้สามารถพบได้กับ O (M1 + M2) พยายามใช้อัลกอริทึมที่อธิบายไว้ใน Shamos & Preparata, การคำนวณเรขาคณิต ดังนั้นการให้ 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 (ขวา) คือหนึ่งในสี่ของวินาที อย่างไรก็ตามสิ่งนี้ส่วนใหญ่มีไว้สำหรับแสดงผล โดยทั่วไปR
รหัสนี้จะจัดการกับ N M = 1.5 ล้านต่อวินาที (ใช้เวลาเพียง 0.002 วินาทีหรือมากกว่านั้นเพื่อทำการคำนวณตัวอย่างทั้งหมดที่แสดง) อย่างไรก็ตามการปรากฏตัวของผลิตภัณฑ์ M N หมายถึงการขยายตัวของตัวเลขจำนวนมากหรือตัวเลขที่ซับซ้อนผ่านพื้นที่ใกล้เคียงที่มีรายละเอียดอาจใช้เวลานานดังนั้นระวัง! เกณฑ์มาตรฐานเวลาสำหรับปัญหาที่มีขนาดเล็กก่อนที่จะแก้ปัญหาใหญ่ ในกรณีดังกล่าวหนึ่งอาจมองไปที่การแก้ปัญหาแรสเตอร์-based (ซึ่งเป็นมากง่ายต่อการใช้ต้องใช้หลักเพียงการคำนวณเขตเดียว.)
มันค่อนข้างกว้าง แต่คุณสามารถ: