0

I have a polygon shapefile which I am reading with geopandas. The polygon contains holes. My purpose is to add a random point inside each hole. In order to create the points inside the holes (interior polygon rings), the definition of their coordinates needs to be reversed (from counterclockwise to clockwise) otherwise the point falls outside of the polygon.

I get the coordinates of the interior rings using GeoSeries.interiors and what I get is

LINEARRING (85002.811 446988.023, 85010.79399999999 446992.869, 85005.61900000001 447001.417, 84982.78599999999 446987.513, 84987.933 446978.99, 85001.89999999999 446987.47, 85002.811 446988.023)

How can I reverse the definition of the coordinates? Alternatively, I could get the x,y coordinates in a list and reverse it, but even in this case I cannot find a way to convert the geometry to list.

  • Can you post the entire geometry, with the interior rings inside? – wfgeo Apr 10 '19 at 20:52
  • You can use shapely for that. Geopandas uses fiona, a gdal/ogr wrapper for python for reading and writing from various geodata file formats. As far as i know geopandas does not support any geometric operations, but Shapely does. – Andreas Müller Apr 10 '19 at 21:08
1

Since the orientation of the exterior ring doesn't matter, you could use the shapely.geometry.polygon.orient function, which will orient the interiors colckwise or counterclockwise.

Simple example:

from shapely.geometry import Polygon                                    
from shapely.geometry import polygon                                   
pol = Polygon([(0,0),(1,0),(1,1),(0,1)],[[(0.1,0.1),(0.1,0.2),(0.2,0.2)]])

[[*p.coords] for p in polygon.orient(pol,-1).interiors]                
#Out[]: [[(0.1, 0.1), (0.2, 0.2), (0.1, 0.2), (0.1, 0.1)]]
[[*p.coords] for p in polygon.orient(pol,1).interiors]                 
#Out[]: [[(0.1, 0.1), (0.1, 0.2), (0.2, 0.2), (0.1, 0.1)]]

In geopandas you will map the function over the gemetry column with:

new_geometry_series = dataframe.geometry.apply(polygon.orient,args=(1)) #or -1
0

This should do what you want, and works in cases where a polygon has multiple holes. It involves using shapely, which is a geopandas dependency.

Explanations and further reading are in the comments.

import geopandas as gpd
from shapely.geometry import Polygon

# Initializing a polygon with two holes
# See: https://stackoverflow.com/questions/48770822/how-to-make-holes-in-a-polygon-in-shapely-python-having-polygons
outer = Polygon(((0,0),(20,0),(20,20),(0,20),(0,0)))
inners = (Polygon(((4,4),(4,6),(6,6),(6,4),(4,4))), Polygon(((7,7),(7,8),(8,8),(8,7),(7,7))))
p = Polygon(outer.exterior.coords, [inner.exterior.coords for inner in inners])

# Make a geoseries with this polygon
gs = gpd.GeoSeries({0: p})

print("Before:")
print(gs[0])

# Get the interiors and initialize a list for new interior polygons
interiors = gs.interiors
reversed_interior_polys = []

# Iterate through the interiors, and through their subsequent rings
for interior in interiors:
    for ring in interior:
        # Unpack the ring's coordinates and convert it to a polygon
        # See: https://shapely.readthedocs.io/en/stable/manual.html#object.is_ccw
        interior_poly_reversed = Polygon(list(ring.coords)[::-1])
        reversed_interior_polys.append(interior_poly_reversed)
reversed_interior_polys = tuple(reversed_interior_polys)

# Reconstruct the original polygon from the list reversed polygons we just made
# See (again): https://stackoverflow.com/questions/48770822/how-to-make-holes-in-a-polygon-in-shapely-python-having-polygons
fixed_polygon = Polygon(outer.exterior.coords, [inner.exterior.coords for inner in reversed_interior_polys])

# Map this value back to the GeoSeries
gs[0] = fixed_polygon

print("\nAfter:")
print(gs[0])

Output:

Before:
POLYGON ((0 0, 20 0, 20 20, 0 20, 0 0), (4 4, 4 6, 6 6, 6 4, 4 4), (7 7, 7 8, 8 8, 8 7, 7 7))

After:
POLYGON ((0 0, 20 0, 20 20, 0 20, 0 0), (4 4, 6 4, 6 6, 4 6, 4 4), (7 7, 8 7, 8 8, 7 8, 7 7))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.