Beispiel #1
0
def _cast(collection):
    """
    Cast a collection to a pygeos geometry array.
    """
    if isinstance(collection, (geopandas.GeoSeries, geopandas.GeoDataFrame)):
        return collection.geometry.values.data.squeeze()
    elif pygeos.is_geometry(collection).all():
        if isinstance(collection, (numpy.ndarray, list)):
            return numpy.asarray(collection)
        else:
            return numpy.array([collection])
    elif isinstance(collection, (numpy.ndarray, list)):
        return pygeos.from_shapely(collection).squeeze()
    else:
        return numpy.array([pygeos.from_shapely(collection)])
Beispiel #2
0
def _valid_hull(geoms, points):
    """Sanity check within ``alpha_shape_auto()`` to verify the generated alpha
    shape actually contains the original set of points (xys).

    Parameters
    ----------

    geoms : GeoSeries
        See alpha_geoms()

    points : list
        xys parameter cast as shapely.geometry.Point objects

    Returns
    -------

    flag : bool
        Valid hull for alpha shape [True] or not [False]

    """
    flag = True
    # if there is not exactly one polygon
    if geoms.shape[0] != 1:
        return False
    # if any (xys) points do not intersect the polygon
    if HAS_PYGEOS:
        return pygeos.intersects(pygeos.from_shapely(geoms[0]), points).all()
    else:
        for point in points:
            if not point.intersects(geoms[0]):
                return False
        return True
Beispiel #3
0
def from_shapely(data):
    """
    Convert a list or array of shapely objects to an object-dtype numpy
    array of validated geometry elements.

    """
    # First try a fast path for pygeos if possible, but do this in a try-except
    # block because pygeos.from_shapely only handles Shapely objects, while
    # the rest of this function is more forgiving (also __geo_interface__).
    if compat.USE_PYGEOS and compat.PYGEOS_SHAPELY_COMPAT:
        if not isinstance(data, np.ndarray):
            arr = np.empty(len(data), dtype=object)
            with compat.ignore_shapely2_warnings():
                arr[:] = data
        else:
            arr = data
        try:
            return pygeos.from_shapely(arr)
        except TypeError:
            pass

    out = []

    for geom in data:
        if compat.USE_PYGEOS and isinstance(geom, pygeos.Geometry):
            out.append(geom)
        elif isinstance(geom, BaseGeometry):
            if compat.USE_PYGEOS:
                out.append(_shapely_to_pygeos(geom))
            else:
                out.append(geom)
        elif hasattr(geom, "__geo_interface__"):
            geom = shapely.geometry.asShape(geom)
            # asShape returns GeometryProxy -> trigger actual materialization
            # with one of its methods
            geom.wkb
            if compat.USE_PYGEOS:
                out.append(_shapely_to_pygeos(geom))
            else:
                out.append(geom)
        elif _isna(geom):
            out.append(None)
        else:
            raise TypeError(
                "Input must be valid geometry objects: {0}".format(geom))

    if compat.USE_PYGEOS:
        return np.array(out, dtype=object)
    else:
        # numpy can expand geometry collections into 2D arrays, use this
        # two-step construction to avoid this
        aout = np.empty(len(data), dtype=object)
        with compat.ignore_shapely2_warnings():
            aout[:] = out
        return aout
Beispiel #4
0
def _cast(collection):
    """
    Cast a collection to a pygeos geometry array.
    """
    try:
        import pygeos, geopandas
    except (ImportError, ModuleNotFoundError) as exception:
        raise type(exception)(
            "pygeos and geopandas are required for map comparison statistics.")

    if isinstance(collection, (geopandas.GeoSeries, geopandas.GeoDataFrame)):
        return collection.geometry.values.data.squeeze()
    elif pygeos.is_geometry(collection).all():
        if isinstance(collection, (numpy.ndarray, list)):
            return numpy.asarray(collection)
        else:
            return numpy.array([collection])
    elif isinstance(collection, (numpy.ndarray, list)):
        return pygeos.from_shapely(collection).squeeze()
    else:
        return numpy.array([pygeos.from_shapely(collection)])
Beispiel #5
0
def _shapely_to_pygeos(geom):
    if geom is None:
        return None

    if compat.PYGEOS_SHAPELY_COMPAT:
        return pygeos.from_shapely(geom)

    # fallback going through WKB
    if geom.is_empty and geom.geom_type == "Point":
        # empty point does not roundtrip through WKB
        return pygeos.from_wkt("POINT EMPTY")
    else:
        return pygeos.from_wkb(geom.wkb)
Beispiel #6
0
def get_gdp_values(gdf, data_path):
    """[summary]

    Args:
        gdf ([type]): [description]

    Returns:
        [type]: [description]
    """
    world_pop = os.path.join(data_path, 'global_gdp', 'GDP_2015.tif')
    gdf['geometry'] = gdf.geometry.apply(lambda x: loads(pygeos.to_wkb(x)))
    gdp = list(item['sum']
               for item in zonal_stats(gdf.geometry, world_pop, stats="sum"))
    gdp = [x if x is not None else 0 for x in gdp]
    gdf['geometry'] = pygeos.from_shapely(gdf.geometry)
    return gdp
