กำลังพิจารณาหลุม / ข้อ จำกัด ในการสร้างรูปหลายเหลี่ยม Voronoi ใน QGIS?


12

ฉันกำลังพยายามสร้างรูปหลายเหลี่ยม voronoi ใน QGIS ที่จะพิจารณา "หลุม" ในโดเมนทั่วไป ตัวอย่างจะเป็น:

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

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

ปัญหาสองประการเกิดขึ้นที่นี่:

  1. ฟังก์ชั่น "ความแตกต่าง" ไม่ทำงานอย่างถูกต้อง 100% โดยมีขอบเขตรูปหลายเหลี่ยมบางส่วนขยายเข้าไปใน "หลุม" สิ่งนี้สามารถแก้ไขได้โดยการค้นหาแถวในตารางแอตทริบิวต์ซึ่งไม่มีหมายเลขโพลีกอน (หรือ ID ของ "0")

  2. "การเจาะรู" หลังจากนั้นความจริงประเภทนี้อาจส่งผลให้เกิดรูปหลายเหลี่ยมที่ไม่ต่อเนื่องดังที่แสดงโดยลูกศรสีแดงในภาพ

คำถามของฉันคือ: มีเครื่องมือหรือปลั๊กอิน Voronoi ที่สามารถพิจารณาการปรากฏตัวของ "หลุม" ในใจกลางของโดเมนเป็นกระบวนการขั้นตอนเดียวและยังกำจัดการสร้างรูปหลายเหลี่ยมไม่ต่อเนื่อง? ฉันจินตนาการว่าเครื่องมือดังกล่าวจะขยายขอบเขตของรูปหลายเหลี่ยมไปยังจุดตัดที่ใกล้ที่สุดพร้อมกับขอบเขตอื่นยกเว้นว่าจะมีการต่อยกับเขต "หลุม" เป็นครั้งแรก


นี้จะคล้ายกับ แต่ตรงข้ามของ (ฉันคิด) โดยใช้หน้ากากสภาพแวดล้อมใน ArcGIS ซึ่งจะช่วยให้คุณ จำกัด รูปหลายเหลี่ยมที่สร้างขึ้นภายในขอบเขตที่เฉพาะเจาะจง อย่างไรก็ตามฉันไม่ทราบถึงเครื่องมือใด ๆ ที่จะใช้ประโยชน์จากขอบเขต / หลุมที่ซับซ้อน (แม้ว่าใน ArcGIS อาจจะเป็นหน้ากากที่ซับซ้อน - ฉันยังไม่ได้ทดสอบและฉันอาจลองอีกครั้งในภายหลังถ้าฉันมีเวลา)
Chris W

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

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

1
ฉันจะทำให้แน่ใจว่าเรขาคณิต voronoi นั้นถูกต้องโดยใช้ v.clean แล้วตรวจสอบเรขาคณิต สุดท้ายเรียกใช้ความแตกต่างเพื่อสร้างหลุม
klewis

Voronoi เกี่ยวกับหลุมเหล่านี้คืออะไร? คุณไม่ต้องการที่จะเจาะรูอย่างหมดจดเหรอ? ทำไมเลเยอร์รูปหลายเหลี่ยมจะไม่ทำล่ะ
mdsumner

คำตอบ:


3

อาจเป็นไปได้โดยใช้ rasters ก่อนอื่นให้แปลงคะแนนและรูปหลายเหลี่ยมขอบเขตของคุณเป็นแรสเตอร์ความละเอียดสูง r.maskตั้งหน้ากากสำหรับขอบเขตของคุณโดยใช้ จากนั้นเรียกใช้ในสนามหญ้าและใช้r.grow.distance Value= outputสิ่งนี้จะให้คุณสำหรับแต่ละพิกเซลซึ่งเป็นจุดที่ใกล้ที่สุด แปลงกลับเป็นเวกเตอร์รูปหลายเหลี่ยม อาจมีขั้นตอนพิเศษที่จำเป็นในการกำจัดรูปหลายเหลี่ยมที่มีเศษไม้


2

เป็นไปได้อย่างแน่นอนกับ rasters

