Xem xét các lỗ hổng / ràng buộc trong việc tạo đa giác Voronoi trong QGIS?


12

Tôi đang cố gắng tạo đa giác voronoi trong QGIS để xem xét "lỗ hổng" trong miền chung. Một ví dụ sẽ là:

nhập mô tả hình ảnh ở đây

Tôi thực sự đã tạo Voronois trong hình ảnh này bằng cách sử dụng QGIS thông qua lệnh GRASS, sau đó sử dụng công cụ "Sự khác biệt" để tạo ra các lỗ hổng. Một shapefile đa giác riêng biệt, chứa phạm vi của các lỗ, đã được sử dụng làm lớp "Sự khác biệt". Một ứng dụng ví dụ sẽ tạo ra các đa giác xung quanh các điểm lấy mẫu được thu thập giữa các cấu trúc nên được loại trừ khỏi phân tích.

Hai vấn đề phát sinh ở đây:

  1. Hàm "khác biệt" dường như không hoạt động đúng 100%, với một số ranh giới đa giác mở rộng vào các "lỗ". Điều này có thể được khắc phục bằng cách tìm hàng trong Bảng thuộc tính không có số ID đa giác (hoặc ID là "0").

  2. Kiểu "bấm lỗ" sau thực tế này có thể dẫn đến đa giác không liên tục, như được hiển thị bởi mũi tên màu đỏ trong hình ảnh.

Câu hỏi của tôi là: có công cụ hoặc plugin Voronoi nào có thể xem xét sự hiện diện của "lỗ hổng" ở trung tâm của miền, như một quá trình một bước và cũng loại bỏ việc tạo ra các đa giác không liên tục? Tôi hình dung rằng một công cụ như vậy sẽ mở rộng một ranh giới đa giác đến giao lộ gần nhất với một ranh giới khác, trừ khi ranh giới khác đó chống lại ranh giới "lỗ" trước.


Điều này sẽ tương tự nhưng ngược lại với (tôi nghĩ) khi sử dụng mặt nạ môi trường trong ArcGIS . Điều đó sẽ cho phép bạn giới hạn các đa giác được tạo trong một ranh giới cụ thể. Tuy nhiên tôi không biết bất kỳ công cụ nào sẽ sử dụng các ranh giới / lỗ hổng phức tạp (mặc dù có thể trong ArcGIS, mặt nạ có thể phức tạp đến mức đó - tôi đã không thử nó và tôi có thể thử lại sau nếu tôi có thời gian).
Chris W

Tôi đã thử nghiệm lý thuyết ArcGIS và nó sẽ không hoạt động. Theo câu hỏi được liên kết, bạn có thể giới hạn kết quả thành hình dạng bên ngoài. Tuy nhiên, một lỗ cắt trong hình dạng được bỏ qua bởi các polys kết quả. Hơn nữa, nếu lỗ đó cũng có một số điểm trong đó, công cụ sẽ bị lỗi và không chạy được. Tôi không thể giải thích vấn đề đầu tiên của bạn với sự khác biệt, nhưng kết quả thứ hai dẫn đến các phần tử không hoàn toàn bất ngờ - xét cho cùng, khu vực đó vẫn sẽ được phân bổ cho cùng một điểm ngay cả khi có lỗ hổng. Bạn có thể sử dụng phương pháp đó và sau đó kết hợp các phần tử vào hàng xóm của họ với phương pháp dọn dẹp.
Chris W

2
Bạn có khả năng có thể giải quyết điều này bằng cách đi đến raster. Với mặt nạ raster, khoảng cách Euclide sẽ đi ra khỏi các điểm của bạn cho đến khi nó chạm vào các ô đi ra từ một điểm khác hoặc raster mặt nạ của bạn (mô tả về ranh giới của bạn). Sau đó, bạn thực hiện một số dọn dẹp khu vực và vector hóa kết quả để có được đa giác.
Chris W

1
Tôi chắc chắn rằng Hình học voronoi là hợp lệ bằng cách chạy v.clean sau đó kiểm tra hình học. Cuối cùng, thực hiện Sự khác biệt để tạo ra các lỗ hổng.
klewis

Voronoi về những lỗ này là gì? Không phải bạn muốn đục lỗ một cách sạch sẽ sao? Tại sao không có lớp đa giác nào?
mdsumner