Beispiel #7
0
def country_grid_gdp_filled(trans_network,
                            country,
                            data_path,
                            rough_grid_split=100,
                            from_main_graph=False):
    """[summary]

    Args:
        trans_network ([type]): [description]
        rough_grid_split (int, optional): [description]. Defaults to 100.

    Returns:
        [type]: [description]
    """
    if from_main_graph == True:
        node_df = trans_network.copy()
        envelop = pygeos.envelope(
            pygeos.multilinestrings(node_df.geometry.values))
        height = np.sqrt(pygeos.area(envelop) / rough_grid_split)
    else:
        node_df = trans_network.nodes.copy()
        node_df.geometry, approximate_crs = convert_crs(node_df)
        envelop = pygeos.envelope(
            pygeos.multilinestrings(node_df.geometry.values))
        height = np.sqrt(pygeos.area(envelop) / rough_grid_split)

    gdf_admin = pd.DataFrame(create_grid(create_bbox(node_df), height),
                             columns=['geometry'])

    #load data and convert to pygeos
    country_shape = gpd.read_file(os.path.join(data_path, 'GADM',
                                               'gadm36_levels.gpkg'),
                                  layer=0)
    country_shape = pd.DataFrame(
        country_shape.loc[country_shape.GID_0 == country])
    country_shape.geometry = pygeos.from_shapely(country_shape.geometry)

    gdf_admin = pygeos.intersection(gdf_admin, country_shape.geometry)
    gdf_admin = gdf_admin.loc[~pygeos.is_empty(gdf_admin.geometry)]

    gdf_admin['centroid'] = pygeos.centroid(gdf_admin.geometry)
    gdf_admin['km2'] = area(gdf_admin)
    gdf_admin['gdp'] = get_gdp_values(gdf_admin, data_path)
    gdf_admin = gdf_admin.loc[gdf_admin.gdp > 0].reset_index()
    gdf_admin['gdp_area'] = gdf_admin.gdp / gdf_admin['km2']

    return gdf_admin
Beispiel #8
0
def test_build_adjacent_matrix_pygeo():
    vector_path = os.path.expanduser(
        '~/Data/dem_processing/grid_9053_tmp_files/20140701_dem_slope_bin_patches_all_polyTouchEdge.gpkg'
    )
    polygons = vector_gpd.read_polygons_gpd(vector_path,
                                            b_fix_invalid_polygon=False)
    print('read %d polygons' % len(polygons))
    # polygons = [item.buffer(1.0) for item in polygons]    # for complex polygons, buffer take a long time.
    from pygeos import area, intersection, from_shapely, touches
    import numpy as np
    geo_polygons = from_shapely(polygons)
    print(geo_polygons)

    # inter_matrix = intersection(geo_polygons[:, np.newaxis], geo_polygons[np.newaxis, :])
    # print(inter_matrix)

    # print(touches(geo_polygons[:, np.newaxis], geo_polygons[np.newaxis, :]))
    print(touches(geo_polygons, geo_polygons))
Beispiel #9
0
def create_OD_points():
    """[summary]
    """      
    #load data and convert to pygeos
    gdf = gpd.read_file(r'/scistor/ivm/data_catalogue/open_street_map/GADM36/gadm36_levels.gpkg',layer=0)
    tqdm.pandas(desc='Convert geometries to pygeos')
    gdf = pd.DataFrame(gdf)
    gdf['geometry'] = gdf.geometry.progress_apply(dumps)
    
    # create OD points
    save_points_per_country = []
    list_countries = list([x[1] for x in gdf.iterrows()])
    
    #multiprocess all countries
    with Pool(cpu_count()-1) as pool: 
        save_points_per_country = pool.map(create_country_OD_points,list_countries,chunksize=1) 
           
    df_all = pd.concat(save_points_per_country)
    df_all['geometry'] = df_all.geometry.progress_apply(lambda x: pygeos.from_shapely(x))
    df_all.reset_index(inplace=True,drop=True)
    df_all.to_csv('od_points_per_country.csv')
Beispiel #10
0
def test_from_shapely_arr():
    actual = pygeos.from_shapely([ShapelyGeometryMock(point), None])
    assert pygeos.equals(point, actual[0])
Beispiel #11
0
def test_from_shapely_incompatible_prepared(geom):
    actual = pygeos.from_shapely(ShapelyPreparedMock(geom))
    assert isinstance(actual, pygeos.Geometry)
    assert pygeos.equals(geom, actual)
    assert geom._ptr != actual._ptr
Beispiel #12
0
def test_from_shapely_incompatible_none():
    actual = pygeos.from_shapely(None)
    assert actual is None