ภาพหน้าจอนี้หวังว่าจะแสดงปัญหาได้ชัดเจนยิ่งขึ้น ส่วน B ของโวโรนอยนั้นอยู่ใกล้กับ 'เหมือนนกกาบิน' ไปยังศูนย์โวโรโนอิดั้งเดิม, แต่สิ่งนี้ไม่ได้คำนึงถึงความจริงที่ว่ามันต้องใช้เวลานานกว่าจะเดินไปรอบ ๆ อาคาร. ความเข้าใจในคำถามของ OP คือ voronoi ต้องพิจารณาระยะทางพิเศษนี้เพื่อเดินไปรอบ ๆ อาคาร

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

ฉันชอบคำแนะนำจาก @Guillaume อย่างไรก็ตามเมื่อฉันลองมันฉันมีปัญหาในการr.grow.distanceให้เกียรติหน้ากาก (ดูด้านล่างระลอกไม่ควรผ่านอาคาร)

ความรู้หญ้าของฉันไม่แข็งแรงเท่าที่ควรดังนั้นฉันอาจจะทำอะไรที่โง่ แน่นอนตรวจสอบคำแนะนำนั้นก่อนเพราะมันจะทำงานน้อยกว่าของฉัน ;-)

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

ขั้นตอนที่ 1 - สร้างพื้นผิวต้นทุน

ขั้นตอนแรกคือการสร้างพื้นผิวต้นทุน สิ่งนี้จะต้องทำเพียงครั้งเดียว

  • สร้างเลเยอร์ที่สามารถแก้ไขได้หลุมและทั้งหมด
  • เพิ่มเขตข้อมูลที่เรียกว่า 'หน่วย' ตั้งเป็น 1
  • ใช้รูปหลายเหลี่ยมต่อแรสเตอร์บนเลเยอร์เวกเตอร์ "punched out" ของคุณ (อันที่มีรู) โดยใช้ฟิลด์ 'หน่วย' ขณะนี้คุณมีเลเยอร์ "หน้ากาก" โดยที่ 1 คือพื้นที่ว่างและ 0 กำลังสร้าง
  • ใช้เครื่องคิดเลขแรสเตอร์เพื่อเปลี่ยนสิ่งนี้ให้เป็นพื้นผิวต้นทุน ฉันจะตั้ง 'กลางแจ้ง' เป็น 1 และ 'ในบ้าน' ถึง 9999 ซึ่งจะทำให้การเคลื่อนย้ายผ่านอาคารเป็นเรื่องยาก

    (( "หน้ากาก @ 1" = 1) * 1) + (( "หน้ากาก @ 1" = 0) * 9999)

คุณสามารถได้ผลลัพธ์ 'อินทรีย์' มากขึ้นโดยการเพิ่มเสียงเล็กน้อยลงบนพื้นผิวของค่าใช้จ่าย (เช่นใช้หมายเลขสุ่มจาก 1 ถึง 3 แทนที่จะเป็น 1 สำหรับ pxiels กลางแจ้ง)

ขั้นตอนที่ 2 สร้าง raster ต้นทุนสะสมสำหรับแต่ละศูนย์ voronoi

ตอนนี้เราสามารถเรียกใช้ (สำหรับ voronoi ครั้งละหนึ่งเซลล์) อัลกอริทึม GRASS r.cost.coordinatesเทียบกับชั้นผิวต้นทุนของเรา

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

ผลลัพธ์แสดงให้เห็นเส้นเวลาการเดินทางที่เท่ากันจากศูนย์โวโรโนอิหนึ่งศูนย์ สังเกตว่ามีวงพันรอบอาคารอย่างไร

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

ไม่แน่ใจว่าจะทำให้สิ่งนี้ดีที่สุดอย่างไร อาจกำลังประมวลผลโหมดแบตช์หรือดำเนินการใน pyqgis

ขั้นตอนที่ 3. รวม rasters

นี่อาจจะต้องใช้รหัส ขั้นตอนวิธีจะเป็น

