Dom McEwen
Dom McEwen

Reputation: 421

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

Upvotes: 4

Views: 2865

Answers (2)

rick debbout
rick debbout

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

Dom McEwen
Dom McEwen

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

Related Questions