ปรับรูปหลายเหลี่ยมให้เรียบลงในแผนที่รูปร่าง


52

นี่คือแผนที่รูปร่างที่มีรูปหลายเหลี่ยมหลายระดับ

ให้ถามวิธีทำให้รูปหลายเหลี่ยมทำให้จุดยอดทั้งหมดคงอยู่ในตำแหน่งที่แน่นอนได้อย่างไร

จริง ๆ แล้วรูปร่างนั้นถูกสร้างขึ้นบนข้อมูลกริดคุณอาจแนะนำให้ปรับข้อมูลกริดให้เรียบและดังนั้นผลลัพธ์ที่ได้จะราบรื่นขึ้น โปรดทราบว่านี่ไม่ทำงานตามที่ฉันต้องการเนื่องจากฟังก์ชั่นการปรับให้เรียบเช่นตัวกรองแบบเกาส์จะลบชุดข้อมูลขนาดเล็กและจะเปลี่ยนช่วงของตัวแปรที่สามเช่นความสูงที่ไม่ได้รับอนุญาตในแอปพลิเคชันของฉัน

อันที่จริงฉันกำลังมองหาชิ้นส่วนของรหัส (โดยเฉพาะอย่างยิ่งในPython ) ซึ่งสามารถทำการปรับให้เรียบของรูปหลายเหลี่ยม 2 มิติ (ชนิดใดก็ได้: นูนเว้าการตัดกันด้วยตนเองเป็นต้น) ไม่เจ็บปวดพอสมควร (ลืมหน้ารหัส) และแม่นยำ

FYI มีฟังก์ชั่นใน ArcGISที่ทำสิ่งนี้ได้อย่างสมบูรณ์แบบ แต่การใช้แอพพลิเคชั่นเพื่อการค้าของบุคคลที่สามไม่ใช่ตัวเลือกของฉันสำหรับคำถามนี้

ป้อนคำอธิบายรูปภาพที่นี่


1)

Scipy.interpolate:

ป้อนคำอธิบายรูปภาพที่นี่

ในขณะที่คุณเห็นเส้นโค้งที่เกิด (สีแดง) ไม่เป็นที่พอใจ!

2)

นี่คือผลลัพธ์โดยใช้รหัสที่ให้ไว้ในที่นี้ มันใช้งานไม่ได้!

ป้อนคำอธิบายรูปภาพที่นี่

3)

สำหรับฉันทางออกที่ดีที่สุดควรเป็นสิ่งต่อไปนี้ซึ่งเป็นรูปสี่เหลี่ยมจัตุรัสที่ถูกปรับให้เรียบโดยค่อยๆเปลี่ยนค่าเพียงค่าเดียว ฉันหวังว่าแนวคิดที่คล้ายกันเพื่อทำให้รูปหลายเหลี่ยมเรียบขึ้น

ป้อนคำอธิบายรูปภาพที่นี่

ตอบสนองเงื่อนไขที่เส้นโค้งผ่านจุด:

ป้อนคำอธิบายรูปภาพที่นี่

4)

นี่คือการดำเนินการของฉัน "ความคิดของคนจรจัด" ทีละบรรทัดในPythonกับข้อมูลของเขา อาจมีข้อบกพร่องบางอย่างเนื่องจากผลลัพธ์ไม่ดี

ป้อนคำอธิบายรูปภาพที่นี่

K = 2 เป็นหายนะดังนั้น k> = 4

5)

ฉันลบจุดหนึ่งไปยังตำแหน่งที่มีปัญหาและตอนนี้เดือยที่เกิดขึ้นนั้นเหมือนกับของ whuber's แต่ก็ยังคงมีคำถามว่าทำไมวิธีการนี้จึงใช้ไม่ได้กับทุกกรณี

ป้อนคำอธิบายรูปภาพที่นี่

6)

การปรับให้เรียบของข้อมูลของ whuber สามารถทำได้ดังต่อไปนี้ (วาดโดยซอฟต์แวร์กราฟิกแบบเวกเตอร์) ซึ่งมีการเพิ่มจุดพิเศษอย่างราบรื่น (เปรียบเทียบกับการอัปเดต

4):

ป้อนคำอธิบายรูปภาพที่นี่

7)