Câu trả lời:


3

Điều này có thể có thể sử dụng raster. Đầu tiên chuyển đổi điểm và đa giác ranh giới của bạn thành một raster độ phân giải cao. Đặt mặt nạ cho ranh giới của bạn bằng cách sử dụng r.mask. Sau đó, chạy r.grow.distancetrong GRASS và sử dụng Value= output. Điều này sẽ cung cấp cho bạn cho mỗi pixel, đó là điểm gần nhất. Chuyển đổi này trở lại thành đa giác vector. Có thể có thêm các bước cần thiết để thoát khỏi đa giác cúi.


2

Điều này là chắc chắn có thể với các raster.

Ảnh chụp màn hình này hy vọng cho thấy vấn đề rõ ràng hơn. Phần B của voronoi gần hơn 'khi con quạ bay' đến trung tâm voronoi ban đầu, nhưng điều này không tính đến thực tế là sẽ mất nhiều thời gian hơn để đi bộ quanh tòa nhà. Sự hiểu biết của tôi về câu hỏi của OP là voronoi cần tính đến khoảng cách thêm này để đi bộ xung quanh tòa nhà.

nhập mô tả hình ảnh ở đây

Tôi thích đề xuất từ ​​@Guillaume. Tuy nhiên, khi tôi thử nó, tôi gặp vấn đề trong việc r.grow.distancetôn vinh mặt nạ (xem bên dưới. Những gợn sóng không nên đi qua các tòa nhà).

Kiến thức về Cỏ của tôi không mạnh như nó có thể, vì vậy có lẽ tôi đang làm điều gì đó ngu ngốc. Chắc chắn, hãy kiểm tra đề xuất đó trước vì nó sẽ làm việc ít hơn nhiều so với của tôi ;-)

nhập mô tả hình ảnh ở đây

Bước 1 - Tạo bề mặt chi phí

Bước đầu tiên là tạo ra một bề mặt chi phí. Điều này chỉ cần được thực hiện một lần.

  • tạo một lớp có thể chỉnh sửa, lỗ và tất cả.
  • thêm một trường gọi là 'đơn vị', đặt nó thành 1.
  • sử dụng đa giác-raster trên lớp vectơ "đục lỗ" của bạn (lớp có lỗ), sử dụng trường 'đơn vị'. Bây giờ bạn có một lớp "mặt nạ", trong đó 1 là không gian trống và 0 đang xây dựng.
  • sử dụng máy tính raster để biến điều này thành một bề mặt chi phí. Tôi sẽ đặt 'ngoài trời' thành 1 và 'trong nhà' thành 9999. Điều này sẽ khiến việc di chuyển qua các tòa nhà trở nên khó khăn.

    (("mặt nạ @ 1" = 1) * 1) + (("mặt nạ @ 1" = 0) * 9999)

Bạn có thể nhận được nhiều kết quả 'hữu cơ' hơn bằng cách thêm một chút nhiễu vào bề mặt chi phí (ví dụ: sử dụng số ngẫu nhiên từ 1 đến 3, thay vì chỉ 1 cho các pxiels ngoài trời.)

Bước 2. Tạo các raster chi phí tích lũy cho mỗi trung tâm voronoi

Bây giờ chúng ta có thể chạy (cho một ô voronoi cùng một lúc) thuật toán GRASS r.cost.coordinateschống lại lớp bề mặt chi phí của chúng ta.

Đối với tọa độ bắt đầu, sử dụng trung tâm vornoi. Đối với tọa độ cuối, chọn một trong các góc của khu vực của bạn. Tôi đề nghị sử dụng 'Hiệp sĩ du lịch' vì điều này mang lại kết quả mượt mà hơn.

Kết quả cho thấy các dòng thời gian di chuyển bằng nhau từ một trung tâm voronoi. Lưu ý cách các dải quấn quanh các tòa nhà.

nhập mô tả hình ảnh ở đây

Không chắc chắn làm thế nào tốt nhất để tự động hóa này. Có thể xử lý chế độ hàng loạt, hoặc được thực hiện trong pyqgis.

Bước 3. Hợp nhất các trình quét

Điều này có thể sẽ cần mã. Thuật toán sẽ là

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

