Tạo điểm trong nhiều tính năng chiếm khoảng cách tối thiểu vào tài khoản


8

Tôi có một chức năng tạo ra Tua bin gió được biểu thị như các điểm. Về cơ bản, nó sử dụng mã từ các điểm Ngẫu nhiên bên trong công cụ đa giác (cố định) mặc dù có một số thay đổi nhỏ.

Mục tiêu là tạo các điểm ngẫu nhiên bên trong đa giác có tính đến khoảng cách tối thiểu được chỉ định. Điều này hoạt động rất tốt đặc biệt là với các đa giác không gần với một đa giác khác (ví dụ: một đa giác đơn):

Thí dụ

Tuy nhiên, nếu đa giác gần hoặc liền kề với đa giác khác (ví dụ như được hiển thị bên dưới), các điểm từ mỗi đa giác có thể nằm trong khoảng cách tối thiểu như được hiển thị màu đỏ:

Vấn đề mẫu

Làm cách nào tôi có thể thay đổi mã để các điểm đó có màu đỏ không gần với điểm khác từ đa giác gần đó?

Lý tưởng nhất, tôi muốn nhiều điểm được thay thế bằng một điểm duy nhất:

Kết quả


Đây là mã có thể được sao chép trong Bảng điều khiển Python , một lớp đa giác phải được chọn với CRS có liên quan trước khi chạy chức năng:

import random
from PyQt4.QtCore import QVariant

def checkMinDistance(point, index, distance, points):
    if distance == 0:
        return True
    neighbors = index.nearestNeighbor(point, 1)
    if len(neighbors) == 0:
        return True
    if neighbors[0] in points:
        np = points[neighbors[0]]
        if np.sqrDist(point) < (distance * distance):
            return False
    return True

def generate_wind_turbines(spacing):
    layer = iface.activeLayer()
    crs = layer.crs()
    # Memory layer
    memory_lyr = QgsVectorLayer("Point?crs=epsg:" + unicode(crs.postgisSrid()) + "&index=yes", "Wind turbines for " + str(layer.name()), "memory")
    QgsMapLayerRegistry.instance().addMapLayer(memory_lyr)
    memory_lyr.startEditing()
    provider = memory_lyr.dataProvider()
    provider.addAttributes([QgsField("ID", QVariant.Int)])
    # Variables
    point_density = 0.0001
    fid = 1
    distance_area = QgsDistanceArea()
    # List of features
    fts = []
    # Create points
    for f in layer.getFeatures():
        fGeom = QgsGeometry(f.geometry())
        bbox = fGeom.boundingBox()
        pointCount = int(round(point_density * distance_area.measure(fGeom)))
        index = QgsSpatialIndex()
        points = dict()
        nPoints = 0
        fid += 1
        nIterations = 0
        maxIterations = pointCount * 200
        random.seed()
        while nIterations < maxIterations and nPoints < pointCount:
            rx = bbox.xMinimum() + bbox.width() * random.random()
            ry = bbox.yMinimum() + bbox.height() * random.random()
            pnt = QgsPoint(rx, ry)
            geom = QgsGeometry.fromPoint(pnt)
            if geom.within(fGeom) and checkMinDistance(pnt, index, spacing, points):
                f = QgsFeature(nPoints)
                f.setAttributes([fid])
                f.setGeometry(geom)
                fts.append(f)
                index.insertFeature(f)
                points[nPoints] = pnt
                nPoints += 1
            nIterations += 1
    provider.addFeatures(fts)
    memory_lyr.updateFields()
    memory_lyr.commitChanges()

generate_wind_turbines(500)

Biên tập:

Hòa tan và / hoặc chuyển đổi các đa giác thành đơn lẻ dường như không giúp được gì nhiều vì các điểm được tạo dường như vẫn nằm trong khoảng cách tối thiểu.

Đã thử nghiệm trên QGIS 2.18.3 .


1
nếu đó là một tùy chọn: Bạn đã thử sử dụng đa giác nhiều phần làm đa giác đầu vào chưa?
CườiU

@LaughU - Tôi vừa thử nó với đa giác nhiều phần nhưng không có điểm nào được tạo. Nó sẽ là một tùy chọn để chuyển đổi các tính năng một phần thành nhiều phần nếu mã có thể được điều chỉnh để hoạt động với đa giác nhiều phần.
Joseph