Beispiel #13
0
def verify(g, shp, thorough=False):
    #---------------------------------------------------------------------
    logger.info(' Verify grid against coastline\n')
    #---------------------------------------------------------------------

    lon_min = g.Dataset.SCHISM_hgrid_node_x.values.min()
    lon_max = g.Dataset.SCHISM_hgrid_node_x.values.max()
    lat_min = g.Dataset.SCHISM_hgrid_node_y.values.min()
    lat_max = g.Dataset.SCHISM_hgrid_node_y.values.max()

    c = shp.cx[lon_min:lon_max, lat_min:lat_max]

    # ## Test polygons

    d = g.Dataset

    x = d.SCHISM_hgrid_node_x.values
    y = d.SCHISM_hgrid_node_y.values
    tri = d.SCHISM_hgrid_face_nodes.values

    nodes = pd.DataFrame({'lon': x, 'lat': y})

    elems = pd.DataFrame(tri, columns=['a', 'b', 'c'])

    bnodes = g.Dataset[['node', 'id', 'type']].to_dataframe()

    # ### Find the invalid nodes (that cross the coasts)
    cos = pygeos.from_shapely(c.geometry)
    cos_ = pygeos.set_operations.union_all(cos)

    gps = pygeos.points(list(nodes.values))

    gtree = pygeos.STRtree(gps)

    invs = gtree.query(cos_, predicate='contains').tolist()

    #---------------------------------------------------------------------
    logger.info('Number of nodes within the coastlines {}\n'.format(len(invs)))
    #---------------------------------------------------------------------

    nps = len(invs)

    nels = 1

    if thorough:

        # ### Find invalid elements (that cross land)

        # cells to polygons
        ap = nodes.loc[elems.a]
        bp = nodes.loc[elems.b]
        cp = nodes.loc[elems.c]

        elems['ap'] = ap.values.tolist()
        elems['bp'] = bp.values.tolist()
        elems['cp'] = cp.values.tolist()

        n = 2
        al = elems.ap + elems.bp + elems.cp + elems.ap
        coords = [[l[i:i + n] for i in range(0, len(l), n)] for l in al]
        elems['coordinates'] = coords

        jig = pygeos.polygons(coords)

        jtree = pygeos.STRtree(jig)

        jig_ = pygeos.set_operations.union_all(jig)

        cross = pygeos.set_operations.intersection(jig_, cos_)

        # #### convert to dataframe

        fd = pd.DataFrame({'overlap': pygeos.to_wkt(cross)}, index=[0])

        fd['overlap'] = fd['overlap'].apply(shapely.wkt.loads)

        gover = gp.GeoDataFrame(fd, geometry='overlap')

        # #### Reject small injuctions
        ipols = gover.explode().loc[0]

        ipols.columns = ['geometry']

        mask = ipols.area.values == 0.

        ipols = ipols[~mask].reset_index(drop=True)
        ipols = gp.GeoDataFrame(ipols)

        #---------------------------------------------------------------------
        logger.info(
            'Number of elements intersecting the coastlines {}\n'.format(
                ipols.shape[0]))
        #---------------------------------------------------------------------

        nels = ipols.shape[0]

    if nps == 0 and nels == 0:
        #---------------------------------------------------------------------
        logger.info('Grid is verified against the coastline')
        #---------------------------------------------------------------------
        return True
    elif nps == 0:
        #---------------------------------------------------------------------
        logger.info('Grid is node verified against the coastline')
        #---------------------------------------------------------------------
        return True
    else:
        #---------------------------------------------------------------------
        logger.warning('Grid is not verified against the coastline')
        #---------------------------------------------------------------------
        return False
Beispiel #14
0
def global_shapefiles(data_path, regionalized=False, assigned_level=1):
    """ 
    This function will simplify shapes and add necessary columns, to make further processing more quickly
    
    For now, we will make use of the latest GADM data, split by level: https://gadm.org/download_world.html

    Optional Arguments:
        *regionalized*  : Default is **False**. Set to **True** will also create the global_regions.shp file.
    """

    gadm_path = os.path.join(data_path, 'GADM36', 'gadm36_levels.gpkg')
    cleaned_shapes_path = os.path.join(data_path, 'cleaned_shapes')

    if not os.path.exists(cleaned_shapes_path):
        os.makedirs(cleaned_shapes_path)

    # path to country GADM file
    if regionalized == False:

        # load country file
        gadm_level0 = pandas.DataFrame(
            geopandas.read_file(gadm_path, layer='level0'))

        #convert to pygeos
        tqdm.pandas(desc='Convert geometries to pygeos')
        gadm_level0['geometry'] = gadm_level0.geometry.progress_apply(
            lambda x: pygeos.from_shapely(x))

        # remove antarctica, no roads there anyways
        gadm_level0 = gadm_level0.loc[~gadm_level0['NAME_0'].
                                      isin(['Antarctica'])]

        # remove tiny shapes to reduce size substantially
        tqdm.pandas(desc='Remove tiny shapes')
        gadm_level0['geometry'] = gadm_level0.progress_apply(
            remove_tiny_shapes, axis=1)

        #simplify geometry
        tqdm.pandas(desc='Simplify geometry')
        gadm_level0.geometry = gadm_level0.geometry.progress_apply(
            lambda x: pygeos.simplify(pygeos.buffer(
                pygeos.simplify(x, tolerance=0.005, preserve_topology=True),
                0.01),
                                      tolerance=0.005,
                                      preserve_topology=True))

        #save to new country file

        glob_ctry_path = os.path.join(cleaned_shapes_path,
                                      'global_countries.gpkg')
        tqdm.pandas(desc='Convert geometries back to shapely')
        gadm_level0.geometry = gadm_level0.geometry.progress_apply(
            lambda x: loads(pygeos.to_wkb(x)))
        geopandas.GeoDataFrame(gadm_level0).to_file(glob_ctry_path,
                                                    layer='level0',
                                                    driver="GPKG")

    else:
        # this is dependent on the country file, so check whether that one is already created:
        glob_ctry_path = os.path.join(cleaned_shapes_path,
                                      'global_countries.gpkg')
        if os.path.exists(glob_ctry_path):
            gadm_level0 = geopandas.read_file(os.path.join(glob_ctry_path),
                                              layer='level0')
        else:
            print('ERROR: You need to create the country file first')
            return None

        # load region file
        gadm_level_x = pandas.DataFrame(
            geopandas.read_file(gadm_path,
                                layer='level{}'.format(assigned_level)))

        #convert to pygeos
        tqdm.pandas(desc='Convert geometries to pygeos')
        gadm_level_x['geometry'] = gadm_level_x.geometry.progress_apply(
            lambda x: pygeos.from_shapely(x))

        # remove tiny shapes to reduce size substantially
        tqdm.pandas(desc='Remove tiny shapes')
        gadm_level_x['geometry'] = gadm_level_x.progress_apply(
            remove_tiny_shapes, axis=1)

        #simplify geometry
        tqdm.pandas(desc='Simplify geometry')
        gadm_level_x.geometry = gadm_level_x.geometry.progress_apply(
            lambda x: pygeos.simplify(pygeos.buffer(
                pygeos.simplify(x, tolerance=0.005, preserve_topology=True),
                0.01),
                                      tolerance=0.005,
                                      preserve_topology=True))

        # add some missing geometries from countries with no subregions
        get_missing_countries = list(
            set(list(gadm_level0.GID_0.unique())).difference(
                list(gadm_level_x.GID_0.unique())))

        #TO DO: GID_2 and lower tiers should first be filled by a tier above, rather then by the country file
        mis_country = gadm_level0.loc[gadm_level0['GID_0'].isin(
            get_missing_countries)]  #
        if assigned_level == 1:
            mis_country['GID_1'] = mis_country['GID_0'] + '.' + str(
                0) + '_' + str(1)
        elif assigned_level == 2:
            mis_country['GID_2'] = mis_country['GID_0'] + '.' + str(
                0) + '.' + str(0) + '_' + str(1)
        elif assigned_level == 3:
            mis_country['GID_3'] = mis_country['GID_0'] + '.' + str(
                0) + '.' + str(0) + '.' + str(0) + '_' + str(1)
        elif assigned_level == 4:
            mis_country['GID_4'] = mis_country['GID_0'] + '.' + str(
                0) + '.' + str(0) + '.' + str(0) + '.' + str(0) + '_' + str(1)
        elif assigned_level == 5:
            mis_country['GID_5'] = mis_country['GID_0'] + '.' + str(
                0) + '.' + str(0) + '.' + str(0) + '.' + str(0) + '.' + str(
                    0) + '_' + str(1)

        tqdm.pandas(desc='Convert geometries back to shapely')
        gadm_level_x.geometry = gadm_level_x.geometry.progress_apply(
            lambda x: loads(pygeos.to_wkb(x)))

        # concat missing country to gadm levels
        gadm_level_x = geopandas.GeoDataFrame(
            pandas.concat([gadm_level_x, mis_country], ignore_index=True))
        gadm_level_x.reset_index(drop=True, inplace=True)

        #save to new country file
        gadm_level_x.to_file(os.path.join(cleaned_shapes_path,
                                          'global_regions.gpkg'),
                             layer='level{}'.format(assigned_level),
                             driver="GPKG")
