¿Generando polígonos de dimensiones consistentes en unidades de mm?

11

Tengo una función que crea paneles fotovoltaicos solares representados como polígonos. Básicamente, crea una cuadrícula rectangular donde el usuario puede especificar los siguientes parámetros:

  • Longitud
  • Anchura
  • Distancia horizontal
  • Distancia vertical

El código se basa en el complemento FeatureGridCreator pero se centra solo en el aspecto del polígono. Funciona bien en su mayor parte, especialmente cuando se crean polígonos con grandes dimensiones (por ejemplo, 10 m de largo y ancho; 10 m de distancia horizontal y vertical).

Pero noté un par de problemas:

  1. Al especificar polígonos para dimensiones inferiores a 2 m para longitud y anchura, no se crearon polígonos.

  2. Al especificar polígonos con diferentes dimensiones (por ejemplo, 5 m de largo y 7 m de ancho), las dimensiones no eran las mismas cuando se midieron con la herramienta Medir línea . Para estas dimensiones, se demostró que la longitud y el ancho eran de 4 my 6 m respectivamente.

    Ejemplo de diferente longitud y ancho

El CRS utilizado tanto para la proyección como para la capa es EPSG: 27700, aunque no hubiera pensado que esto sería un problema.

Entonces, ¿alguien tiene alguna idea de lo que podría estar causando estos problemas? También estoy abierto a sugerencias sobre cómo se podría mejorar el código o incluso reemplazarlo por una mejor alternativa.


Aquí está el código que se puede reproducir en la Consola Python , se debe seleccionar una capa de polígono con un CRS relevante antes de ejecutar la función:

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
fuente

Respuestas:

11

Su algoritmo tiene sentido, pero parece que su problema se debe a un error de redondeo cuando divide por 2000 (dividir por entero, lo que explica por qué un número menor que dos da 0, y todas las distancias se redondean a valores pares)

Debes cambiar la división entera con una división flotante

l = length / 2000

debiera ser

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

o

l = float(length) / 2000

Tenga en cuenta que esto le proporciona las dimensiones exactas ingresadas por el formulario, pero puede decidir redondear el tamaño de sus parcelas en un metro si lo prefiere:

l = float(length/1000) / 2

Tenga en cuenta que también debe verificar el redondeo en las coordenadas de inicio, pero no sé si este redondeo es a propósito.

start_x = bbox.xMinimum() + float(distance_x) / 2
radouxju
fuente
Creo que esto ha resuelto los problemas que mencioné (irónicamente, noté que ocurrían algunos problemas nuevos, pero creo que se han resuelto). Continuará probando esto más e informará. Muchas gracias :)
Joseph
Sí, creo que su solución ha funcionado. Gracias de nuevo;)
Joseph
3

Gracias a @radouxju , aquí está el código final que también tiene en cuenta que las distancias horizontal y vertical son cero:

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

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

    escenario 1


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

    Escenario 2

Joseph
fuente