Bạn có nghĩ đến việc hòa tan các đa giác, theo sau là nhiều phần thành một lớp tạm thời mà bạn sử dụng để tạo điểm không? Bạn cũng có thể sử dụng bộ đệm âm cho lớp tạm thời để tránh chồng chéo biểu tượng ở các cạnh.
Mờ

@Matte - Cảm ơn, tôi đã thử hòa tan tất cả các đa giác trước đây. Tôi đã thử lại và chuyển đổi nó thành một phần (không chắc nó có làm gì không vì nó là một tính năng duy nhất) nhưng một số điểm gần các cạnh nằm trong các điểm trong các đa giác khác. Tôi muốn tránh sử dụng bộ đệm âm vì tôi muốn cho phép các điểm ở gần các cạnh :)
Joseph

Câu trả lời:


5

Bạn phải thay đổi hai điều để làm việc này. Tuy nhiên, bạn sẽ không nhận được tối đa các tuabin gió cho mỗi khu vực. Đối với điều này, bạn sẽ cần chạy một số lần lặp cho mỗi giá trị và nhận được số điểm tối đa.

Tôi đã di chuyển trong index = QgsSpatialIndex()points = dict()ngoài vòng lặp for. Mã sẽ trông như thế này:

import random
def generate_wind_turbines(spacing):
    layer = self.iface.activeLayer()
    crs = layer.crs()
    # Memory layer
    memory_lyr = QgsVectorLayer("Point?crs=epsg:" + unicode(crs.postgisSrid()) + "&index=yes", "Wind turbines for " + str(layer.name()), "memory")
    QgsMapLayerRegistry.instance().addMapLayer(memory_lyr)
    memory_lyr.startEditing()
    provider = memory_lyr.dataProvider()
    provider.addAttributes([QgsField("ID", QVariant.Int)])
    # Variables
    point_density = 0.0001
    fid = 1
    distance_area = QgsDistanceArea()
    # List of features
    fts = []
    # Create points
    points = dict() # changed from me 
    index = QgsSpatialIndex()# changend from me 
    nPoints = 0 # changed in the edit 
    pointCount = 0 # changed in the edit 

    for f in layer.getFeatures():
        fGeom = QgsGeometry(f.geometry())
        bbox = fGeom.boundingBox()
        # changed here as well 
        pointCount = int(round(point_density * distance_area.measure(fGeom))) + int(pointCount)
        fid += 1
        nIterations = 0
        maxIterations = pointCount * 200
        random.seed()
        while nIterations < maxIterations and nPoints < pointCount:
            rx = bbox.xMinimum() + bbox.width() * random.random()
            ry = bbox.yMinimum() + bbox.height() * random.random()
            pnt = QgsPoint(rx, ry)
            geom = QgsGeometry.fromPoint(pnt)
            if geom.within(fGeom) and checkMinDistance(pnt, index, spacing, points):
                f = QgsFeature(nPoints)
                f.setAttributes([fid])
                f.setGeometry(geom)
                fts.append(f)
                index.insertFeature(f)
                points[nPoints] = pnt
                nPoints += 1
            nIterations += 1
    provider.addFeatures(fts)
    memory_lyr.updateFields()
    memory_lyr.commitChanges()

def checkMinDistance( point, index, distance, points):
    if distance == 0:
        return True
    neighbors = index.nearestNeighbor(point, 1)
    if len(neighbors) == 0:
        return True
    if neighbors[0] in points:
        np = points[neighbors[0]]
        if np.sqrDist(point) < (distance * distance):
            return False
    return True

BIÊN TẬP:

Joseph đã đúng. những thay đổi của tôi chỉ làm việc cho một khu vực nhỏ thực sự. Tôi đã thử nghiệm xung quanh và tìm ra giải pháp mới bằng cách di chuyển hai biến ra khỏi vòng lặp for và thay đổi pointCountbiến.

Tôi đã thử nghiệm nó với 500m và đây là kết quả (hai lần thử khác nhau):

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


1
Rất vui, cảm ơn vì đã chú ý! Tôi chắc chắn rằng điều này cải thiện hiệu quả rất nhiều =)
Joseph

1
@Joseph bạn đã đúng. Tôi đã thử nó với các khu vực nhỏ hoạt động. Tôi đã thêm một số thay đổi nhỏ và nó hoạt động với tôi bây giờ. Hãy cho tôi biết nếu nó vẫn cần cải thiện :)
CườiU

