def aggr_constrained_shp(unconstr_gdf, constr_gdf):
    """
    Returns identical aggregation for unconstrained dataframe to match that of contrained dataframe.

    Parameters:
    -----------
    unconstr_gdf    :   (gpd.GeoDataFrame)
        gdf of admin unit polygons with labels indicating how units will be aggregated BEFORE DISSOLVING (unconstr and constr should have the same number of rows)
    constr_gdf  :   (gpd.GeoDataFrame)
        gdf polygonised from admin units covering ONLY areas that are 'built'.

    Retruns:
    ---------
    constr_gdf    :   (gpd.GeoDataFrame)
        Unconstrained geodataframe aggregated to same level (as indicated by the constrained labels) as constrained geodataframe
    """
    constr_gdf = constr_gdf[['geometry']]
    unconstr_gdf = unconstr_gdf.reset_index()
    constr_gdf['labels'] = unconstr_gdf['labels']
    constr_gdf['adm_id'] = constr_gdf['labels']
    constr_gdf = constr_gdf[['adm_id','geometry']]
    constr_gdf = constr_gdf.dissolve(by='adm_id')
    return constr_gdf