(
    buildings, size=30, compactness=0.2, islands=True, loops=2, verbose=True
)
return blg[buildings.columns]
Preprocesses building geometry to eliminate additional structures being single
    features.

    Certain data providers (e.g. Ordnance Survey in GB) do not provide building geometry
    as one feature, but divided into different features depending their level (if they
    are on ground floor or not - passages, overhangs). Ideally, these features should
    share one building ID on which they could be dissolved. If this is not the case,
    series of steps needs to be done to minimize errors in morphological analysis.

    This script attempts to preprocess such geometry based on several condidions:
    If feature area is smaller than set size it will be a) deleted if it does not
    touch any other feature; b) will be joined to feature with which it shares the
    longest boundary. If feature is fully within other feature, these will be joined.
    If feature's circular compactness (:py:class:`momepy.CircularCompactness`)
    is < 0.2, it will be joined to feature with which it shares the longest boundary.
    Function does multiple loops through.


    Parameters
    ----------
    buildings : geopandas.GeoDataFrame
        geopandas.GeoDataFrame containing building layer
    size : float (default 30)
        maximum area of feature to be considered as additional structure. Set to
        None if not wanted.
    compactness : float (default .2)
        if set, function will resolve additional structures identified based on
        their circular compactness.
    islands : bool (default True)
        if True, function will resolve additional structures which are fully within
        other structures (share 100% of exterior boundary).
    loops : int (default 2)
        number of loops
    verbose : bool (default True)
        if True, shows progress bars in loops and indication of steps

    Returns
    -------
    GeoDataFrame
        GeoDataFrame containing preprocessed geometry
Preprocesses building geometry to eliminate additional structures being single
    features.
Preprocesses building geometry to eliminate additional structures being single features .
def preprocess(
    buildings, size=30, compactness=0.2, islands=True, loops=2, verbose=True
):
    """
    Preprocesses building geometry to eliminate additional structures being single
    features.

    Certain data providers (e.g. Ordnance Survey in GB) do not provide building geometry
    as one feature, but divided into different features depending their level (if they
    are on ground floor or not - passages, overhangs). Ideally, these features should
    share one building ID on which they could be dissolved. If this is not the case,
    series of steps needs to be done to minimize errors in morphological analysis.

    This script attempts to preprocess such geometry based on several condidions:
    If feature area is smaller than set size it will be a) deleted if it does not
    touch any other feature; b) will be joined to feature with which it shares the
    longest boundary. If feature is fully within other feature, these will be joined.
    If feature's circular compactness (:py:class:`momepy.CircularCompactness`)
    is < 0.2, it will be joined to feature with which it shares the longest boundary.
    Function does multiple loops through.


    Parameters
    ----------
    buildings : geopandas.GeoDataFrame
        geopandas.GeoDataFrame containing building layer
    size : float (default 30)
        maximum area of feature to be considered as additional structure. Set to
        None if not wanted.
    compactness : float (default .2)
        if set, function will resolve additional structures identified based on
        their circular compactness.
    islands : bool (default True)
        if True, function will resolve additional structures which are fully within
        other structures (share 100% of exterior boundary).
    loops : int (default 2)
        number of loops
    verbose : bool (default True)
        if True, shows progress bars in loops and indication of steps

    Returns
    -------
    GeoDataFrame
        GeoDataFrame containing preprocessed geometry
    """
    blg = buildings.copy()
    if GPD_10:
        blg = blg.explode(ignore_index=True)
    else:
        blg = blg.explode()
        blg.reset_index(drop=True, inplace=True)
    for loop in range(0, loops):
        print("Loop", loop + 1, f"out of {loops}.") if verbose else None
        blg.reset_index(inplace=True, drop=True)
        blg["mm_uid"] = range(len(blg))
        sw = libpysal.weights.contiguity.Rook.from_dataframe(blg, silence_warnings=True)
        blg["neighbors"] = sw.neighbors
        blg["neighbors"] = blg["neighbors"].map(sw.neighbors)
        blg["n_count"] = blg.apply(lambda row: len(row.neighbors), axis=1)
        blg["circu"] = CircularCompactness(blg).series

        # idetify those smaller than x with only one neighbor and attaches it to it.
        join = {}
        delete = []

        for row in tqdm(
            blg.itertuples(),
            total=blg.shape[0],
            desc="Identifying changes",
            disable=not verbose,
        ):
            if size:
                if row.geometry.area < size:
                    if row.n_count == 1:
                        uid = blg.iloc[row.neighbors[0]].mm_uid

                        if uid in join:
                            existing = join[uid]
                            existing.append(row.mm_uid)
                            join[uid] = existing
                        else:
                            join[uid] = [row.mm_uid]
                    elif row.n_count > 1:
                        shares = {}
                        for n in row.neighbors:
                            shares[n] = row.geometry.intersection(
                                blg.at[n, blg.geometry.name]
                            ).length
                        maximal = max(shares.items(), key=operator.itemgetter(1))[0]
                        uid = blg.loc[maximal].mm_uid
                        if uid in join:
                            existing = join[uid]
                            existing.append(row.mm_uid)
                            join[uid] = existing
                        else:
                            join[uid] = [row.mm_uid]
                    else:
                        delete.append(row.Index)
            if compactness:
                if row.circu < compactness:
                    if row.n_count == 1:
                        uid = blg.iloc[row.neighbors[0]].mm_uid
                        if uid in join:
                            existing = join[uid]
                            existing.append(row.mm_uid)
                            join[uid] = existing
                        else:
                            join[uid] = [row.mm_uid]
                    elif row.n_count > 1:
                        shares = {}
                        for n in row.neighbors:
                            shares[n] = row.geometry.intersection(
                                blg.at[n, blg.geometry.name]
                            ).length
                        maximal = max(shares.items(), key=operator.itemgetter(1))[0]
                        uid = blg.loc[maximal].mm_uid
                        if uid in join:
                            existing = join[uid]
                            existing.append(row.mm_uid)
                            join[uid] = existing
                        else:
                            join[uid] = [row.mm_uid]

            if islands:
                if row.n_count == 1:
                    shared = row.geometry.intersection(
                        blg.at[row.neighbors[0], blg.geometry.name]
                    ).length
                    if shared == row.geometry.exterior.length:
                        uid = blg.iloc[row.neighbors[0]].mm_uid
                        if uid in join:
                            existing = join[uid]
                            existing.append(row.mm_uid)
                            join[uid] = existing
                        else:
                            join[uid] = [row.mm_uid]

        for key in tqdm(
            join, total=len(join), desc="Changing geometry", disable=not verbose
        ):
            selection = blg[blg["mm_uid"] == key]
            if not selection.empty:
                geoms = [selection.iloc[0].geometry]

                for j in join[key]:
                    subset = blg[blg["mm_uid"] == j]
                    if not subset.empty:
                        geoms.append(blg[blg["mm_uid"] == j].iloc[0].geometry)
                        blg.drop(blg[blg["mm_uid"] == j].index[0], inplace=True)
                new_geom = shapely.ops.unary_union(geoms)
                blg.loc[
                    blg.loc[blg["mm_uid"] == key].index[0], blg.geometry.name
                ] = new_geom

        blg.drop(delete, inplace=True)
    return blg[buildings.columns]
