Tạo đa giác có kích thước nhất quán theo đơn vị mm?


11

Tôi có một chức năng tạo ra các tấm quang điện mặt trời được biểu diễn dưới dạng đa giác. Về cơ bản, nó tạo ra một lưới hình chữ nhật nơi người dùng có thể chỉ định các tham số sau:

  • Chiều dài
  • Chiều rộng
  • Khoảng cách ngang
  • Khoảng cách dọc

Mã này dựa trên plugin FeatureGridCreator nhưng chỉ tập trung vào khía cạnh đa giác. Nó hoạt động tốt đối với hầu hết các phần, đặc biệt là khi tạo đa giác với kích thước lớn (ví dụ: chiều dài và chiều rộng 10m; khoảng cách ngang và dọc 10m).

Nhưng tôi nhận thấy một vài vấn đề:

  1. Khi chỉ định đa giác cho kích thước nhỏ hơn 2m cho cả chiều dài và chiều rộng, không có đa giác nào được tạo.

  2. Khi chỉ định đa giác với các kích thước khác nhau (ví dụ: chiều dài 5m và chiều rộng 7m), kích thước không giống nhau khi được đo bằng công cụ Đường đo . Đối với các kích thước này, chiều dài và chiều rộng được hiển thị lần lượt là 4m và 6m.

    Ví dụ về chiều dài và chiều rộng khác nhau

CRS được sử dụng cho cả hình chiếu và lớp là EPSG: 27700 mặc dù tôi không nghĩ đây sẽ là một vấn đề.

Vì vậy, có ai có bất kỳ ý tưởng những gì có thể gây ra những vấn đề này? Tôi cũng sẵn sàng để đề xuất về cách mã có thể được cải thiện hoặc thậm chí thay thế bằng một sự thay thế tốt hơn.


Đâ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:

from PyQt4.QtCore import QVariant
from math import ceil

def generate_pv_panels(length, width, distance_x, distance_y):
    # Define layer properties
    layer = iface.activeLayer()
    crs = layer.crs()
    memory_lyr = QgsVectorLayer("Polygon?crs=epsg:" + unicode(crs.postgisSrid()) + "&index=yes", "PV panels for " + str(layer.name()), "memory")
    QgsMapLayerRegistry.instance().addMapLayer(memory_lyr)
    memory_lyr.startEditing()
    provider = memory_lyr.dataProvider()
    provider.addAttributes([QgsField("ID", QVariant.Int)])
    fid = 0
    start_x = 0
    start_y = 0
    # Ensure polygons are not created 'within each other'
    if distance_x < (length / 1000):
        distance_x = (length / 1000)
    if distance_y < (width / 1000):
        distance_y = (width / 1000)
    fts = []
    for f in layer.getFeatures():
        fid += 1
        bbox = f.geometry().boundingBox()
        start_x = bbox.xMinimum() + float(distance_x / 2)
        start_y = bbox.yMinimum() + float(distance_y / 2)
        for row in range(0, int(ceil(bbox.height() / distance_y))):
            for column in range(0, int(ceil(bbox.width() / distance_x))):
                fet = QgsFeature()
                geom_type = pv_panel_size(length, width, start_x, start_y)
                if f.geometry().contains(geom_type):
                    fet.setGeometry(geom_type)
                    fet.setAttributes([fid])
                    fts.append(fet)
                start_x += distance_x + (length / 1000)
            start_x = bbox.xMinimum() + float(distance_x / 2)
            start_y += distance_y + (width / 1000)
    provider.addFeatures(fts)
    memory_lyr.updateFields()
    memory_lyr.commitChanges()

def pv_panel_size(length, width, x, y):
    # Length & width measured in mm; x & y measured in m
    l = length / 2000
    w = width / 2000
    return QgsGeometry.fromRect(QgsRectangle(x - l, y - w, x + l, y + w))

generate_pv_panels(10000, 10000, 100, 100)

Câu trả lời:


11

