Skip to content

shapely error for invalid polygon #1

@srose89

Description

@srose89

Received this error: TopologyException: unable to assign free hole to a shell at 5718.16259765625 3264.425048828125

Appears this could be due to self intersecting polygons.

Updated fix_invalid_geometry to the following to handle this and now sample completes successfully.

def fix_self_intersection(poly):
    """
    Attempts to fix self-intersecting polygons using buffer(0).
    Returns the fixed Polygon, or None if fixing failed or result is not a Polygon.
    """
    if poly.is_valid:
        return poly

    try:
        # buffer(0) is a common trick to fix self-intersections
        fixed_poly = poly.buffer(0)

        # buffer(0) might return MultiPolygon - take largest component
        if fixed_poly.geom_type == 'MultiPolygon':
            fixed_poly = max(fixed_poly.geoms, key=lambda p: p.area)
        
        # Ensure the result is actually a Polygon (not Point/LineString/Empty)
        if fixed_poly.geom_type == 'Polygon':
            return fixed_poly
        
        return None
    except Exception:
        return None

def fix_invalid_geometry(gdf: gpd.GeoDataFrame):
    """
    Fix invalid geometries by first attempting to resort coordinates,
    and then attempting to fix self-intersections via buffer(0) for those
    that remain invalid.
    """
    # 1. Identify initial invalid geometries
    mask = ~gdf.geometry.is_valid
    if not mask.any():
        return gdf
    
    # 2. First attempt: Resort coordinates
    fixed_step1 = gdf.loc[mask].geometry.apply(resort_coordinates)
    gdf.loc[mask, gdf.geometry.name] = fixed_step1

    # 3. Check if any are STILL invalid after resort
    sub_gdf = gdf.loc[mask]
    mask_still_invalid = ~sub_gdf.geometry.is_valid
    
    # If everything is fixed, we are done
    if not mask_still_invalid.any():
        return gdf

    # Get the global indices of rows that are still invalid
    problem_indices = sub_gdf[mask_still_invalid].index

    # 4. Second attempt: Fix self-intersections (buffer(0))
    fixed_step2 = gdf.loc[problem_indices].geometry.apply(fix_self_intersection)
    
    # Update the GeoDataFrame
    # Note: this will assign None if the fix failed.
    gdf.loc[problem_indices, gdf.geometry.name] = fixed_step2

    return gdf

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions