web-dev-qa-db-fra.com

Calculer l'aire du polygone à l'aide des coordonnées (x, y)

J'ai un ensemble de points et je voudrais savoir s'il existe une fonction (pour des raisons de commodité et probablement de vitesse) qui peut calculer la zone entourée par un ensemble de points.

par exemple:

x = np.arange(0,1,0.001)
y = np.sqrt(1-x**2)

points = Zip(x,y)

étant donné points la zone doit être approximativement égale à (pi-2)/4. Peut-être qu'il y a quelque chose de scipy, matplotlib, numpy, shapely, etc. pour ce faire? Je ne rencontrerai aucune valeur négative pour les coordonnées x ou y ... et ce seront des polygones sans fonction définie.

MODIFIER:

les points ne seront probablement pas dans un ordre spécifié (dans le sens horaire ou antihoraire) et peuvent être assez complexes car ils sont un ensemble de coordonnées utm d'un fichier de formes sous un ensemble de frontières

30
pbreach

L'implémentation de Shoelace formula pourrait être effectuée dans Numpy. En supposant ces sommets:

import numpy as np
x = np.arange(0,1,0.001)
y = np.sqrt(1-x**2)

On peut redéfinir la fonction en numpy pour trouver la zone:

def PolyArea(x,y):
    return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))

Et obtenir des résultats:

print PolyArea(x,y)
# 0.26353377782163534

Éviter la boucle for rend cette fonction ~ 50X plus rapide que PolygonArea:

%timeit PolyArea(x,y)
# 10000 loops, best of 3: 42 µs per loop
%timeit PolygonArea(Zip(x,y))
# 100 loops, best of 3: 2.09 ms per loop.

Le chronométrage se fait dans le cahier Jupyter.

67
Mahdi

Vous pouvez utiliser la formule de lacet , par exemple

def PolygonArea(corners):
    n = len(corners) # of corners
    area = 0.0
    for i in range(n):
        j = (i + 1) % n
        area += corners[i][0] * corners[j][1]
        area -= corners[j][0] * corners[i][1]
    area = abs(area) / 2.0
    return area

# examples
corners = [(2.0, 1.0), (4.0, 5.0), (7.0, 8.0)]

Cela ne fonctionne que pour les polygones simples


  • Si vous avez un polygone avec des trous : calculez la zone de l'anneau externe et sous-traitez les zones des anneaux internes

  • Si vous avez des anneaux auto-entrecroisés : vous devez les décomposer en secteurs simples

27
Nikos Athanasiou

En analysant la réponse de Mahdi, j'ai conclu que la majorité du temps était consacré à la np.roll(). En supprimant le besoin du rouleau et en utilisant toujours numpy, j'ai réduit le temps d'exécution à 4-5µs par boucle par rapport aux 41µs de Mahdi (à titre de comparaison, la fonction de Mahdi a pris en moyenne 37µs sur ma machine).

def polygon_area(x,y):
    correction = x[-1] * y[0] - y[-1]* x[0]
    main_area = np.dot(x[:-1], y[1:]) - np.dot(y[:-1], x[1:])
    return 0.5*np.abs(main_area + correction)

En calculant le terme correctionnel, puis en découpant les tableaux, il n'est pas nécessaire de rouler ou de créer un nouveau tableau.

Repères:

10000 iterations
PolyArea(x,y): 37.075µs per loop
polygon_area(x,y): 4.665µs per loop

Le chronométrage a été fait en utilisant le module time et time.clock()

5
maxb

la réponse de maxb donne de bonnes performances mais peut facilement entraîner une perte de précision lorsque les valeurs de coordonnées ou le nombre de points sont importants. Cela peut être atténué avec un simple décalage de coordonnées:

def polygon_area(x,y):
    # coordinate shift
    x_ = x - x.mean()
    y_ = y - y.mean()
    # everything else is the same as maxb's code
    correction = x_[-1] * y_[0] - y_[-1]* x_[0]
    main_area = np.dot(x_[:-1], y_[1:]) - np.dot(y_[:-1], x_[1:])
    return 0.5*np.abs(main_area + correction)

Par exemple, un système de référence géographique commun est UTM, qui peut avoir des coordonnées (x, y) de (488685.984, 7133035.984). Le produit de ces deux valeurs est 3485814708748.448. Vous pouvez voir que ce seul produit est déjà à la limite de la précision (il a le même nombre de décimales que les entrées). L'ajout de quelques-uns de ces produits, sans parler de milliers, entraînera une perte de précision.

Un moyen simple d'atténuer cela est de déplacer le polygone de grandes coordonnées positives vers quelque chose de plus proche de (0,0), par exemple en soustrayant le centroïde comme dans le code ci-dessus. Cela aide de deux manières:

  1. Il élimine un facteur de x.mean() * y.mean() de chaque produit
  2. Il produit un mélange de valeurs positives et négatives au sein de chaque produit scalaire, qui sera en grande partie annulé.

Le décalage de coordonnées ne modifie pas la surface totale, il rend simplement le calcul plus stable numériquement.

4
Trenton

Il y a une erreur dans le code ci-dessus car il ne prend pas de valeurs absolues à chaque itération. Le code ci-dessus renverra toujours zéro. (Mathématiquement, c'est la différence entre la prise d'une zone signée ou d'un produit de coin et la zone réelle http://en.wikipedia.org/wiki/Exterior_algebra .) Voici un autre code.

def area(vertices):
    n = len(vertices) # of corners
    a = 0.0
    for i in range(n):
        j = (i + 1) % n
        a += abs(vertices[i][0] * vertices[j][1]-vertices[j][0] * vertices[i][1])
    result = a / 2.0
    return result
2
Chris Judge

C'est beaucoup plus simple, pour les polygones réguliers:

import math

def area_polygon(n, s):
    return 0.25 * n * s**2 / math.tan(math.pi/n)

puisque la formule est ¼ n s2/tan (π/n). Étant donné le nombre de côtés, n, et la longueur de chaque côté, s

1
Bizarre

Basé sur

https://www.mathsisfun.com/geometry/area-irregular-polygons.html

def _area_(coords):
    t=0
    for count in range(len(coords)-1):
        y = coords[count+1][1] + coords[count][1]
        x = coords[count+1][0] - coords[count][0]
        z = y * x
        t += z
    return abs(t/2.0)

a=[(5.09,5.8), (1.68,4.9), (1.48,1.38), (4.76,0.1), (7.0,2.83), (5.09,5.8)]
print _area_(a)

L'astuce est que la première coordonnée doit également être la dernière.

0
Takis Tsiberis