create a raster 'A' to match the size of your cumulative cost images
fill raster 'A' with a suitably high number e.g. 9999
create an array of the same size as the raster.
for each cumulative cost raster number 1..N
    for each cell in image
        if cell < value in raster 'A'
            set value in raster 'A' to cell value
            set corresponding cell in array to cum. cost image number
write out array as a raster

วิธีการนั้นควรให้แรสเตอร์ที่แต่ละเซลล์ถูกจัดหมวดหมู่ตามศูนย์โวโรนอยซึ่งใกล้เคียงที่สุดโดยคำนึงถึงอุปสรรค

จากนั้นคุณสามารถใช้แรสเตอร์ต่อรูปหลายเหลี่ยม จากนั้นคุณสามารถใช้ปลั๊กอินGeneralizeเพื่อลบเอฟเฟกต์ "ขั้นตอน" ออกจากแรสเตอร์

ขอโทษสำหรับความคลุมเครือในขั้นตอนที่ 2 และ 3 ... ฉันหวังว่าบางคนจะตีระฆังด้วยวิธีที่หรูหรากว่า :)


1
ขอบคุณสตีเว่นฉันมีงาน GRASS raster แล้ว แต่ฉันก็หวังว่าจะได้คำตอบที่ดีกว่าดังที่ได้กล่าวไว้ในคำอธิบายของรางวัล
underdark

0

หมายเหตุ # 1 : ฉันไม่สามารถทำซ้ำปัญหาที่เสนอเนื่องจากเครื่องมือความแตกต่างทำงานได้ดีสำหรับฉันในการทดสอบหลายอย่างที่ฉันทำ (อาจเป็นเพราะเรขาคณิตที่เรียบง่ายของปัญหาหรือเพราะเครื่องมือได้รับการปรับปรุงตั้งแต่คำถามคือ ถามเมื่อ 1 ปีก่อน)

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

  1. ชั้นรูปหลายเหลี่ยมเวกเตอร์ที่แสดงรูปหลายเหลี่ยม Voronoi;
  2. เลเยอร์เวกเตอร์รูปหลายเหลี่ยมที่แสดงถึงหลุม / ข้อ จำกัด ซึ่งจำเป็นต้องแยกออกจากการวิเคราะห์

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

หมายเหตุ # 2 : เนื่องจากฉันไม่ต้องการใช้เครื่องมือความแตกต่างฉันจึงไม่สามารถหลีกเลี่ยงการสร้าง "slivers" (ดูแล้ว) ดังนั้นฉันจึงจำเป็นต้องเรียกใช้v.cleanเครื่องมือเพื่อกำจัดพวกเขา นอกจากนี้ @Chris W ยังกล่าวอีกว่า

[... ] แต่สิ่งที่สองที่ทำให้เกิด slivers นั้นไม่ได้คาดไม่ถึงเลย - หลังจากนั้นพื้นที่นั้นก็จะยังคงถูกจัดสรรให้อยู่ในจุดเดียวกันแม้ว่าจะมีรูอยู่ก็ตาม คุณสามารถใช้วิธีการที่แล้วรวมเศษเข้าไปในประเทศเพื่อนบ้านของพวกเขาด้วยวิธีการทำความสะอาด

หลังจากสถานที่ที่จำเป็นเหล่านี้ฉันโพสต์รหัสของฉัน:

##Voronoi_Polygons=vector polygon
##Constraints=vector polygon
##Voronoi_Cleaned=output vector

from qgis.core import *

voronoi = processing.getObject(Voronoi_Polygons)
crs = voronoi.crs().toWkt()
ex = voronoi.extent()
extent = '%f,%f,%f,%f' % (ex.xMinimum(), ex.xMaximum(), ex.yMinimum(), ex.yMaximum())

constraints = processing.getObject(Constraints)

# Create the output layer
voronoi_mod = QgsVectorLayer('Polygon?crs='+ crs, 'voronoi' , 'memory')
prov = voronoi_mod.dataProvider()
fields = voronoi.pendingFields() # Fields from the input layer
prov.addAttributes(fields) # Add input layer fields to the outLayer
voronoi_mod.updateFields()

