Gleichmäßig dimensionierte Polygone in mm-Einheiten erzeugen?

11

Ich habe eine Funktion, die Photovolatische Solarmodule erstellt, die als Polygone dargestellt werden. Im Wesentlichen wird ein rechteckiges Raster erstellt, in dem der Benutzer die folgenden Parameter angeben kann:

  • Länge
  • Breite
  • Horizontaler Abstand
  • Vertikale Entfernung

Der Code basiert auf dem Plugin FeatureGridCreator , konzentriert sich jedoch nur auf den Polygonaspekt. Es funktioniert größtenteils gut, insbesondere beim Erstellen von Polygonen mit großen Abmessungen (z. B. 10 m Länge und Breite; 10 m horizontale und vertikale Abstände).

Aber ich habe ein paar Probleme bemerkt:

  1. Bei der Angabe von Polygonen für Abmessungen von weniger als 2 m für Länge und Breite wurden keine Polygone erstellt.

  2. Bei der Angabe von Polygonen mit unterschiedlichen Abmessungen (z. B. 5 m Länge und 7 m Breite) waren die Abmessungen bei der Messung mit dem Werkzeug " Linie messen" nicht dieselben . Für diese Abmessungen wurden Länge und Breite von 4 m bzw. 6 m gezeigt.

    Beispiel für unterschiedliche Länge und Breite

Das CRS, das sowohl für die Projektion als auch für die Ebene verwendet wird, ist EPSG: 27700, obwohl ich nicht gedacht hätte, dass dies ein Problem sein würde.

Hat jemand eine Idee, was diese Probleme verursachen könnte? Ich bin auch offen für Vorschläge, wie der Code verbessert oder sogar durch eine bessere Alternative ersetzt werden könnte.


Hier ist der Code, der in der Python-Konsole reproduziert werden kann. Vor dem Ausführen der Funktion muss eine Polygonebene mit einem relevanten CRS ausgewählt werden:

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

Antworten:

11

Ihr Algorithmus ist sinnvoll, aber es scheint, dass Ihr Problem auf einen Rundungsfehler beim Teilen durch 2000 zurückzuführen ist (Teilen durch Ganzzahl, was erklärt, warum eine Zahl kleiner als zwei 0 ergibt und alle Abstände auf gerade Werte gerundet werden).

Sie sollten die Ganzzahldivision durch eine Floatdivision ändern

l = length / 2000

sollte sein

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

oder

l = float(length) / 2000

Beachten Sie, dass Sie dadurch die genauen Abmessungen erhalten, die vom Formular eingegeben wurden. Sie können jedoch auch die Größe Ihrer Pakete auf einen Meter runden, wenn Sie dies vorziehen:

l = float(length/1000) / 2

Beachten Sie, dass Sie auch die Rundung an den Startkoordinaten überprüfen sollten, aber ich weiß nicht, ob diese Rundung absichtlich ist.

start_x = bbox.xMinimum() + float(distance_x) / 2
Radouxju
quelle
Ich denke, dies hat die von mir erwähnten Probleme gelöst (ironischerweise sind mir einige neue Probleme aufgefallen, aber ich denke, sie wurden behoben). Wird dies weiter testen und zurückmelden. Vielen Dank :)
Joseph
Ja, ich glaube, Ihre Lösung hat funktioniert. Nochmals
Joseph
3

Dank @radouxju ist hier der endgültige Code, der auch berücksichtigt, dass die horizontalen und vertikalen Abstände Null sind:

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

  • Verwenden von generate_pv_panels(5500, 5000, 20, 1):

    Szenario 1


  • Verwenden von generate_pv_panels(5500, 5000, 20, 0):

    Szenario 2

Joseph
quelle