Beispiel #15
0
        [30.78, 60.10],
        [75.21, 58.93],
        [79.26, 7.68],
        [8.23, 39.93],
        [98.73, 77.17],
        [89.78, 42.53],
        [65.19, 92.08],
        [54.46, 8.48],
    ]
)

tree = spatial.cKDTree(points)

chull = spatial.ConvexHull(points)
ashape = alpha_shape_auto(points)
pygeos_ashape = pygeos.from_shapely(ashape)

bbox = numpy.asarray((*points.min(axis=0), *points.max(axis=0)))

support = numpy.linspace(0, 100, num=15)

d_self = spatial.distance.pdist(points)
D_self = spatial.distance.squareform(d_self)
try:
    numpy.random.seed(2478879)
    random_pattern = random.poisson(bbox, size=500)
    D_other = spatial.distance.cdist(points, random_pattern)
except:
    # will cause failures in all ripley tests later from NameErrors about D_other
    # If D_other is missing, then test_simulate should also fail.
    pass
Beispiel #16
0
def test_from_shapely_error(geom):
    with pytest.raises(TypeError):
        pygeos.from_shapely(geom)
Beispiel #17
0
def test_from_shapely_incompatible_versions():
    with pytest.raises(ImportError):
        pygeos.from_shapely(point)
