Polygon Issues – Fix Bowtie or Hourglass Polygon Validity When Self-Crossing Point is Not Defined

polygonpythonself-intersectionshapely

Using Python2.7 and shapely, let:

import shapely
coords = [(0, 0), (0, 2), (2, 0), (2, 2), (0, 0)]
bowtie = shapely.geometry.Polygon(coords)
bowtie.is_valid

which gives this :

Self-intersection at or near point 1 1  
Out[2]: False

So let try the buffer(0) method as describer in the doc (http://toblerity.org/shapely/manual.html):

clean = bowtie.buffer(0)
type(clean)

which gives that:

Out[4]: shapely.geometry.polygon.Polygon

The difference between the example in the doc is that in the present case, the crossing point is not defined in the original coordinates, so it's not resulting in a multipolygon and it's not really consistent with these initial coordinates, as this command:

clean.wkt

shows when it returns:

Out[16]: 'POLYGON ((0 0, 0 2, 1 1, 0 0))'

And the resulting image for a more visual understanding:

bowtie cleaned polygon vs original self-intersecting bowtie polygon

With:
Red: original self-intersecting polygon
Teal: cleaned bowtie polygon with buffer(0) method (only one half of the original polygon. Take care of the different scaling effect on iPython…)

Question:
How to easily get the original shape back with a valid geometry so that we can perform further operations like intersections, etc?

Best Answer

A different hack would be using only the exterior of the bowtie, intersecting it with itself (generating a MultiLineString), polygonizing that (generating Polygons) and finally aggregating into a MultiPolygon:

be = bowtie.exterior
mls = be.intersection(be)
polygons = polygonize(mls)
valid_bowtie = MultiPolygon(polygons)
print(valid_bowtie)

MULTIPOLYGON (((0 0, 0 2, 1 1, 0 0)), ((2 0, 1 1, 2 2, 2 0)))
Related Question