Thuật toán của bạn có ý nghĩa, nhưng có vẻ như vấn đề của bạn là do lỗi làm tròn khi bạn chia cho 2000 (chia cho số nguyên, giải thích tại sao một số nhỏ hơn hai cho 0 và tất cả các khoảng cách được làm tròn thành giá trị chẵn)

Bạn nên thay đổi phép chia số nguyên bằng phép chia float

l = length / 2000

nên là

l = length / 2000. # the . makes sure that you divide by a decimal value

hoặc là

l = float(length) / 2000

Lưu ý rằng điều này cung cấp cho bạn các kích thước chính xác được nhập bởi biểu mẫu, nhưng bạn có thể quyết định làm tròn kích thước của bưu kiện của mình ở một mét nếu bạn muốn:

l = float(length/1000) / 2

Lưu ý rằng bạn cũng nên kiểm tra làm tròn ở tọa độ bắt đầu, nhưng tôi không biết liệu làm tròn này có chủ đích hay không.

start_x = bbox.xMinimum() + float(distance_x) / 2

Tôi nghĩ rằng điều này đã giải quyết các vấn đề tôi đã đề cập (trớ trêu thay, tôi nhận thấy một số vấn đề mới xảy ra nhưng nghĩ rằng chúng đã được giải quyết). Sẽ tiếp tục thử nghiệm điều này hơn nữa và báo cáo lại. Rất cám ơn :)
Joseph

Yup, tôi tin rằng giải pháp của bạn đã làm việc. Cảm ơn bạn một lần nữa;)
Joseph

3

Nhờ @radouxju , đây là mã cuối cùng cũng tính đến khoảng cách ngang và dọc là 0:

from PyQt4.QtCore import QVariant
from math import ceil

def generate_pv_panels(length, width, distance_x, distance_y):
    # Define layer properties
    layer = iface.activeLayer()
    crs = layer.crs()
    memory_lyr = QgsVectorLayer("Polygon?crs=epsg:" + unicode(crs.postgisSrid()) + "&index=yes", "PV panels for " + str(layer.name()), "memory")
    QgsMapLayerRegistry.instance().addMapLayer(memory_lyr)
    memory_lyr.startEditing()
    provider = memory_lyr.dataProvider()
    provider.addAttributes([QgsField("ID", QVariant.Int)])
    # Define variables
    fid = 0
    start_x = 0
    start_y = 0
    state_x = False
    state_y = False
    # Ensure polygons are not created 'within each other' if distance is zero;
    # Instead they will align on the bounding box
    if distance_x == 0:
        distance_x = (length / 1000)
        state_x = True
    if distance_y == 0:
        distance_y = (width / 1000)
        state_y = True
    fts = []
    for f in layer.getFeatures():
        fid += 1
        bbox = f.geometry().boundingBox()
        start_x = bbox.xMinimum() + float(distance_x / 2)
        start_y = bbox.yMinimum() + float(distance_y / 2)
        for row in range(0, int(ceil(bbox.height() / distance_y))):
            for column in range(0, int(ceil(bbox.width() / distance_x))):
                fet = QgsFeature()
                geom_type = pv_panel_size(length, width, start_x, start_y)
                if f.geometry().contains(geom_type):
                    fet.setGeometry(geom_type)
                    fet.setAttributes([fid])
                    fts.append(fet)
                if state_x == False:
                    start_x += distance_x + (length / 1000)
                else:
                    start_x += distance_x
            start_x = bbox.xMinimum() + float(distance_x / 2)
            if state_y == False:
                start_y += distance_y + (width / 1000)
            else:
                start_y += distance_y
    provider.addFeatures(fts)
    memory_lyr.updateFields()
    memory_lyr.commitChanges()

def pv_panel_size(length, width, x, y):
    # Length & width measured in mm; x & y measured in m
    l = float(length) / 2000
    w = float(width) / 2000
    return QgsGeometry.fromRect(QgsRectangle(x - l, y - w, x + l, y + w))

  • Sử dụng generate_pv_panels(5500, 5000, 20, 1):

    cảnh 1


  • Sử dụng generate_pv_panels(5500, 5000, 20, 0):

    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.