Finding the centroid of a polygon in Python
Question:
I want to calculate the centroid of a figure formed by the points: (0,0), (70,0), (70,25), (45, 45), (45, 180), (95, 188), (95, 200), (-25, 200), (-25,188), (25,180), (25,45), (0, 25), (0,0).
I know that the correct result for the centroid of this polygon is x = 35 and y = 100.4615, but the code below does not return the correct values (figure of the polygon below).
import numpy as np
import matplotlib.pyplot as plt
poligono = np.array([(0,0), (70,0), (70,25), (45,45), (45,180), (95,188), (95,200), (-25,200), (-25, 188), (25,180), (25,45), (0,25), (0,0)])
x = pontos[:, 0]
y = pontos[:, 1]
points_array_2 = np.array(points)
centroid = np.mean(points_array_2, axis=0)
print("Centroide:", centroid)
I would like to know if someone can help me correctly calculate the centroid of the polygon.
Answers:
Fixed:
def centroid(vertices):
x, y = 0, 0
n = len(vertices)
signed_area = 0
for i in range(len(vertices)):
x0, y0 = vertices[i]
x1, y1 = vertices[(i + 1) % n]
# shoelace formula
area = (x0 * y1) - (x1 * y0)
signed_area += area
x += (x0 + x1) * area
y += (y0 + y1) * area
signed_area *= 0.5
x /= 6 * signed_area
y /= 6 * signed_area
return x, y
x, y = centroid(vertices)
print(x, y)
Produces:
35.0 100.46145124716553
Here’s some code. Explanations follow.
import numpy as np
polygon = np.array(
[
(0,0), (70,0), (70,25), (45, 45), (45, 180), (95, 188),
(95, 200), (-25, 200), (-25,188), (25,180), (25,45), (0, 25),
],
dtype=np.float64,
)
# Same polygon, but with vertices cycled around. Now the polygon
# decomposes into triangles of the form origin-polygon[i]-polygon2[i]
polygon2 = np.roll(polygon, -1, axis=0)
# Compute signed area of each triangle
signed_areas = 0.5 * np.cross(polygon, polygon2)
# Compute centroid of each triangle
centroids = (polygon + polygon2) / 3.0
# Get average of those centroids, weighted by the signed areas.
centroid = np.average(centroids, axis=0, weights=signed_areas)
print("Centroid:", centroid)
The result of running this code on my machine is the one you expect:
$ python polygon_centroid.py
Centroid: [ 35. 100.46145125]
The basic idea is that we decompose the shape into triangles. We can compute the centroid for each triangle via the obvious formula (it’s the mean of the vertices). To compute the centroid of the whole polygon, we just have to compute the average of the centroids of the constituent triangles, with each triangle weighted by its (signed) area.
What makes this task especially easy for a polygon is that we can allow ourselves to include "negative" triangles – that is, we can express the polygon as a sum and difference of triangles. Provided we keep track of the signs in the areas, everything works out. So there’s no need to worry about whether your polygon is convex or not, for example, or whether the origin is inside the polygon.
In the code above, we simply decompose into the triangles formed by consecutive vertices together with the origin.
See this Wikipedia page for more.
The centroid of a serie of ALIGNED points is the mean of them.
For a closed. non-self-intersecting, polygon you must use some more compliated formulas
See "Of a polygon" at wikipedia centroid
A note aside. I guess you also need the inertia of the beam. For that I advice to decompese into rentangles and triangles and then use the Steiner’s Theorema https://es.wikipedia.org/wiki/Teorema_del_eje_paralelo
Very good and detailed answers have been already provided. In case you do not want to do the math from scratch, I highly suggest to use the shapely library when dealing with polygons.
To compute the centroid of the polygon given the coordinates of its vertices, you can do the following:
from shapely.geometry import Polygon
polygon = Polygon([(0,0), (70,0), (70,25), (45, 45), (45, 180), (95, 188), (95, 200), (-25, 200), (-25,188), (25,180), (25,45), (0, 25)])
print(polygon.centroid)
>>> POINT (35 100.46145124716553)
I want to calculate the centroid of a figure formed by the points: (0,0), (70,0), (70,25), (45, 45), (45, 180), (95, 188), (95, 200), (-25, 200), (-25,188), (25,180), (25,45), (0, 25), (0,0).
I know that the correct result for the centroid of this polygon is x = 35 and y = 100.4615, but the code below does not return the correct values (figure of the polygon below).
import numpy as np
import matplotlib.pyplot as plt
poligono = np.array([(0,0), (70,0), (70,25), (45,45), (45,180), (95,188), (95,200), (-25,200), (-25, 188), (25,180), (25,45), (0,25), (0,0)])
x = pontos[:, 0]
y = pontos[:, 1]
points_array_2 = np.array(points)
centroid = np.mean(points_array_2, axis=0)
print("Centroide:", centroid)
I would like to know if someone can help me correctly calculate the centroid of the polygon.
Fixed:
def centroid(vertices):
x, y = 0, 0
n = len(vertices)
signed_area = 0
for i in range(len(vertices)):
x0, y0 = vertices[i]
x1, y1 = vertices[(i + 1) % n]
# shoelace formula
area = (x0 * y1) - (x1 * y0)
signed_area += area
x += (x0 + x1) * area
y += (y0 + y1) * area
signed_area *= 0.5
x /= 6 * signed_area
y /= 6 * signed_area
return x, y
x, y = centroid(vertices)
print(x, y)
Produces:
35.0 100.46145124716553
Here’s some code. Explanations follow.
import numpy as np
polygon = np.array(
[
(0,0), (70,0), (70,25), (45, 45), (45, 180), (95, 188),
(95, 200), (-25, 200), (-25,188), (25,180), (25,45), (0, 25),
],
dtype=np.float64,
)
# Same polygon, but with vertices cycled around. Now the polygon
# decomposes into triangles of the form origin-polygon[i]-polygon2[i]
polygon2 = np.roll(polygon, -1, axis=0)
# Compute signed area of each triangle
signed_areas = 0.5 * np.cross(polygon, polygon2)
# Compute centroid of each triangle
centroids = (polygon + polygon2) / 3.0
# Get average of those centroids, weighted by the signed areas.
centroid = np.average(centroids, axis=0, weights=signed_areas)
print("Centroid:", centroid)
The result of running this code on my machine is the one you expect:
$ python polygon_centroid.py
Centroid: [ 35. 100.46145125]
The basic idea is that we decompose the shape into triangles. We can compute the centroid for each triangle via the obvious formula (it’s the mean of the vertices). To compute the centroid of the whole polygon, we just have to compute the average of the centroids of the constituent triangles, with each triangle weighted by its (signed) area.
What makes this task especially easy for a polygon is that we can allow ourselves to include "negative" triangles – that is, we can express the polygon as a sum and difference of triangles. Provided we keep track of the signs in the areas, everything works out. So there’s no need to worry about whether your polygon is convex or not, for example, or whether the origin is inside the polygon.
In the code above, we simply decompose into the triangles formed by consecutive vertices together with the origin.
See this Wikipedia page for more.
The centroid of a serie of ALIGNED points is the mean of them.
For a closed. non-self-intersecting, polygon you must use some more compliated formulas
See "Of a polygon" at wikipedia centroid
A note aside. I guess you also need the inertia of the beam. For that I advice to decompese into rentangles and triangles and then use the Steiner’s Theorema https://es.wikipedia.org/wiki/Teorema_del_eje_paralelo
Very good and detailed answers have been already provided. In case you do not want to do the math from scratch, I highly suggest to use the shapely library when dealing with polygons.
To compute the centroid of the polygon given the coordinates of its vertices, you can do the following:
from shapely.geometry import Polygon
polygon = Polygon([(0,0), (70,0), (70,25), (45, 45), (45, 180), (95, 188), (95, 200), (-25, 200), (-25,188), (25,180), (25,45), (0, 25)])
print(polygon.centroid)
>>> POINT (35 100.46145124716553)