Создание многоугольников с постоянными размерами в мм?

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
radouxju
источник
Я думаю, что это решило проблемы, которые я упомянул (по иронии судьбы, я заметил некоторые новые проблемы, но думаю, что они были решены). Будем продолжать тестировать это дальше и доложить. Большое спасибо :)
Джозеф
Да, я считаю, что ваше решение сработало.
Иосиф
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

Джозеф
источник