# Spatial index containing all the 'constraints'
index_builds = QgsSpatialIndex()
for feat in constraints.getFeatures():
    index_builds.insertFeature(feat)

final_geoms = {}
final_attrs = {}

for feat in voronoi.getFeatures():
    input_geom = feat.geometry()
    input_attrs = feat.attributes()
    final_geom = []
    multi_geom = input_geom.asPolygon()
    input_geoms = [] # edges of the input geometry
    for k in multi_geom:
        input_geoms.extend(k)
    final_geom.append(input_geoms)
    idsList = index_builds.intersects(input_geom.boundingBox())
    mid_geom = [] # edges of the holes/constraints
    if len(idsList) > 0:
        req = QgsFeatureRequest().setFilterFids(idsList)
        for ft in constraints.getFeatures(req):
            geom = ft.geometry()
            hole = []
            res = geom.intersection(input_geom)
            res_geom = res.asPolygon()
            for i in res_geom:
                hole.extend(i)
                mid_geom.append(hole)
        final_geom.extend(mid_geom)
    final_geoms[feat.id()] = final_geom
    final_attrs[feat.id()] = input_attrs

# Add the features to the output layer
outGeom = QgsFeature()
for key, value in final_geoms.iteritems():
    outGeom.setGeometry(QgsGeometry.fromPolygon(value))
    outGeom.setAttributes(final_attrs[key])
    prov.addFeatures([outGeom])

# Add 'voronoi_mod' to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(voronoi_mod)

# Run 'v.clean'
processing.runalg("grass7:v.clean",voronoi_mod, 2, 0.1, extent, -1, 0.0001, Voronoi_Cleaned, None)

# Remove 'voronoi_mod' to the Layers panel
QgsMapLayerRegistry.instance().removeMapLayer(voronoi_mod)

ซึ่งนำไปสู่ผลลัพธ์นี้:

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

เพื่อความชัดเจนนี่จะเป็นผลลัพธ์โดยไม่ต้องใช้v.cleanเครื่องมือ:

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

ความแตกต่างกับผลที่ได้จาก @LeaningCactus คือว่าตอนนี้รูปทรงเรขาคณิตที่จะไม่เสียและพวกเขาจะได้รับการ "ทำความสะอาด" โดยไม่มีข้อผิดพลาด


ทำให้หลุมยาวขึ้นเช่นตัดผ่านแผนที่ทั้งหมดเช่นแม่น้ำและคุณจะเห็นปัญหา การเพิ่ม slivers ให้กับเพื่อนบ้านสร้างรูปหลายเหลี่ยมที่มีลักษณะแตกต่างจากแผนภาพ Voronoi ที่มีข้อ จำกัด ที่เหมาะสม ฉันลองมัน
underdark

ขออภัยฉันไม่เข้าใจ: คุณพบข้อผิดพลาดในผลลัพธ์หรือไม่ ฉันทดสอบโค้ดสำหรับกรณีที่รูปหลายเหลี่ยมนั้นคล้ายกับรหัสที่เสนอในคำถามเท่านั้น
mgri

ไม่สามารถทดสอบรหัสได้ในตอนนี้โชคไม่ดี แต่คุณสามารถแสดงผลลัพธ์ที่ได้จากการเปลี่ยนแปลงของหลุมที่ร่างในi.stack.imgur.com/Jpfra.png ได้หรือไม่?
underdark

ถ้าผมขยายข้อ จำกัด ถึงคุณลักษณะทางด้านขวาที่ผมได้รับนี้ แต่ถ้าผมย้ายโดยตรง จำกัด ที่ผมได้รับนี้
mgri

สามเหลี่ยมเล็ก ๆ ที่ลูกศรสีแดงในรูปวาดของฉันชี้ไปที่เป็นปัญหา มันไม่ควรจะอยู่ตรงนั้น แต่มันก็อยู่ในผลลัพธ์ของคุณด้วย ดูเหมือนว่าวิธีการนี้จะช่วยแก้ปัญหา # 1 ของคำถาม แต่ไม่ได้รับการแก้ไข # 2
underdark
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.