def graph_to_geojson()

in libs/solaris/vector/graph.py [0:0]


def graph_to_geojson(G, output_path, encoding='utf-8', overwrite=False,
                     verbose=False):
    """
    Save graph to two geojsons: one containing nodes, the other edges.
    Arguments
    ---------
    G : :class:`networkx.MultiDiGraph`
        A graph object to save to geojson files.
    output_path : str
        Path to save the geojsons to. ``'_nodes.geojson'`` and
        ``'_edges.geojson'`` will be appended to ``output_path`` (after
        stripping the extension).
    encoding : str, optional
        The character encoding for the saved files.
    overwrite : bool, optional
        Should files at ``output_path`` be overwritten? Defaults to no
        (``False``).
    verbose : bool, optional
        Switch to print relevant values.  Defaults to no (``False``).

    Notes
    -----
    This function is based on ``osmnx.save_load.save_graph_shapefile``, with
    tweaks to make it work with our graph objects. It will save two geojsons:
    a file containing all of the nodes and a file containing all of the edges.
    When writing to geojson, must convert the coordinate reference system
    (crs) to string if it's a dict, otherwise no crs will be appended to the
    geojson.

    Returns
    -------
    None
    """

    # convert directed graph G to an undirected graph for saving as a shapefile
    G_to_save = G.copy().to_undirected()
    # create GeoDataFrame containing all of the nodes
    nodes, data = zip(*G_to_save.nodes(data=True))
    gdf_nodes = gpd.GeoDataFrame(list(data), index=nodes)

    # get coordinate reference system
    g_crs = G_to_save.graph['crs']
    if type(g_crs) == dict:
        # convert from dict
        g_crs = rio.crs.CRS.from_dict(g_crs)
    gdf_nodes.crs = g_crs
    if verbose:
        print("crs:", g_crs)

    gdf_nodes['geometry'] = gdf_nodes.apply(
        lambda row: Point(row['x'], row['y']), axis=1
        )
    gdf_nodes = gdf_nodes.drop(['x', 'y'], axis=1)
    # gdf_nodes['node_idx'] = gdf_nodes['node_idx'].astype(np.int32)

    # # make everything but geometry column a string
    # for col in [c for c in gdf_nodes.columns if not c == 'geometry']:
    #    gdf_nodes[col] = gdf_nodes[col].fillna('').map(make_str)

    # create GeoDataFrame containing all of the edges
    edges = []
    for u, v, key, data in G_to_save.edges(keys=True, data=True):
        edge = {'key': key}
        for attr_key in data:
            edge[attr_key] = data[attr_key]
        if 'geometry' not in data:
            point_u = Point((G_to_save.nodes[u]['x'], G_to_save.nodes[u]['y']))
            point_v = Point((G_to_save.nodes[v]['x'], G_to_save.nodes[v]['y']))
            edge['geometry'] = LineString([point_u, point_v])
        edges.append(edge)

    gdf_edges = gpd.GeoDataFrame(edges)
    gdf_edges.crs = g_crs

    for col in [c for c in gdf_nodes.columns if c != 'geometry']:
        gdf_nodes[col] = gdf_nodes[col].fillna('').apply(str)
    for col in [c for c in gdf_edges.columns if c != 'geometry']:
        gdf_edges[col] = gdf_edges[col].fillna('').apply(str)

    # make directory structure
    if not os.path.exists(os.path.split(output_path)[0]):
        os.makedirs(os.path.split(output_path)[0])

    edges_path = os.path.splitext(output_path)[0] + '_edges.geojson'
    nodes_path = os.path.splitext(output_path)[0] + '_nodes.geojson'
    if overwrite:
        if os.path.exists(edges_path):
            os.remove(edges_path)
        if os.path.exists(nodes_path):
            os.remove(nodes_path)

    gdf_edges.to_file(edges_path, encoding=encoding, driver='GeoJSON')
    gdf_nodes.to_file(nodes_path, encoding=encoding, driver='GeoJSON')