ดูผลลัพธ์จากโค้ดของ whuber ของ Python สำหรับรูปร่างที่เป็นสัญลักษณ์:

ป้อนคำอธิบายรูปภาพที่นี่
โปรดทราบว่าวิธีนี้ใช้ไม่ได้กับ polylines สำหรับมุมสีเขียว (รูปร่าง) เป็นสิ่งที่ฉันต้องการ แต่มีสีแดง สิ่งนี้จำเป็นต้องได้รับการแก้ไขเนื่องจากแผนที่รูปร่างมักจะมีโพลีนอยู่เสมอถึงแม้ว่าโพลีลีนแบบปิดจะถือว่าเป็นรูปหลายเหลี่ยมเหมือนในตัวอย่างของฉัน ยังไม่ได้ที่ปัญหาที่เกิดขึ้นในการปรับปรุง 4 ยังไม่ได้รับการแก้ไข

8) [ล่าสุดของฉัน]

นี่คือทางออกสุดท้าย (ไม่สมบูรณ์แบบ!):

ป้อนคำอธิบายรูปภาพที่นี่

จำไว้ว่าคุณจะต้องทำอะไรบางอย่างเกี่ยวกับพื้นที่ที่ดาวชี้ อาจมีข้อผิดพลาดในรหัสของฉันหรือวิธีที่เสนอต้องการการพัฒนาเพิ่มเติมเพื่อพิจารณาสถานการณ์ทั้งหมดและเพื่อให้ผลลัพธ์ที่ต้องการ


คุณสร้างรูปร่าง 'รูปหลายเหลี่ยม' อย่างไร พวกเขาจะไม่เป็นเส้นเสมอหรือไม่เนื่องจากรูปร่างที่ตัดกับขอบของ DEM จะไม่เข้าใกล้ตัวเองหรือ
pistachionut

ฉันใช้ฟังก์ชั่น v.generalize ใน GRASS เพื่อทำให้เส้นชั้นความสูงเรียบขึ้นด้วยผลลัพธ์ที่ดีแม้ว่ามันอาจใช้เวลาสักครู่สำหรับแผนที่ที่มีรูปทรงที่หนาแน่นมาก
pistachionut

@pistachionut คุณอาจพิจารณาว่าระดับเส้นชั้นความสูงเป็นเส้นโพลี ฉันกำลังมองหารหัสบริสุทธิ์ในระยะแรก ถ้าไม่พร้อมใช้งานแพ็กเกจแบบเบาสำหรับ Python
นักพัฒนา

อาจดูscipy.org/Cookbook/Interpolationเพราะดูเหมือนว่าคุณต้องการที่จะโค้ง
PolyGeo

1
@Pablo Bezier โค้งในลิงค์ของคุณทำงานได้ดีสำหรับ polylines Whuber ทำงานได้ดีกับรูปหลายเหลี่ยม ดังนั้นพวกเขาจึงสามารถตอบคำถามได้ ขอบคุณมากสำหรับการแบ่งปันความรู้ของคุณได้ฟรี
นักพัฒนา

คำตอบ:


37

วิธีการส่วนใหญ่ในการเรียงลำดับของตัวเลขจะเป็นรูปหลายเหลี่ยม เคล็ดลับคือการทำให้เส้นโค้ง "ปิด" อย่างราบรื่นที่จุดสิ้นสุด เมื่อต้องการทำเช่นนี้ให้ "ตัด" จุดยอดที่อยู่รอบ ๆ ปลาย จากนั้นให้อิสระกับพิกัด x และ y- แยกกัน

Rนี่เป็นตัวอย่างการทำงานใน มันใช้splineขั้นตอนลูกบาศก์เริ่มต้นที่มีอยู่ในแพคเกจสถิติพื้นฐาน สำหรับการควบคุมที่มากขึ้นให้แทนที่กระบวนการเกือบทุกอย่างที่คุณต้องการ: ตรวจสอบให้แน่ใจว่ามันคั่นด้วยตัวเลข (นั่นคือสอดแทรกพวกมัน) แทนที่จะใช้มันเป็น "จุดควบคุม"

