mm単位で一貫した寸法のポリゴンを生成しますか?


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)

回答:


11

アルゴリズムは理にかなっていますが、問題は2000で除算するときの丸め誤差によるものと思われます(整数で除算するため、2より小さい数は0になり、すべての距離は偶数値に丸められます)

整数除算を浮動小数点除算で変更する必要があります

l = length / 2000

あるべき

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

または

l = float(length) / 2000

これにより、フォームに入力された正確な寸法が得られますが、必要に応じて区画のサイズを1メートルに丸めることができます。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.