1
Hấp dẫn! Tôi sẽ kiểm tra điều này vào ngày mai và báo cáo lại nhưng kết quả có vẻ rất tốt;)
Joseph

1
Vâng, điều này là tốt! Kịch bản của bạn cũng nhanh hơn và vẫn mang lại kết quả mà tôi đang tìm kiếm. Cuối cùng sẽ trao thưởng cho bạn tiền thưởng, hy vọng bạn không phiền nhưng tôi đã chỉnh sửa mã của bạn một chút;)
Joseph

1
@Joseph Không, tôi không phiền;) Tôi đã thử nghiệm mã và quên dọn dẹp mớ hỗn độn hay còn gọi là whitespaces
CườiU

4

Một phương pháp có thể là tạo một hàm khác thực hiện như sau:

  1. Tạo bộ đệm có cùng bán kính với khoảng cách xung quanh tất cả các điểm và lưu trữ chúng trong một lớp đa giác.
  2. Tạo một danh sách chứa id của tất cả các bộ đệm giao nhau.
  3. Xóa bộ đệm bằng danh sách id này.
  4. Tạo centroid của các bộ đệm còn lại và lưu trữ chúng trong một lớp điểm.

Đây là chức năng tôi đã sử dụng:

def wind_turbine_spacing_checker(layer, spacing):
    # Create buffers for points
    poly_layer =  QgsVectorLayer("Polygon?crs=epsg:27700", 'Buffers' , "memory")
    pr = poly_layer.dataProvider() 
    pr.addAttributes([QgsField("ID", QVariant.Int)])
    feat_list = []
    for f in layer.getFeatures():
        poly = QgsFeature()
        f_buffer = f.geometry().buffer((spacing / 2), 99)
        f_poly = poly.setGeometry(QgsGeometry.fromPolygon(f_buffer.asPolygon()))
        poly.setAttributes([1])
        poly.setGeometry(f_buffer)
        feat_list.append(poly)

    pr.addFeatures(feat_list)
    poly_layer.updateExtents()
    poly_layer.updateFields()
    QgsMapLayerRegistry.instance().addMapLayers([poly_layer])

    # Get pairs of intersecting buffer features
    features = [feat for feat in poly_layer.getFeatures()]
    ids = []
    for feat in poly_layer.getFeatures():
        for geat in features:
            if feat.id() != geat.id():
                if geat.geometry().intersects(feat.geometry()):
                    ids.append([feat.id(), geat.id()])

    # Set/sort list and get id of intersecting feature(s)
    for x in ids:
        x.sort()

    ids_sort = set(tuple(x) for x in ids)
    ids_list = [list(x) for x in ids_sort]
    ids_firstItem = [item[0] for item in ids_list]
    final_list = list(set(ids_firstItem))

    # Use ids from final_list to remove intersecting buffer features
    with edit(poly_layer):
        poly_layer.deleteFeatures(final_list)

    # Create new point layer and get centroids from buffers
    # (using final_list to delete the original features may not delete those points where the buffer interesects
    # so best to obtain the centroid of the buffers and output this as a new file)
    result_layer = QgsVectorLayer('Point?crs=epsg:27700&field=id:string', 'Result' , 'memory')
    result_layer.startEditing()
    for feat in poly_layer.getFeatures():
        centroid = feat.geometry().centroid()
        name = feat.attribute("ID")
        centroid_feature = QgsFeature(poly_layer.fields())
        centroid_feature.setGeometry(centroid)
        centroid_feature['ID'] = name
        result_layer.addFeature(centroid_feature)

    result_layer.commitChanges()
    QgsMapLayerRegistry.instance().addMapLayer(result_layer)

Hàm có thể được thực thi ngay lập tức khi kết thúc generate_wind_turbines()hàm bằng cách sử dụng:

...
memory_lyr.commitChanges()
wind_turbine_spacing_checker(memory_lyr, spacing)

Điều này cho kết quả như thể hiện trong hình ảnh trong câu hỏi. Có lẽ không phải là giải pháp hiệu quả nhất nhưng nó dường như hoạt động.


Một số ví dụ trong đó các điểm đỏ là những điểm được tạo ban đầu và các điểm được hiển thị dưới dạng tua bin có ranh giới là kết quả cuối cùng:

  • generate_wind_turbines(500)

    cảnh 1


  • generate_wind_turbines(1000)

    Kịch bản 2

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.