Beispiel #18
0
def check(g, shp, bad):

    # ## Read Grid
    #    g = pg.grid(type='tri2d',grid_file='/eos/jeodpp/data/projects/FLOODS-COAST/floods-coast/OPER/grids/eur_fixed.gr3')

    lon_min = g.Dataset.SCHISM_hgrid_node_x.values.min()
    lon_max = g.Dataset.SCHISM_hgrid_node_x.values.max()
    lat_min = g.Dataset.SCHISM_hgrid_node_y.values.min()
    lat_max = g.Dataset.SCHISM_hgrid_node_y.values.max()

    c = shp.cx[lon_min:lon_max, lat_min:lat_max]

    # ## Test polygons

    d = g.Dataset

    x = d.SCHISM_hgrid_node_x.values
    y = d.SCHISM_hgrid_node_y.values
    tri = d.SCHISM_hgrid_face_nodes.values

    nodes = pd.DataFrame({'lon': x, 'lat': y})

    elems = pd.DataFrame(tri, columns=['a', 'b', 'c'])

    bnodes = g.Dataset[['node', 'id', 'type']].to_dataframe()

    # drop bad
    dpoints = [bad]
    # get corresponding elements
    ma = elems.a.isin(dpoints)
    mb = elems.b.isin(dpoints)
    mc = elems.c.isin(dpoints)
    ids1 = elems[ma].index
    ids2 = elems[mb].index
    ids3 = elems[mc].index

    dropels = list(
        np.hstack([ids1, ids2, ids3])
    )  #these are the elements to be dropped - the ones which have dropped nodes

    dropels = np.unique(dropels)

    #take care of the boundary nodes
    dns = np.unique(elems.loc[dropels, ['a', 'b', 'c']].values.flatten())
    #get boundary nodes
    ibs = [x for x in dns if x not in dpoints]

    maxb = bnodes.id.min()
    # check if new boundary nodes merge
    if bnodes.node.isin(ibs).sum() > 0:
        if bnodes.node.isin(ibs).sum() > 0:
            ids_ = np.unique(bnodes.loc[bnodes.node.isin(ibs), 'id'].values)
            ids_.sort()
            if ids_.shape[0] > 1:
                itype = bnodes.loc[bnodes.id == ids_[-1], ['type']].values[0]
                bnodes.loc[bnodes.id.isin(ids_[:-1]), 'id'] = ids_[-1]
                ibs = pd.DataFrame({
                    'node': ibs,
                    'type': itype,
                    'id': ids_[-1]
                },
                                   index=np.arange(len(ibs)))
            else:
                itype, iid = bnodes.loc[bnodes.node.isin(ibs),
                                        ['type', 'id']].values[0]
                ibs = pd.DataFrame({
                    'node': ibs,
                    'type': itype,
                    'id': iid
                },
                                   index=np.arange(len(ibs)))
    else:
        maxb -= 1
        ibs = pd.DataFrame({
            'node': ibs,
            'type': 1,
            'id': maxb
        },
                           index=np.arange(len(ibs)))

    bnodes = pd.concat([bnodes, ibs], ignore_index=True)

    bnodes = bnodes.drop_duplicates()

    #remove
    nodes, elems = nreduce(dpoints, dropels, nodes, elems)

    bnodes = bnodes.drop(bnodes.loc[bnodes.node.isin(dpoints)].index)
    sg = nodes.reset_index().set_index('tag')
    idx = bnodes.node.values, 'index'
    nvs = sg.loc[idx].values
    bnodes.node = nvs

    bnodes = bnodes.reset_index(drop=True)

    bnodes.index.name = 'bnodes'

    bnodes = bnodes.drop_duplicates('node')

    nodes = nodes.drop('tag', axis=1)

    # #### Check for hanging nodes

    # Look for hanging nodes
    tri3 = elems.values[:, :3]
    q = np.unique(tri3.flatten())  # all the unique nodes in elements

    dq = list(set(range(nodes.shape[0])) -
              set(q))  # the ones that are in gcrop but not in elems

    dq.sort()

    if len(dq) > 0:
        nodes, elems, bnodes = drop(nodes, elems, bnodes, dq)

    # ### Find the invalid nodes (that cross the coasts)
    cos = pygeos.from_shapely(c.geometry)
    cos_ = pygeos.set_operations.union_all(cos)

    gps = pygeos.points(list(nodes.values))

    gtree = pygeos.STRtree(gps)

    invs = gtree.query(cos_, predicate='contains').tolist()

    # ### Find invalid elements (that cross land)

    # cells to polygons
    ap = nodes.loc[elems.a]
    bp = nodes.loc[elems.b]
    cp = nodes.loc[elems.c]

    elems['ap'] = ap.values.tolist()
    elems['bp'] = bp.values.tolist()
    elems['cp'] = cp.values.tolist()

    n = 2
    al = elems.ap + elems.bp + elems.cp + elems.ap
    coords = [[l[i:i + n] for i in range(0, len(l), n)] for l in al]
    elems['coordinates'] = coords

    jig = pygeos.polygons(coords)

    jtree = pygeos.STRtree(jig)

    jig_ = pygeos.set_operations.union_all(jig)

    cross = pygeos.set_operations.intersection(jig_, cos_)

    # #### convert to dataframe

    fd = pd.DataFrame({'overlap': pygeos.to_wkt(cross)}, index=[0])

    fd['overlap'] = fd['overlap'].apply(shapely.wkt.loads)

    gover = gp.GeoDataFrame(fd, geometry='overlap')

    # #### Reject small injuctions
    ipols = gover.explode().loc[0]

    ipols.columns = ['geometry']

    mask = ipols.area.values == 0.

    ipols = ipols[~mask].reset_index(drop=True)
    ipols = gp.GeoDataFrame(ipols)

    # Sometimes the edges cross the coastlines but the nodes not

    # ## Get overlapping elements for each contour

    jcos = pygeos.from_shapely(ipols.geometry)

    maxb = bnodes.id.min()

    if len(jcos) > 0:
        m = 0
        for l in tqdm(range(len(jcos))):
            con = jcos[l]
            m += 1
            jig = pygeos.polygons(elems.coordinates.to_list())
            jtree = pygeos.STRtree(jig)
            ci = jtree.query(con, predicate='intersects').tolist()
            if not ci: continue
            delems = elems.loc[ci].index.values
            dnodes = np.unique(elems.loc[ci, ['a', 'b', 'c']].values.flatten())
            #    print (ci, dnodes, delems)

            inp = pygeos.points(list(nodes.loc[dnodes].values))
            itree = pygeos.STRtree(inp)
            ipoints = itree.query(cos_, predicate='contains').tolist()
            ipoints = nodes.loc[dnodes].index[ipoints].values

            #find elements that use these points
            ma = elems.a.isin(ipoints)
            mb = elems.b.isin(ipoints)
            mc = elems.c.isin(ipoints)
            ids1 = elems[ma].index
            ids2 = elems[mb].index
            ids3 = elems[mc].index

            dropels = list(
                np.hstack([ids1, ids2, ids3])
            )  #these are the elements to be dropped - the ones which have dropped nodes

            dropels = np.unique(dropels)

            dds = np.unique(np.concatenate((delems, dropels), axis=0))

            #get all nodes
            dns = np.unique(elems.loc[dds, ['a', 'b', 'c']].values.flatten())
            #get boundary nodes
            ibs = [x for x in dns if x not in ipoints]

            #    print (ipoints, ibs)

            # check if new boundary nodes merge
            if bnodes.node.isin(ibs).sum() > 0:
                ids_ = np.unique(bnodes.loc[bnodes.node.isin(ibs),
                                            'id'].values)
                ids_.sort()
                if ids_.shape[0] > 1:
                    itype = bnodes.loc[bnodes.id == ids_[-1],
                                       ['type']].values[0]
                    bnodes.loc[bnodes.id.isin(ids_[:-1]), 'id'] = ids_[-1]
                    ibs = pd.DataFrame(
                        {
                            'node': ibs,
                            'type': itype,
                            'id': ids_[-1]
                        },
                        index=np.arange(len(ibs)))
                else:
                    itype, iid = bnodes.loc[bnodes.node.isin(ibs),
                                            ['type', 'id']].values[0]
                    ibs = pd.DataFrame({
                        'node': ibs,
                        'type': itype,
                        'id': iid
                    },
                                       index=np.arange(len(ibs)))
            else:
                maxb -= 1
                ibs = pd.DataFrame({
                    'node': ibs,
                    'type': 1,
                    'id': maxb
                },
                                   index=np.arange(len(ibs)))

            bnodes = pd.concat([bnodes, ibs], ignore_index=True)

            bnodes = bnodes.drop_duplicates()

            nodes, elems = nreduce(ipoints, dds, nodes, elems)

            # Renumber boundary nodes
            if ipoints.size > 0:
                bnodes = bnodes.drop(
                    bnodes.loc[bnodes.node.isin(ipoints)].index)
                sg = nodes.reset_index().set_index('tag')
                #        print(ipoints.size)
                idx = bnodes.node.values, 'index'
                nvs = sg.loc[idx].values
                bnodes.node = nvs

            nodes = nodes.drop('tag', axis=1)

    # #### Check for hanging nodes

    # Look for hanging nodes
    tri3 = elems.values[:, :3]
    q = np.unique(tri3.flatten())  # all the unique nodes in elements

    dq = list(set(range(nodes.shape[0])) -
              set(q))  # the ones that are in gcrop but not in elems

    dq.sort()

    if len(dq) > 0:
        nodes, elems, bnodes = drop(nodes, elems, bnodes, dq)

    # Get the largest continous area
    jels = pygeos.polygons(elems.coordinates.values.tolist())
    wat = pygeos.set_operations.coverage_union_all(jels)
    w = pd.DataFrame({'overlap': pygeos.to_wkt(wat)}, index=[0])
    w['overlap'] = w['overlap'].apply(shapely.wkt.loads)
    gw = gp.GeoDataFrame(w, geometry='overlap')

    gw = gw.explode()
    gw = gw.droplevel(0)
    gw.columns = ['geometry']
    gw['length'] = gw['geometry'][:].length
    gw = gw.sort_values(by='length', ascending=0)  #optional
    gw = gw.reset_index(drop=True)

    # indentify the elements of the large polygon
    cgw = pygeos.from_shapely(gw.loc[1:].geometry)
    cgw_ = pygeos.set_operations.union_all(cgw)
    jtree_ = pygeos.STRtree(jels)
    invs = jtree_.query(cgw_, predicate='intersects').tolist()

    if len(invs) > 0:
        #Sort the elements (some shouldn't be there)

        qnodes = np.unique(
            [elems.loc[x, ['a', 'b', 'c']].values.astype(int) for x in invs])
        nnodes = pygeos.points(list(nodes.loc[qnodes].values))
        ntree = pygeos.STRtree(nnodes)
        nels_ = pygeos.from_shapely(gw.loc[0].geometry.buffer(.00001))
        nevs = ntree.query(nels_, predicate='intersects').tolist()
        pns = qnodes[nevs]
        dpoints = [x for x in qnodes if x not in pns]

        #find elements that use these points
        ma = elems.a.isin(dpoints)
        mb = elems.b.isin(dpoints)
        mc = elems.c.isin(dpoints)
        ids1 = elems[ma].index
        ids2 = elems[mb].index
        ids3 = elems[mc].index

        dropels = list(
            np.hstack([ids1, ids2, ids3])
        )  #these are the elements to be dropped - the ones which have dropped nodes

        dropels = np.unique(dropels)

        #Remove the invalid elements
        nodes, elems = nreduce(dpoints, dropels, nodes, elems)

        #reindex boundary nodes
        bnodes = bnodes.drop(bnodes.loc[bnodes.node.isin(dpoints)].index)
        sg = nodes.reset_index().set_index('tag')
        idx = bnodes.node.values, 'index'
        nvs = sg.loc[idx].values
        bnodes.node = nvs

        bnodes = bnodes.reset_index(drop=True)

        bnodes.index.name = 'bnodes'

        nodes = nodes.drop('tag', axis=1)

    # Look for hanging nodes
    tri3 = elems.values[:, :3]
    q = np.unique(tri3.flatten())  # all the unique nodes in elements

    dq = list(set(range(nodes.shape[0])) -
              set(q))  # the ones that are in gcrop but not in elems

    dq.sort()

    if len(dq) > 0:
        nodes, elems, bnodes = drop(nodes, elems, bnodes, dq)

    # check if empty boundary

    lk = -1
    for k in range(-1, bnodes.id.min().astype(int) - 1, -1):
        if not bnodes.loc[bnodes.id == k].empty:
            bnodes.loc[bnodes.id == k, 'id'] = lk
            lk -= 1

    bnodes = bnodes.drop_duplicates('node')

    # ### create the new dataset

    nod = nodes.loc[:, ['lon', 'lat']].to_xarray().rename({
        'index':
        'nSCHISM_hgrid_node',
        'lon':
        'SCHISM_hgrid_node_x',
        'lat':
        'SCHISM_hgrid_node_y'
    })
    nod = nod.drop_vars('nSCHISM_hgrid_node')

    depx = xr.Dataset({
        'depth':
        (['nSCHISM_hgrid_node'], np.zeros(nod.nSCHISM_hgrid_node.shape[0]))
    })

    elsx = xr.DataArray(
        elems.loc[:, ['a', 'b', 'c']].values,
        dims=['nSCHISM_hgrid_face', 'nMaxSCHISM_hgrid_face_nodes'],
        name='SCHISM_hgrid_face_nodes')

    ngr = xr.merge([nod, depx, elsx, bnodes.to_xarray()])  # total

    return ngr
