I have a WKT object and want to move its center to the origin (0,0). Here is an example and what I tried:
from shapely import wkt
poly_str = 'POLYGON ((14.217343909259455 -2.9030822376560224, 16.003619392313993 -2.639545672126154, 16.363681477720576 -5.080080154489572, 14.577405994666037 -5.34361672001944, 14.217343909259455 -2.9030822376560224))'
geom = wkt.loads(poly_str)
normalized = geom.normalize() # this does nothing
normalized == geom # TRUE
centroid = geom.centroid
moved_geom = geom - centroid # this seems logical, but does not achieve what I want
print(moved)
>>>> 'POLYGON ((16.003619392313993 -2.639545672126154, 16.363681477720576 -5.080080154489572, 14.577405994666037 -5.34361672001944, 14.217343909259455 -2.9030822376560224, 16.003619392313993 -2.639545672126154))'
Why is that last polygon not moved by the amount of the centroid and how would I obtain a shifted polygon from my original whose centroid would be at (0,0)?
You are actually on the right track, but there's a mistake in how you're calculating the moved geometry. The subtraction operation geom - centroid
is not directly supported in Shapely for moving geometries. Instead, you should manually translate the coordinates of each point. This should be your solution:
from matplotlib import pyplot as plt
from shapely import wkt
from shapely.affinity import translate
poly_str = 'POLYGON ((14.217343909259455 -2.9030822376560224, 16.003619392313993 -2.639545672126154, 16.363681477720576 -5.080080154489572, 14.577405994666037 -5.34361672001944, 14.217343909259455 -2.9030822376560224))'
geom = wkt.loads(poly_str)
# Calculate the centroid
centroid = geom.centroid
# Calculate the translation vector
translation_vector = (-centroid.x, -centroid.y)
# Perform the translation on each point
moved_coords = [(x + translation_vector[0], y + translation_vector[1]) for x, y in geom.exterior.coords]
# Create a new geometry with the moved coordinates
moved_geom = wkt.loads('POLYGON ((' + ', '.join([f'{x} {y}' for x, y in moved_coords]) + '))')
print("Original GEOM: \n",geom.wkt)
print("Moved GEOM: \n",moved_geom.wkt)
# Plot the original and moved geometries
plt.figure(figsize=(10, 5))
# Plot the original geometry
plt.subplot(1, 2, 1)
plt.plot(*geom.exterior.xy, label='Original Exterior', color='blue')
for interior in geom.interiors:
plt.plot(*interior.xy, label='Original Interior', color='red')
plt.title('Original Geometry')
plt.legend()
# Plot the moved geometry
plt.subplot(1, 2, 2)
plt.plot(*moved_geom.exterior.xy, label='Moved Exterior', color='blue')
for interior in moved_geom.interiors:
plt.plot(*interior.xy, label='Moved Interior', color='red')
plt.title('Moved Geometry')
plt.legend()
plt.tight_layout()
plt.show()
Output:
Original GEOM:
POLYGON ((14.217343909259455 -2.9030822376560224, 16.003619392313993 -2.639545672126154, 16.363681477720576 -5.080080154489572, 14.577405994666037 -5.34361672001944, 14.217343909259455 -2.9030822376560224))
Moved GEOM:
POLYGON ((-1.0731687842305604 1.0884989584167752, 0.713106698823978 1.3520355239466437, 1.0731687842305604 -1.0884989584167744, -0.713106698823978 -1.3520355239466428, -1.0731687842305604 1.0884989584167752))
See, your example polygon doesn't have any hole cut in it, so the interior points are not taken into consideration.
If you have a polygon with both interior and exterior points, here's how you move it to the center
import matplotlib.pyplot as plt
from shapely import wkt
from shapely.affinity import translate
# Define the polygon string with a hole
poly_str = 'POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10), (12 12, 18 12, 18 18, 12 18, 12 12))'
# Load the original geometry
geom = wkt.loads(poly_str)
# Calculate the centroid
centroid = geom.centroid
# Calculate the translation vector
translation_vector = (-centroid.x, -centroid.y)
# Perform the translation on each point (exterior and interior)
moved_coords = []
for ring in geom.interiors: # Process interior rings (holes)
moved_ring = [(x + translation_vector[0], y + translation_vector[1]) for x, y in ring.coords]
moved_coords.append(moved_ring)
moved_exterior = [(x + translation_vector[0], y + translation_vector[1]) for x, y in geom.exterior.coords]
moved_coords.append(moved_exterior)
# Construct the moved geometry using its WKT representation
moved_geom_wkt = 'POLYGON ((' + '), ('.join([', '.join([f'{x} {y}' for x, y in ring]) for ring in moved_coords]) + '))'
moved_geom = wkt.loads(moved_geom_wkt)
print("Original GEOM: \n",geom.wkt)
print("Moved GEOM: \n",moved_geom.wkt)
# Plot the original and moved geometries
plt.figure(figsize=(10, 5))
# Plot the original geometry
plt.subplot(1, 2, 1)
plt.plot(*geom.exterior.xy, label='Original Exterior', color='blue')
for interior in geom.interiors:
plt.plot(*interior.xy, label='Original Interior', color='red')
plt.title('Original Geometry')
plt.legend()
# Plot the moved geometry
plt.subplot(1, 2, 2)
plt.plot(*moved_geom.exterior.xy, label='Moved Exterior', color='blue')
for interior in moved_geom.interiors:
plt.plot(*interior.xy, label='Moved Interior', color='red')
plt.title('Moved Geometry')
plt.legend()
plt.tight_layout()
plt.show()
Output:
Original GEOM:
POLYGON ((10 10, 20 10, 20 20, 10 20, 10 10), (12 12, 18 12, 18 18, 12 18, 12 12))
Moved GEOM:
POLYGON ((-3 -3, 3 -3, 3 3, -3 3, -3 -3), (-5 -5, 5 -5, 5 5, -5 5, -5 -5))