Generujesz wielokąty o stałych wymiarach w mm?

11

Mam funkcję, która tworzy fotowoltaiczne panele słoneczne reprezentowane jako wielokąty. Zasadniczo tworzy prostokątną siatkę, w której użytkownik może określić następujące parametry:

  • Długość
  • Szerokość
  • Odległość pozioma
  • Odległość w pionie

Kod oparty jest na wtyczce FeatureGridCreator, ale koncentruje się tylko na aspekcie wielokąta. W większości przypadków działa dobrze, zwłaszcza przy tworzeniu wielokątów o dużych wymiarach (np. 10 m długości i szerokości; 10 m odległości poziomych i pionowych).

Ale zauważyłem kilka problemów:

  1. Podczas określania wielokątów dla wymiarów mniejszych niż 2 m zarówno dla długości, jak i szerokości, nie zostały utworzone wielokąty.

  2. Podczas określania wielokątów o różnych wymiarach (np. 5 m długości i 7 m szerokości) wymiary nie były takie same, gdy były mierzone za pomocą narzędzia Pomiar linii . Dla tych wymiarów pokazano, że długość i szerokość wynoszą odpowiednio 4 mi 6 m.

    Przykład różnej długości i szerokości

CRS zastosowany zarówno do projekcji, jak i warstwy to EPSG: 27700, chociaż nie pomyślałbym, że to będzie problem.

Czy ktoś ma pojęcie, co może być przyczyną tych problemów? Jestem również otwarty na sugestie, w jaki sposób można ulepszyć kod, a nawet zastąpić go lepszą alternatywą.


Oto kod, który można odtworzyć w konsoli Pythona , przed uruchomieniem funkcji należy wybrać warstwę wielokąta z odpowiednim 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)
Joseph
źródło

Odpowiedzi:

11

Twój algorytm ma sens, ale wydaje się, że twój problem jest spowodowany błędem zaokrąglenia podczas dzielenia przez 2000 (podziel przez liczbę całkowitą, co wyjaśnia, dlaczego liczba mniejsza niż dwa daje 0, a wszystkie odległości są zaokrąglane do wartości parzystych)

Powinieneś zmienić podział na liczbę całkowitą za pomocą podziału zmiennoprzecinkowego

l = length / 2000

Powinien być

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

lub

l = float(length) / 2000

Pamiętaj, że daje to dokładne wymiary wprowadzone w formularzu, ale możesz zdecydować o zaokrągleniu wielkości paczek o jeden metr, jeśli wolisz:

l = float(length/1000) / 2

Pamiętaj, że powinieneś również sprawdzić zaokrąglenie na współrzędnych początkowych, ale nie wiem, czy to zaokrąglenie jest celowe.

start_x = bbox.xMinimum() + float(distance_x) / 2
radouxju
źródło
Myślę, że to rozwiązało problemy, o których wspomniałem (jak na ironię, zauważyłem kilka nowych problemów, ale myślę, że zostały rozwiązane). Będzie kontynuował testowanie tego i złożył raport. Wielkie dzięki :)
Joseph
Tak, uważam, że twoje rozwiązanie zadziałało. Jeszcze raz dziękuję;)
Joseph
3

Dzięki @radouxju tutaj jest końcowy kod, który uwzględnia również zerowe odległości w poziomie i pionie:

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

  • Używanie generate_pv_panels(5500, 5000, 20, 1):

    Scenariusz 1


  • Używanie generate_pv_panels(5500, 5000, 20, 0):

    Scenariusz 2

Joseph
źródło