pythonshapelywkt

WKT moving polygon to center


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


Solution

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

    enter image description here See, your example polygon doesn't have any hole cut in it, so the interior points are not taken into consideration.

    Moving both interior and exterior points to the center

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

    enter image description here