def preprocess ( buildings , size = 30 , compactness = 0.2 , islands = True , loops = 2 , verbose = True ) : blg = buildings . copy ( ) if GPD_10 : blg = blg . explode ( ignore_index = True ) else : blg = blg . explode ( ) blg . reset_index ( drop = True , inplace = True ) for loop in range ( 0 , loops ) : print ( "Loop" , loop + 1 , f"out of {loops}." ) if verbose else None blg . reset_index ( inplace = True , drop = True ) blg [ "mm_uid" ] = range ( len ( blg ) ) sw = libpysal . weights . contiguity . Rook . from_dataframe ( blg , silence_warnings = True ) blg [ "neighbors" ] = sw . neighbors blg [ "neighbors" ] = blg [ "neighbors" ] . map ( sw . neighbors ) blg [ "n_count" ] = blg . apply ( lambda row : len ( row . neighbors ) , axis = 1 ) blg [ "circu" ] = CircularCompactness ( blg ) . series # idetify those smaller than x with only one neighbor and attaches it to it. join = { } delete = [ ] for row in tqdm ( blg . itertuples ( ) , total = blg . shape [ 0 ] , desc = "Identifying changes" , disable = not verbose , ) : if size : if row . geometry . area < size : if row . n_count == 1 : uid = blg . iloc [ row . neighbors [ 0 ] ] . mm_uid if uid in join : existing = join [ uid ] existing . append ( row . mm_uid ) join [ uid ] = existing else : join [ uid ] = [ row . mm_uid ] elif row . n_count > 1 : shares = { } for n in row . neighbors : shares [ n ] = row . geometry . intersection ( blg . at [ n , blg . geometry . name ] ) . length maximal = max ( shares . items ( ) , key = operator . itemgetter ( 1 ) ) [ 0 ] uid = blg . loc [ maximal ] . mm_uid if uid in join : existing = join [ uid ] existing . append ( row . mm_uid ) join [ uid ] = existing else : join [ uid ] = [ row . mm_uid ] else : delete . append ( row . Index ) if compactness : if row . circu < compactness : if row . n_count == 1 : uid = blg . iloc [ row . neighbors [ 0 ] ] . mm_uid if uid in join : existing = join [ uid ] existing . append ( row . mm_uid ) join [ uid ] = existing else : join [ uid ] = [ row . mm_uid ] elif row . n_count > 1 : shares = { } for n in row . neighbors : shares [ n ] = row . geometry . intersection ( blg . at [ n , blg . geometry . name ] ) . length maximal = max ( shares . items ( ) , key = operator . itemgetter ( 1 ) ) [ 0 ] uid = blg . loc [ maximal ] . mm_uid if uid in join : existing = join [ uid ] existing . append ( row . mm_uid ) join [ uid ] = existing else : join [ uid ] = [ row . mm_uid ] if islands : if row . n_count == 1 : shared = row . geometry . intersection ( blg . at [ row . neighbors [ 0 ] , blg . geometry . name ] ) . length if shared == row . geometry . exterior . length : uid = blg . iloc [ row . neighbors [ 0 ] ] . mm_uid if uid in join : existing = join [ uid ] existing . append ( row . mm_uid ) join [ uid ] = existing else : join [ uid ] = [ row . mm_uid ] for key in tqdm ( join , total = len ( join ) , desc = "Changing geometry" , disable = not verbose ) : selection = blg [ blg [ "mm_uid" ] == key ] if not selection . empty : geoms = [ selection . iloc [ 0 ] . geometry ] for j in join [ key ] : subset = blg [ blg [ "mm_uid" ] == j ] if not subset . empty : geoms . append ( blg [ blg [ "mm_uid" ] == j ] . iloc [ 0 ] . geometry ) blg . drop ( blg [ blg [ "mm_uid" ] == j ] . index [ 0 ] , inplace = True ) new_geom = shapely . ops . unary_union ( geoms ) blg . loc [ blg . loc [ blg [ "mm_uid" ] == key ] . index [ 0 ] , blg . geometry . name ] = new_geom blg . drop ( delete , inplace = True ) return blg [ buildings . columns ]