Generare poligoni di dimensioni coerenti in unità mm?


11

Ho una funzione che crea pannelli fotovoltaici fotovoltaici rappresentati come poligoni. In sostanza, crea una griglia rettangolare in cui l'utente può specificare i seguenti parametri:

  • Lunghezza
  • Larghezza
  • Distanza orizzontale
  • Distanza verticale

Il codice si basa sul plugin FeatureGridCreator ma si concentra solo sull'aspetto poligonale. Funziona bene per la maggior parte, specialmente quando si creano poligoni di grandi dimensioni (ad es. 10 m di lunghezza e larghezza; 10 m di distanza orizzontale e verticale).

Ma ho notato un paio di problemi:

  1. Quando si specificavano poligoni per dimensioni inferiori a 2 m per lunghezza e larghezza, non venivano creati poligoni.

  2. Quando si specificavano poligoni con dimensioni diverse (ad es. Lunghezza 5m e larghezza 7m), le dimensioni non erano le stesse se misurate con lo strumento Misura linea . Per queste dimensioni, la lunghezza e la larghezza sono state mostrate rispettivamente a 4m e 6m.

    Esempio di diversa lunghezza e larghezza

Il CRS utilizzato sia per la proiezione che per il livello è EPSG: 27700 anche se non avrei pensato che questo sarebbe stato un problema.

Qualcuno ha idea di cosa potrebbe causare questi problemi? Sono anche aperto a suggerimenti su come il codice potrebbe essere migliorato o addirittura sostituito con un'alternativa migliore.


Ecco il codice che può essere riprodotto in Python Console , un layer poligonale deve essere selezionato con un CRS rilevante prima di eseguire la funzione:

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)

Risposte:


11

Il tuo algoritmo ha un senso, ma sembra che il tuo problema sia dovuto a un errore di arrotondamento quando dividi per 2000 (dividi per intero, il che spiega perché un numero inferiore a due dà 0 e tutte le distanze sono arrotondate a valori pari)

È necessario modificare la divisione intera con una divisione float

l = length / 2000

dovrebbe essere

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

o

l = float(length) / 2000

Tieni presente che questo ti dà le dimensioni esatte inserite dal modulo, ma puoi decidere di arrotondare le dimensioni dei tuoi pacchi a un metro se preferisci:

l = float(length/1000) / 2

Nota che dovresti anche controllare l'arrotondamento alle coordinate di partenza, ma non so se questo arrotondamento sia apposta.

start_x = bbox.xMinimum() + float(distance_x) / 2

Penso che questo abbia risolto i problemi che ho menzionato (ironicamente, ho notato alcuni nuovi problemi che si verificano ma penso che siano stati risolti). Continuerà a testarlo ulteriormente e riferirà. Mille grazie :)
Joseph,

Sì, credo che la tua soluzione abbia funzionato. Grazie ancora;)
Joseph,

3

Grazie a @radouxju , ecco il codice finale che tiene conto anche delle distanze orizzontali e verticali essendo zero:

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

  • Utilizzando generate_pv_panels(5500, 5000, 20, 1):

    scenario 1


  • Utilizzando generate_pv_panels(5500, 5000, 20, 0):

    Scenario 2

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.