Gerando polígonos de dimensões consistentes em unidades de mm?

11

Eu tenho uma função que cria painéis fotovoláticos solares representados como polígonos. Essencialmente, ele cria uma grade retangular onde o usuário pode especificar os seguintes parâmetros:

  • comprimento
  • Largura
  • Distância horizontal
  • Distância vertical

O código é baseado no plug-in FeatureGridCreator, mas focado apenas no aspecto do polígono. Na maioria das vezes, funciona bem, especialmente ao criar polígonos com grandes dimensões (por exemplo, 10m de comprimento e largura; 10m de distâncias horizontais e verticais).

Mas notei alguns problemas:

  1. Ao especificar polígonos para dimensões inferiores a 2 m para comprimento e largura, nenhum polígono foi criado.

  2. Ao especificar polígonos com dimensões diferentes (por exemplo, 5m de comprimento e 7m de largura), as dimensões não eram as mesmas quando medidas com a ferramenta Medir linha . Para essas dimensões, o comprimento e a largura foram mostrados como 4m e 6m, respectivamente.

    Exemplo de comprimento e largura diferentes

O CRS usado para a projeção e a camada é EPSG: 27700, embora eu não achasse que isso seria um problema.

Então, alguém tem alguma idéia do que poderia estar causando esses problemas? Também estou aberto a sugestões de como o código pode ser aprimorado ou mesmo substituído por uma alternativa melhor.


Aqui está o código que pode ser reproduzido no Python Console , uma camada de polígono deve ser selecionada com um CRS relevante antes de executar a função:

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
fonte

Respostas:

11

Seu algoritmo faz sentido, mas parece que seu problema ocorre devido a um erro de arredondamento quando você divide em 2000 (divida por número inteiro, o que explica por que um número menor que dois dá 0 e todas as distâncias são arredondadas para valores pares)

Você deve alterar a divisão inteira com uma divisão flutuante

l = length / 2000

deveria estar

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

ou

l = float(length) / 2000

Observe que isso fornece as dimensões exatas inseridas pelo formulário, mas você pode optar por arredondar o tamanho de suas parcelas em um metro, se preferir:

l = float(length/1000) / 2

Observe que você também deve verificar o arredondamento nas coordenadas de início, mas não sei se esse arredondamento é intencional.

start_x = bbox.xMinimum() + float(distance_x) / 2
radouxju
fonte
Acho que isso resolveu os problemas que mencionei (ironicamente, notei alguns problemas novos, mas acho que eles foram resolvidos). Continuará testando isso mais e apresentará um relatório. Muito obrigado :)
Joseph
Sim, acredito que sua solução funcionou. Mais uma vez obrigado;)
Joseph
3

Graças a @radouxju , aqui está o código final que também leva em consideração as distâncias horizontal e vertical sendo 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))

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

    Cenário 1


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

    Cenário 2

Joseph
fonte