Reputation: 421
I'm aiming to plot the boundaries of clusters of MSOAs (contiguous geographical units in UK) to do so I've downloaded a shapefile of MSOA boundaries from here. I then add a column of cluster labels and dissolve using geopandas.
df.dissolve(by='label', aggfunc='sum')
When I use Folium to plot there are multiple inner holes as seen in the attached image. How do I remove these?
#creates map
m = folium.Map([54.5,-3],zoom_start=6.8,tiles='cartodbpositron')
#makes boundaries plot
Boundaries = folium.GeoJson(
df,
name='Boundaries',
style_function = lambda x: {
'color': 'black',
'weight': 3,
'fillOpacity': 0
}).add_to(m)
m
Upvotes: 4
Views: 2865
Reputation: 459
This will hopefully help you to use just geopandas to organize your polygons. You can just overwrite the geometry using the functions below. Extra handling is used to preserve or reduce MultiPolygons. I would imagine that a very similar thing is happening with MapShaper, but this way you don't need to do the extra handling.
UPDATES FROM COMMENTS BELOW APPLIED
from shapely.geometry import MultiPolygon, Polygon
def remove_interiors(poly):
"""
Close polygon holes by limitation to the exterior ring.
Arguments
---------
poly: shapely.geometry.Polygon
Input shapely Polygon
Returns
---------
Polygon without any interior holes
"""
if poly.interiors:
return Polygon(list(poly.exterior.coords))
else:
return poly
def pop_largest(gs):
"""
Pop the largest polygon off of a GeoSeries
Arguments
---------
gs: geopandas.GeoSeries
Geoseries of Polygon or MultiPolygon objects
Returns
---------
Largest Polygon in a Geoseries
"""
geoms = [g.area for g in gs]
return gs.pop(geoms.index(max(geoms)))
def close_holes(geom):
"""
Remove holes in a polygon geometry
Arguments
---------
gseries: geopandas.GeoSeries
Geoseries of Polygon or MultiPolygon objects
Returns
---------
Largest Polygon in a Geoseries
"""
if isinstance(geom, MultiPolygon):
ser = gpd.GeoSeries([remove_interiors(g) for g in geom.geoms])
big = pop_largest(ser)
outers = ser.loc[~ser.within(big)].tolist()
if outers:
return MultiPolygon([big] + outers)
return Polygon(big)
if isinstance(geom, Polygon):
return remove_interiors(geom)
df.geometry = df.geometry.apply(lambda p: close_holes(p))
Upvotes: 4
Reputation: 421
In case anyone encounters the same problem I found a website which you can upload, simplify and export shape files from called mapshaper this managed to simplify my boundaries to the required form.
Upvotes: 2