Fix invalid polygon in Shapely

Question:

Shapely defines a Polygon as invalid if any of its segments intersect, including segments that are colinear. Many software packages will create a region or area with a “cutout” as shown here which has colinear segments:

enter image description here

>>> pp = Polygon([(0,0), (0,3), (3,3), (3,0), (2,0), 
                  (2,2), (1,2), (1,1), (2,1), (2,0), (0,0)])
>>> pp.is_valid
WARNING:shapely.geos:Self-intersection at or near point 2 0
False

Naturally, the cutout can be implemented natively in Shapely, or this same geometry can be implemented as two valid polygons, but if I only have the list of points shown above, is there an easy to “fix” this (create valid geometry from this list of points)?

Asked By: jpcgt

||

Answers:

I found a solution that works for the specific case given:

>>> pp2 = pp.buffer(0)
>>> pp2.is_valid
True
>>> pp2.exterior.coords[:]
[(0.0, 0.0), (0.0, 3.0), (3.0, 3.0), (3.0, 0.0), (2.0, 0.0), (0.0, 0.0)]
>>> pp2.interiors[0].coords[:]
[(2.0, 1.0), (2.0, 2.0), (1.0, 2.0), (1.0, 1.0), (2.0, 1.0)]
Answered By: jpcgt

Untested, but it appears that Shapely have added a function to support this now.

https://shapely.readthedocs.io/en/latest/manual.html#validation.make_valid

Answered By: Aidan Kane

Shapely implemented a solution for this. Through pip you can use any shapely version >= 1.8a3 and import this way:

from shapely.validation import make_valid

The current version of shapely available via pip and conda includes the make_valid function. If you need to install an older version of shapely, you can use the shapely implementation as shown below:

def make_valid(ob):

    from shapely.geometry.base import geom_factory
    from shapely.geos import lgeos
    
    if ob.is_valid:
        return ob

    return geom_factory(lgeos.GEOSMakeValid(ob._geom))
Answered By: Rafael Matias

I have used the .buffer(0) method a lot, but it gave different results on Windows and on Linux. Therefore if you encounter such a problem, remember that:

Warning: The .buffer(0) function of shapely may behave different on different operating systems, i.e. Windows and Linux. I had examples, where on Linux an empty Polygon is returned while on Windows the correct non-empty Polygon is returned!

This problem took me days to solve, that’s why I want to add that as an answer (my suggested edit to the accepted answer was sadly rejected).

Answered By: LegNaiB

simplest solution

new_polygon = pp.buffer(0)
new_polygon.is_valid

maybe it would be work.

Answered By: minsu

This is my first attempt at a fix geometry function. I had to handle the special case where a polygons are converted to multipolygons, where make_valid doesn’t work b/c len(make_valid(feature)) !=1.

from shapely.validation import make_valid

def fix_geom(in_feature):

    # avoid changing original geodf
    in_feature = in_feature.copy(deep=True)    
        
    # drop any missing geometries
    in_feature = in_feature[~(in_feature.is_empty)]
    
    # Repair broken geometries
    for index, row in in_feature.iterrows(): # Looping over all polygons
        if row['geometry'].is_valid:
            next
        else:
            fix = make_valid(row['geometry'])

            try:
                in_feature.loc[[index],'geometry'] =  fix # issue with Poly > Multipolygon
            except ValueError:
                in_feature.loc[[index],'geometry'] =  in_feature.loc[[index], 'geometry'].buffer(0)
    return in_feature
Answered By: mmann1123
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.