#
# Splining a polygon.
#
#   The rows of 'xy' give coordinates of the boundary vertices, in order.
#   'vertices' is the number of spline vertices to create.
#              (Not all are used: some are clipped from the ends.)
#   'k' is the number of points to wrap around the ends to obtain
#       a smooth periodic spline.
#
#   Returns an array of points. 
# 
spline.poly <- function(xy, vertices, k=3, ...) {
    # Assert: xy is an n by 2 matrix with n >= k.

    # Wrap k vertices around each end.
    n <- dim(xy)[1]
    if (k >= 1) {
        data <- rbind(xy[(n-k+1):n,], xy, xy[1:k, ])
    } else {
        data <- xy
    }

    # Spline the x and y coordinates.
    data.spline <- spline(1:(n+2*k), data[,1], n=vertices, ...)
    x <- data.spline$x
    x1 <- data.spline$y
    x2 <- spline(1:(n+2*k), data[,2], n=vertices, ...)$y

    # Retain only the middle part.
    cbind(x1, x2)[k < x & x <= n+k, ]
}

เพื่อแสดงให้เห็นถึงการใช้งานขอสร้างรูปหลายเหลี่ยมขนาดเล็ก (แต่ซับซ้อน)

#
# Example polygon, randomly generated.
#
set.seed(17)
n.vertices <- 10
theta <- (runif(n.vertices) + 1:n.vertices - 1) * 2 * pi / n.vertices
r <- rgamma(n.vertices, shape=3)
xy <- cbind(cos(theta) * r, sin(theta) * r)

Spline โดยใช้รหัสก่อนหน้า หากต้องการทำให้เส้นโค้งเรียบเนียนขึ้นให้เพิ่มจำนวนจุดยอดจาก 100 เพื่อทำให้ราบรื่นน้อยลงลดจำนวนจุดยอด

s <- spline.poly(xy, 100, k=3)

หากต้องการดูผลลัพธ์เราได้พล็อต (a) รูปหลายเหลี่ยมต้นฉบับเป็นสีแดงประซึ่งแสดงช่องว่างระหว่างจุดยอดหนึ่งและจุดสุดท้าย และ (b) เส้นโค้งเป็นสีเทาอีกครั้งแสดงช่องว่าง (เนื่องจากช่องว่างเล็กมากจุดสิ้นสุดจึงถูกเน้นด้วยจุดสีน้ำเงิน)

plot(s, type="l", lwd=2, col="Gray")
lines(xy, col="Red", lty=2, lwd=2)
points(xy, col="Red", pch=19)
points(s, cex=0.8)
points(s[c(1,dim(s)[1]),], col="Blue", pch=19)

รูปหลายเหลี่ยมเชิงเส้น


5
คำตอบที่ดี มีวิธีใดที่จะรับประกันว่ารูปทรงจะไม่จบลงเนื่องจากการทำให้เรียบหรือไม่?
Kirk Kuykendall

นั่นเป็นคำถามที่ดี @Kirk ฉันไม่ได้ตระหนักถึงวิธีการใด ๆ ที่จะรับประกันว่าการข้ามจากรูปแบบนี้จะไม่ราบรื่น (อันที่จริงแล้วฉันไม่เห็นด้วยว่าจะรับประกันได้อย่างไรว่าโพลีไลน์สมูทเป็นแบบไม่ตัดกันตัวเอง แต่นี่ไม่ใช่ปัญหาใหญ่สำหรับรูปทรงส่วนใหญ่) เพื่อที่จะทำเช่นนั้นคุณจะต้องกลับไปที่ต้นฉบับ DEM และแทนที่จะใช้วิธีที่ดีกว่าในการคำนวณรูปทรงตั้งแต่แรก (มีเป็นวิธีการที่ดี - พวกเขาได้รับการรู้จักกันมาเป็นเวลานาน - แต่ AFAIK บางส่วนของ GISes ที่นิยมมากที่สุดไม่ได้ใช้พวกเขา.)
whuber

ก่อนอื่นฉันยังคงพยายามใช้คำตอบของคุณใน Python แต่ก็ไม่ประสบความสำเร็จ ประการที่สองสิ่งที่จะเป็นผลถ้าคุณใช้วิธีการของคุณในตาราง? คุณอาจหมายถึงคนที่ฉันวาดในคำถาม
นักพัฒนา