Beispiel #19
0
    def _morphological_tessellation(self,
                                    gdf,
                                    unique_id,
                                    limit,
                                    shrink,
                                    segment,
                                    verbose,
                                    check=True):
        objects = gdf.copy()

        if isinstance(limit, (gpd.GeoSeries, gpd.GeoDataFrame)):
            limit = limit.unary_union
        if isinstance(limit, BaseGeometry):
            limit = pygeos.from_shapely(limit)

        bounds = pygeos.bounds(limit)
        centre_x = (bounds[0] + bounds[2]) / 2
        centre_y = (bounds[1] + bounds[3]) / 2
        objects["geometry"] = objects["geometry"].translate(xoff=-centre_x,
                                                            yoff=-centre_y)

        if shrink != 0:
            print("Inward offset...") if verbose else None
            mask = objects.type.isin(["Polygon", "MultiPolygon"])
            objects.loc[mask, "geometry"] = objects[mask].buffer(-shrink,
                                                                 cap_style=2,
                                                                 join_style=2)

        objects = objects.reset_index(drop=True).explode()
        objects = objects.set_index(unique_id)

        print("Generating input point array...") if verbose else None
        points, ids = self._dense_point_array(objects.geometry.values.data,
                                              distance=segment,
                                              index=objects.index)

        # add convex hull buffered large distance to eliminate infinity issues
        series = gpd.GeoSeries(limit, crs=gdf.crs).translate(xoff=-centre_x,
                                                             yoff=-centre_y)
        width = bounds[2] - bounds[0]
        leng = bounds[3] - bounds[1]
        hull = series.geometry[[0]].buffer(2 * width if width > leng else 2 *
                                           leng)
        # pygeos bug fix
        if (hull.type == "MultiPolygon").any():
            hull = hull.explode()
        hull_p, hull_ix = self._dense_point_array(
            hull.values.data,
            distance=pygeos.length(limit) / 100,
            index=hull.index)
        points = np.append(points, hull_p, axis=0)
        ids = ids + ([-1] * len(hull_ix))

        print("Generating Voronoi diagram...") if verbose else None
        voronoi_diagram = Voronoi(np.array(points))

        print("Generating GeoDataFrame...") if verbose else None
        regions_gdf = self._regions(voronoi_diagram,
                                    unique_id,
                                    ids,
                                    crs=gdf.crs)

        print("Dissolving Voronoi polygons...") if verbose else None
        morphological_tessellation = regions_gdf[[unique_id, "geometry"
                                                  ]].dissolve(by=unique_id,
                                                              as_index=False)

        morphological_tessellation = gpd.clip(morphological_tessellation,
                                              series)

        morphological_tessellation["geometry"] = morphological_tessellation[
            "geometry"].translate(xoff=centre_x, yoff=centre_y)

        if check:
            self._check_result(morphological_tessellation,
                               gdf,
                               unique_id=unique_id)

        return morphological_tessellation
