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
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.
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
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()
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:
x.mean() * y.mean()
de chaque produitLe décalage de coordonnées ne modifie pas la surface totale, il rend simplement le calcul plus stable numériquement.
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
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
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.