以毫米为单位生成尺寸一致的多边形?


11

我有一个函数可以创建表示为多边形的“太阳光能”面板。本质上,它创建一个矩形网格,用户可以在其中指定以下参数:

  • 长度
  • 宽度
  • 水平距离
  • 垂直距离

该代码基于插件FeatureGridCreator,但仅专注于多边形方面。它在大多数情况下效果很好,尤其是在创建较大尺寸的多边形(例如,长度和宽度为10m;水平和垂直距离为10m)时。

但是我注意到了两个问题:

  1. 指定长度和宽度均小于2m的多边形时,不会创建任何多边形。

  2. 当指定具有不同尺寸(例如5m长度和7m宽度)的多边形时,使用“ 测量线”工具测量时尺寸不相同。对于这些尺寸,长度和宽度分别显示为4m和6m。

    长度和宽度不同的示例

用于投影和图层的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)

Answers:


11

您的算法很有意义,但是似乎您的问题是由于当您除以2000时出现舍入误差(除以整数,这说明了为什么小于2的数字给出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

我认为这已经解决了我提到的问题(具有讽刺意味的是,我注意到正在发生一些新问题,但认为它们已经解决了)。将继续对此进行进一步测试并报告。非常感谢:)
约瑟夫

是的,我相信您的解决方案已经奏效。再次谢谢;)
约瑟夫(Joseph

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.