Beispiel #20
0
def test_from_shapely(geom):
    actual = pygeos.from_shapely(ShapelyGeometryMock(geom))
    assert isinstance(geom, pygeos.Geometry)
    assert pygeos.equals(geom, actual)
    assert geom._ptr != actual._ptr
Beispiel #21
0
def line_merge(x):
    if x.geom_type == 'MultiLineString':
        return shapely.wkb.loads(
            pygeos.to_wkb(pygeos.linear.line_merge(pygeos.from_shapely(x))))
    else:
        return x
Beispiel #22
0
    def __init__(
        self,
        gdf,
        unique_id,
        limit=None,
        shrink=0.4,
        segment=0.5,
        verbose=True,
        enclosures=None,
        enclosure_id="eID",
        threshold=0.05,
        use_dask=True,
        n_chunks=None,
        **kwargs,
    ):
        self.gdf = gdf
        self.id = gdf[unique_id]
        self.limit = limit
        self.shrink = shrink
        self.segment = segment
        self.enclosure_id = enclosure_id

        if limit is not None and enclosures is not None:
            raise ValueError(
                "Both `limit` and `enclosures` cannot be passed together. "
                "Pass `limit` for morphological tessellation or `enclosures` "
                "for enclosed tessellation.")

        gdf = gdf.copy()

        if enclosures is not None:

            enclosures = enclosures.copy()

            bounds = enclosures.total_bounds
            centre_x = (bounds[0] + bounds[2]) / 2
            centre_y = (bounds[1] + bounds[3]) / 2

            gdf.geometry = gdf.geometry.translate(xoff=-centre_x,
                                                  yoff=-centre_y)
            enclosures.geometry = enclosures.geometry.translate(xoff=-centre_x,
                                                                yoff=-centre_y)

            self.tessellation = self._enclosed_tessellation(
                gdf,
                enclosures,
                unique_id,
                enclosure_id,
                threshold,
                use_dask,
                n_chunks,
            )
        else:
            if isinstance(limit, (gpd.GeoSeries, gpd.GeoDataFrame)):
                limit = limit.unary_union
            if isinstance(limit, BaseGeometry):
                limit = pygeos.from_shapely(limit)

            bounds = pygeos.bounds(limit)
            centre_x = (bounds[0] + bounds[2]) / 2
            centre_y = (bounds[1] + bounds[3]) / 2
            gdf.geometry = gdf.geometry.translate(xoff=-centre_x,
                                                  yoff=-centre_y)

            # add convex hull buffered large distance to eliminate infinity issues
            limit = (gpd.GeoSeries(limit, crs=gdf.crs).translate(
                xoff=-centre_x, yoff=-centre_y).values.data[0])

            self.tessellation = self._morphological_tessellation(
                gdf, unique_id, limit, shrink, segment, verbose)

        self.tessellation["geometry"] = self.tessellation[
            "geometry"].translate(xoff=centre_x, yoff=centre_y)
