Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fill holes in Multi-polygons created when dissolving geodataframe with geopandas?

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

Visualisation Created

like image 422
Dom McEwen Avatar asked Sep 20 '25 00:09

Dom McEwen


2 Answers

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))
like image 140
rick debbout Avatar answered Sep 21 '25 13:09

rick debbout


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.

like image 24
Dom McEwen Avatar answered Sep 21 '25 14:09

Dom McEwen