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:
>>> 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)?
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)]
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:
>>> 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)?
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
Shapely implemented a solution for this matter. Through pip you can use shapely 1.8a3 version and import this way:
from shapely.validation import make_valid
Unfortunately, if you have had to install other libraries via conda such as geopandas you will probably face a dependency problem because at this point conda only offers shapely 1.7.1 version. So you can use the shapely solution at your program 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))
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, ie 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).
simplest solution
new_polygon = pp.buffer(0)
new_polygon.is_valid
maybe it would be work.
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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.