Beispiel #23
0
def enclosures(primary_barriers,
               limit=None,
               additional_barriers=None,
               enclosure_id="eID"):
    """
    Generate enclosures based on passed barriers.

    Enclosures are areas enclosed from all sides by at least one type of
    a barrier. Barriers are typically roads, railways, natural features
    like rivers and other water bodies or coastline. Enclosures are a
    result of polygonization of the  ``primary_barrier`` and ``limit`` and its
    subdivision based on additional_barriers.

    Parameters
    ----------
    primary_barriers : GeoDataFrame, GeoSeries
        GeoDataFrame or GeoSeries containing primary barriers.
        (Multi)LineString geometry is expected.
    limit : GeoDataFrame, GeoSeries (default None)
        GeoDataFrame or GeoSeries containing external limit of enclosures,
        i.e. the area which gets partitioned. If None is passed,
        the internal area of ``primary_barriers`` will be used.
    additional_barriers : GeoDataFrame
        GeoDataFrame or GeoSeries containing additional barriers.
        (Multi)LineString geometry is expected.
    enclosure_id : str (default 'eID')
        name of the enclosure_id (to be created).

    Returns
    -------
    enclosures : GeoDataFrame
       GeoDataFrame containing enclosure geometries and enclosure_id

    Examples
    --------
    >>> enclosures = mm.enclosures(streets, admin_boundary, [railway, rivers])

    """
    if limit is not None:
        if limit.geom_type.isin(["Polygon", "MultiPolygon"]).any():
            limit = limit.boundary
        barriers = pd.concat([primary_barriers.geometry, limit.geometry])
    else:
        barriers = primary_barriers
    unioned = barriers.unary_union
    polygons = polygonize(unioned)
    enclosures = gpd.GeoSeries(list(polygons), crs=primary_barriers.crs)

    if additional_barriers is not None:
        if not isinstance(additional_barriers, list):
            raise TypeError(
                "`additional_barriers` expects a list of GeoDataFrames or GeoSeries."
                f"Got {type(additional_barriers)}.")
        additional = pd.concat([gdf.geometry for gdf in additional_barriers])

        inp, res = enclosures.sindex.query_bulk(additional.geometry,
                                                predicate="intersects")
        unique = np.unique(res)

        new = []

        for i in unique:
            poly = enclosures.values.data[i]  # get enclosure polygon
            crossing = inp[res == i]  # get relevant additional barriers
            buf = pygeos.buffer(poly, 0.01)  # to avoid floating point errors
            crossing_ins = pygeos.intersection(
                buf, additional.values.data[crossing]
            )  # keeping only parts of additional barriers within polygon
            union = pygeos.union_all(
                np.append(crossing_ins, pygeos.boundary(poly)))  # union
            polygons = np.array(list(polygonize(
                _pygeos_to_shapely(union))))  # polygonize
            within = pygeos.covered_by(
                pygeos.from_shapely(polygons),
                buf)  # keep only those within original polygon
            new += list(polygons[within])

        final_enclosures = (gpd.GeoSeries(enclosures).drop(unique).append(
            gpd.GeoSeries(new)).reset_index(drop=True)).set_crs(
                primary_barriers.crs)

        return gpd.GeoDataFrame({enclosure_id: range(len(final_enclosures))},
                                geometry=final_enclosures)

    return gpd.GeoDataFrame({enclosure_id: range(len(enclosures))},
                            geometry=enclosures)
Beispiel #24
0
def test_from_shapely_none():
    actual = pygeos.from_shapely(None)
    assert actual is None