Утворення послідовно розмірених багатокутників у мм одиницях?


11

У мене є функція, яка створює сонячні фотоволатичні панелі, представлені у вигляді полігонів. По суті, він створює прямокутну сітку, де користувач може вказати наступні параметри:

  • Довжина
  • Ширина
  • Горизонтальна відстань
  • Вертикальна відстань

Код заснований на плагіні FeatureGridCreator, але фокусується лише на полігонному аспекті. Він працює добре здебільшого, особливо при створенні багатокутників з великими розмірами (наприклад, довжина та ширина 10 м; горизонтальні та вертикальні відстані 10 м).

Але я помітив пару питань:

  1. Вказуючи багатокутники для розмірів менше 2 м як для довжини, так і для ширини, багатокутники не створювалися.

  2. Вказуючи багатокутники з різними розмірами (наприклад, довжина 5м і ширина 7м), розміри не були однаковими при вимірюванні інструментом " Лінія вимірювання" . Для цих розмірів було показано, що довжина і ширина відповідно 4 м і 6 м.

    Приклад різної довжини та ширини

CRS, що використовується як для проекції, так і для шару, - це EPSG: 27700, хоча я б не думав, що це буде проблемою.

Так хтось має ідею, що може викликати ці проблеми? Я також відкритий для пропозицій щодо того, як код можна вдосконалити або навіть замінити на кращу альтернативу.


Ось код, який можна відтворити на консолі Python , перед запуском функції слід вибрати полігонний шар із відповідним CRS:

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)

Відповіді:


11

Ваш алгоритм має сенс, але здається, що ваша проблема пов’язана з помилкою округлення при діленні на 2000 (ділиться на ціле число, що пояснює, чому число менше двох дає 0, а всі відстані округляються до парних значень)

Ви повинні змінити ціле ділення на поплавковий поділ

l = length / 2000

має бути

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

або

l = float(length) / 2000

Зауважте, що це дає точні розміри, введені формою, але ви можете вирішити округнути розмір ваших посилок на один метр, якщо вам зручніше:

l = float(length/1000) / 2

Зауважте, що ви також повинні перевірити округлення на стартових координатах, але я не знаю, чи це округлення за призначенням.

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

Я думаю, що це вирішило питання, про які я згадав (за іронією долі, я помітив, що виникають нові проблеми, але думаю, що вони були вирішені). Буде продовжено тестувати це і подати звіт. Велике спасибі :)
Йосип

Так, я вважаю, що ваше рішення спрацювало. Дякую ще раз;)
Йосип

3

Завдяки @radouxju , ось остаточний код, який також враховує горизонтальні та вертикальні відстані, що дорівнюють нулю:

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

  • Використання generate_pv_panels(5500, 5000, 20, 1):

    Сценарій 1


  • Використання generate_pv_panels(5500, 5000, 20, 0):

    Сценарій 2

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.