GeoPandas – Fixing Holes in Multi-Polygon Using Explode in GeoPandas

explodegeopandasoverlay

I've run into an issue with the Geopandas explode method. When I run the explode method, multi-polygons which originally had holes are filled inexplicably. I need to expolode the multi-polygons in a geodataframe which represent jurisdictional boundaries prior to running a spatial overlay process in which I hope to assign jurisdiction to polygons in another geodataframe.

Here is my code:

jurs_url = os.path.join(
    "https://",
    "services3.arcgis.com",
    "i2dkYWmb4wHvYPda",
    "arcgis",
    "rest",
    "services",
    "region_jurisdiction_clp",
    "FeatureServer",
    "0",
    "query?outFields=*&where=1%3D1&f=geojson",
)

jurs_gdf = gpd.read_file(jurs_url).to_crs("EPSG:26910")
jurs_explode_gdf = jurs_gdf.explode(index_parts=True)

Here is a visualization of the issue using a subset of the geodataframe:

Visualization of original gdf

jurs_gdf[jurs_gdf['jurname'] == 'Unincorporated Sonoma'].explore()

holes

Visualization of the exploded gdf

(
    jurs_gdf[jurs_gdf["jurname"] == "Unincorporated Sonoma"].explode(
        index_parts=True
    )
).explore()

enter image description here

I tried to set indexing parts to false and also attempted to ignore the index when I ran the explode method but this did not seem to make a difference. How can I avoid this unintended behavior when running the explode method?

Best Answer

If geometries are checked and fixed beforehand, the explode method appears well-behaved. Shapely has a function for enforcing validity, addressing self-intersections and other infractions. Here's a working example:

from shapely.validation import explain_validity, make_valid

jurs_url = os.path.join(
    "https://",
    "services3.arcgis.com",
    "i2dkYWmb4wHvYPda",
    "arcgis",
    "rest",
    "services",
    "region_jurisdiction_clp",
    "FeatureServer",
    "0",
    "query?outFields=*&where=1%3D1&f=geojson",
)

juris_gdf = gpd.read_file(jurs_url).to_crs("EPSG:26910")

print("Before: Invalid geoms", juris_gdf[~juris_gdf.is_valid].shape[0])

print(juris_gdf.geometry.apply(explain_validity).value_counts())

juris_gdf['geometry_new'] = juris_gdf.geometry.apply(make_valid)
juris_gdf = juris_gdf.set_geometry('geometry_new')

print("After: Invalid geoms", juris_gdf[~juris_gdf.is_valid].shape[0])

jurs_explode_gdf = juris_gdf.explode(index_parts=True).reset_index().rename(
    columns={'level_0': 'orig_id', 'level_1': 'orig_id_component'})

(
    jurs_explode_gdf[jurs_explode_gdf["jurname"] == "Unincorporated Sonoma"]
).explore()

enter image description here