1
ฉันยอมรับว่านี่เป็นคำตอบเพราะมันให้ทางออกที่ดี แม้ว่ามันจะไม่สมบูรณ์แบบ แต่มันก็ทำให้ฉันมีความคิดบางอย่างที่ทำงานรอบ ๆ หวังว่าฉันจะหาทางออกที่ตรงกับจุดที่ฉันกล่าวข้างต้นในคำถามและความคิดเห็นของฉัน คุณอาจพิจารณาความเห็นของ whuber สำหรับคำถาม [QC] มีเทคนิคที่ดี สุดท้ายนี้ฉันควรจะบอกว่าการแปลเป็นภาษาไพ ธ อนตรงไปตรงมาเมื่อติดตั้งแพ็คเกจ Scipy ที่น่ารัก พิจารณาความคิดเห็นของ Pablo ใน QC เป็นวิธีแก้ปัญหาที่เป็นไปได้สำหรับ polylines เช่น Bezier curves โชคดีนะทุกคน.
นักพัฒนา

1
เห็นคำตอบของคุณฉันเสียใจที่ไม่ได้ดูแลคณิตศาสตร์ของฉันดี !!!
vinayan

2

ฉันรู้ว่านี่เป็นโพสต์เก่า แต่ปรากฏใน Google สำหรับสิ่งที่ฉันกำลังมองหาดังนั้นฉันคิดว่าฉันจะโพสต์ทางออกของฉัน

ฉันไม่เห็นว่านี่เป็นแบบฝึกหัดการปรับเส้นโค้งแบบ 2D แต่เป็นแบบ 3D โดยการพิจารณาข้อมูลเป็น 3D เราสามารถมั่นใจได้ว่าเส้นโค้งไม่เคยข้ามกันและสามารถใช้ข้อมูลจากรูปทรงอื่น ๆ เพื่อปรับปรุงการประมาณการของเราสำหรับปัจจุบัน

สารสกัด iPython ต่อไปนี้ใช้การแก้ไขลูกบาศก์โดย SciPy โปรดทราบว่าค่า z ที่ฉันได้พล็อตแล้วนั้นไม่สำคัญตราบใดที่รูปทรงทั้งหมดมีความสูงเท่ากัน

In [1]: %pylab inline
        pylab.rcParams['figure.figsize'] = (10, 10)
        Populating the interactive namespace from numpy and matplotlib

In [2]: import scipy.interpolate as si

        xs = np.array([0.0, 0.0, 4.5, 4.5,
                       0.3, 1.5, 2.3, 3.8, 3.7, 2.3,
                       1.5, 2.2, 2.8, 2.2,
                       2.1, 2.2, 2.3])
        ys = np.array([0.0, 3.0, 3.0, 0.0,
                       1.1, 2.3, 2.5, 2.3, 1.1, 0.5,
                       1.1, 2.1, 1.1, 0.8,
                       1.1, 1.3, 1.1])
        zs = np.array([0,   0,   0,   0,
                       1,   1,   1,   1,   1,   1,
                       2,   2,   2,   2,
                       3,   3,   3])
        pts = np.array([xs, ys]).transpose()

        # set up a grid for us to resample onto
        nx, ny = (100, 100)
        xrange = np.linspace(np.min(xs[zs!=0])-0.1, np.max(xs[zs!=0])+0.1, nx)
        yrange = np.linspace(np.min(ys[zs!=0])-0.1, np.max(ys[zs!=0])+0.1, ny)
        xv, yv = np.meshgrid(xrange, yrange)
        ptv = np.array([xv, yv]).transpose()

        # interpolate over the grid
        out = si.griddata(pts, zs, ptv, method='cubic').transpose()

        def close(vals):
            return np.concatenate((vals, [vals[0]]))

        # plot the results
        levels = [1, 2, 3]
        plt.plot(close(xs[zs==1]), close(ys[zs==1]))
        plt.plot(close(xs[zs==2]), close(ys[zs==2]))
        plt.plot(close(xs[zs==3]), close(ys[zs==3]))
        plt.contour(xrange, yrange, out, levels)
        plt.show()

ลูกบาศก์สอดแทรกผลลัพธ์

ผลลัพธ์ที่นี่ไม่ได้ดูดีที่สุด แต่มีจุดควบคุมเพียงไม่กี่แห่งที่ยังคงใช้ได้อย่างสมบูรณ์ สังเกตว่าดึงเส้นที่ติดตั้งสีเขียวออกมาเพื่อให้ตามเส้นขอบสีฟ้าที่กว้างขึ้นอย่างไร