Cách tiếp cận đó sẽ mang lại một raster trong đó mỗi ô được phân loại bởi trung tâm voronoi gần nhất, có tính đến các trở ngại.

Sau đó, bạn có thể sử dụng raster-to-đa giác. Sau đó, bạn có thể sử dụng plugin Generalize để xóa các tạo phẩm hiệu ứng "bước" khỏi raster.

Xin lỗi vì sự mơ hồ ở bước 2 và 3 ... Tôi hy vọng ai đó sẽ hòa nhập với một giải pháp thanh lịch hơn :)


1
Cảm ơn Steven, tôi có một công việc raster GRASS đang hoạt động nhưng tôi đã hy vọng cho một giải pháp thanh lịch hơn như được đề cập trong mô tả tiền thưởng.
underdark

0

Lưu ý số 1 : Tôi không thể tái tạo vấn đề được đề xuất vì công cụ Khác biệt hoạt động tốt với tôi trong một số thử nghiệm mà tôi đã thực hiện (có thể là do hình học đơn giản của vấn đề hoặc do công cụ đã được cải thiện do câu hỏi là hỏi 1 năm trước).

Tuy nhiên, tôi đề xuất một cách giải quyết trong PyQGIS để tránh việc sử dụng công cụ Khác biệt . Mọi thứ đều dựa trên giao điểm cục bộ giữa hai lớp đầu vào (xem hình bên dưới):

  1. một lớp vectơ đa giác đại diện cho đa giác Voronoi;
  2. một lớp vectơ đa giác đại diện cho các lỗ / ràng buộc cần được loại trừ khỏi phân tích.

nhập mô tả hình ảnh ở đây

Lưu ý # 2 : Vì tôi không muốn sử dụng công cụ Khác biệt , tôi không thể tránh việc tạo ra "slivers" (xem sau đó), vì vậy tôi cần chạy v.cleancông cụ để loại bỏ chúng. Hơn nữa, như @Chris W đã nói,

[...] nhưng kết quả thứ hai trong các phần tử không hoàn toàn bất ngờ - xét cho cùng, khu vực đó vẫn sẽ được phân bổ cho cùng một điểm ngay cả khi có lỗ hổng. Bạn có thể sử dụng phương pháp đó và sau đó kết hợp các phần tử vào hàng xóm của họ với phương pháp dọn dẹp .

Sau những tiền đề cần thiết này, tôi đăng mã của mình:

##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)

dẫn đến kết quả này:

nhập mô tả hình ảnh ở đây

Chỉ cần rõ ràng, đây sẽ là kết quả mà không cần sử dụng v.cleancông cụ:

nhập mô tả hình ảnh ở đây

Sự khác biệt với kết quả của @LeaningCactus là, hiện tại, hình học không bị phá vỡ và chúng có thể được "làm sạch" mà không có lỗi .


Làm cho các lỗ dài hơn, ví dụ như cắt qua toàn bộ bản đồ, như một dòng sông, và bạn sẽ thấy vấn đề. Việc thêm các phần tử vào hàng xóm sẽ tạo ra các đa giác trông rất khác so với biểu đồ Voronoi bị ràng buộc thích hợp. Tôi đã thử nó.
underdark

Xin lỗi, tôi không hiểu: bạn có tìm thấy bất kỳ lỗi nào trong kết quả không? Tôi chỉ kiểm tra mã cho các trường hợp trong đó các đa giác tương tự như các đa giác được đề xuất trong câu hỏi.
mgri

Không thể kiểm tra mã ngay bây giờ, nhưng bạn có thể hiển thị kết quả thu được với thay đổi trong các lỗ được phác thảo trong i.stack.imgur.com/Jpfra.png không?
underdark

Nếu tôi mở rộng các ràng buộc lên đến tính năng bên phải, tôi có được điều này . Thay vào đó, nếu tôi trực tiếp di chuyển các ràng buộc, tôi có được điều này .
mgri

Hình tam giác nhỏ mà mũi tên màu đỏ trong bản vẽ của tôi chỉ ra là vấn đề. Nó không nên ở đó nhưng nó cũng nằm trong kết quả của bạn. Có vẻ như cách tiếp cận này giải quyết vấn đề # 1 của câu hỏi nhưng không giải quyết được # 2.
underdark
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.