Générer des polygones aux dimensions cohérentes en unités mm?

11

J'ai une fonction qui crée des panneaux solaires photovoltaïques représentés comme des polygones. Essentiellement, il crée une grille rectangulaire où l'utilisateur peut spécifier les paramètres suivants:

  • Longueur
  • Largeur
  • Distance horizontale
  • Distance verticale

Le code est basé sur le plugin FeatureGridCreator mais en se concentrant uniquement sur l'aspect polygone. Cela fonctionne bien pour la plupart, en particulier lors de la création de polygones de grandes dimensions (par exemple 10 m de longueur et de largeur; 10 m de distances horizontales et verticales).

Mais j'ai remarqué quelques problèmes:

  1. Lors de la spécification de polygones pour des dimensions inférieures à 2 m pour la longueur et la largeur, aucun polygone n'a été créé.

  2. Lors de la spécification de polygones avec des dimensions différentes (par exemple 5 m de longueur et 7 m de largeur), les dimensions n'étaient pas les mêmes lorsqu'elles étaient mesurées avec l' outil Mesurer la ligne . Pour ces dimensions, la longueur et la largeur étaient respectivement de 4 m et 6 m.

    Exemple de longueur et largeur différentes

Le CRS utilisé à la fois pour la projection et la couche est EPSG: 27700 bien que je n'aurais pas pensé que ce serait un problème.

Alors, quelqu'un a-t-il une idée de ce qui pourrait causer ces problèmes? Je suis également ouvert aux suggestions sur la façon dont le code pourrait être amélioré ou même remplacé par une meilleure alternative.


Voici le code qui peut être reproduit dans la console Python , une couche polygonale doit être sélectionnée avec un CRS approprié avant d'exécuter la fonction:

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
la source

Réponses:

11

Votre algorithme est logique, mais il semble que votre problème soit dû à une erreur d'arrondi lorsque vous divisez par 2000 (division par entier, ce qui explique pourquoi un nombre inférieur à deux donne 0, et toutes les distances sont arrondies à des valeurs paires)

Vous devez changer la division entière avec une division flottante

l = length / 2000

devrait être

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

ou

l = float(length) / 2000

Notez que cela vous donne les dimensions exactes saisies par le formulaire, mais vous pouvez décider d'arrondir la taille de vos colis à un mètre si vous préférez:

l = float(length/1000) / 2

Notez que vous devez également vérifier l'arrondi aux coordonnées de départ, mais je ne sais pas si cet arrondi est exprès.

start_x = bbox.xMinimum() + float(distance_x) / 2
radouxju
la source
Je pense que cela a résolu les problèmes que j'ai mentionnés (ironiquement, j'ai remarqué de nouveaux problèmes, mais je pense qu'ils ont été résolus). Continuera de tester cela plus avant et fera rapport. Merci beaucoup :)
Joseph
Oui, je crois que votre solution a fonctionné. Merci encore;)
Joseph
3

Grâce à @radouxju , voici le code final qui prend également en compte les distances horizontales et verticales nulles:

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

  • En utilisant generate_pv_panels(5500, 5000, 20, 1):

    Scénario 1


  • En utilisant generate_pv_panels(5500, 5000, 20, 0):

    Scénario 2

Joseph
la source