เส้นโค้งเรียบที่ประกอบเข้าด้วยกันควรอยู่ใกล้กับรูปหลายเหลี่ยม / รูปหลายเหลี่ยมดั้งเดิม
นักพัฒนา

1

ผมเขียนเกือบตรงแพคเกจที่คุณกำลังมองหา ... แต่มันก็เป็นใน Perl และถูกกว่าทศวรรษที่ผ่านมา: GD :: Polyline มันใช้เส้นโค้ง 2 มิติลูกบาศก์ Bezier และจะ "ราบรื่น" รูปหลายเหลี่ยมโดยพลการหรือ "Polyline" (ชื่อของฉันแล้วสำหรับสิ่งที่เรียกกันทั่วไปว่า "LineString")

อัลกอริทึมนั้นมีสองขั้นตอน: กำหนดจุดในรูปหลายเหลี่ยมเพิ่มจุดควบคุม Bezier สองจุดระหว่างทุกจุด จากนั้นเรียกอัลกอริธึมแบบง่าย ๆ เพื่อทำการประมาณค่าของเส้นโค้ง

ส่วนที่สองนั้นง่าย ส่วนแรกเป็นบิตของศิลปะ นี่เป็นความเข้าใจที่: พิจารณา "ส่วนการควบคุม" จุดสุดยอด vNN: ส่วนควบคุมคือจุดร่วมเชิงเส้นสามจุด: [cNa, vN, cNb]. จุดศูนย์กลางคือจุดสุดยอด ความชันของส่วนควบคุมนี้เท่ากับความชันจาก Vertex N-1 ถึง Vertex N + 1 ความยาวของส่วนด้านซ้ายของเซ็กเมนต์นี้คือ 1/3 ความยาวจาก Vertex N-1 ถึง Vertex N และความยาวของส่วนด้านขวาของเซ็กเมนต์นี้คือ 1/3 ความยาวจาก Vertex N ถึง Vertex N + 1

ถ้าเส้นโค้งเดิมเป็นสี่จุด: แล้วจุดสุดยอดในแต่ละขณะนี้ได้รับการควบคุมส่วนของรูปแบบ:[v1, v2, v3, v4] [c2a, v2, c2b]ร้อยเชือกเหล่านี้เข้าด้วยกันดังนี้: [v1, c1b, c2a, v2, c2b, c3a, v3, c3b, c4a, v4]และแทะเล็มพวกมันทีละสี่ตัวตามที่ Bezier สี่แต้ม: [v1, c1b, c2a, v2]จากนั้น[v2, c2b, c3a, v3]เป็นต้น เนื่องจาก[c2a, v2, c2b]เป็นแบบ co-linear โค้งที่เกิดขึ้นจะราบรื่นในแต่ละจุดสุดยอด

ดังนั้นสิ่งนี้จึงตรงตามความต้องการของคุณในการกำหนดพารามิเตอร์ "ความรัดกุม" ของเส้นโค้ง: ใช้ค่าที่น้อยกว่า 1/3 สำหรับเส้นโค้ง "ที่แน่นกว่า" ซึ่งเป็นเส้นโค้งที่มีขนาดใหญ่กว่าสำหรับพอดีกับ "วงกลม" ไม่ว่าในกรณีใดโค้งที่เกิดขึ้นจะผ่านจุดที่กำหนดไว้เสมอ

สิ่งนี้ส่งผลให้เกิดเส้นโค้งเรียบที่ "จำกัด " รูปหลายเหลี่ยมดั้งเดิม ฉันมีวิธีที่จะ "จารึก" เส้นโค้งที่ราบรื่น ... แต่ฉันไม่เห็นสิ่งนั้นในรหัส CPAN

อย่างไรก็ตามฉันไม่ได้มีรุ่นที่มีใน Python และฉันไม่มีตัวเลขใด ๆ แต่ ... ถ้า / เมื่อฉันทำพอร์ตนี้กับ Python ฉันจะต้องโพสต์ที่นี่


ไม่สามารถประเมินโค้ด Perl เพิ่มกราฟิกเพื่อแสดงว่ามันทำงานอย่างไรถ้าเป็นไปได้
นักพัฒนา
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.