Home > Back-end >  Set z-order of Edges in Pandas Dataframe
Set z-order of Edges in Pandas Dataframe

Time:02-02

I'm using OSMnx to create some plots of road networks with data shown by colour, below is an example which produces an image with colour showing betweenness centrality. The problem I'm having is that, due to the zordering, some of the lighter regions are covered by other edges, especially around junctions with lots of nodes.

The OSMnx.plot_graph method calls gdf.plot from GeoPandas, which in turn uses PlotAccessor from Pandas. I've been reading the OSMnx documentation, and can't seem to find a way to pass a z-ordering, so my question is: is there a way I can directly plot the graph, either with GeoPandas, pandas, or even matplotlib directly, such that I can specify a z-ordering of the edges?

import networkx as nx
import osmnx as ox

place = 'Hornsea'
G = ox.graph_from_place(place, network_type="drive")

# Digraph removes parallel edges
# Line graph swaps nodes and edges
line_graph = nx.line_graph(ox.get_digraph(G))

betweenness_centrality = nx.betweenness_centrality(line_graph, weight='travel_time')

for edge in G.edges:
    if edge not in betweenness_centrality:
        nx.set_edge_attributes(G, {edge: 0}, 'betweenness_centrality')

betweenness_centrality = {(k[0], k[1], 0): v for k, v in betweenness_centrality.items()}
nx.set_edge_attributes(G, betweenness_centrality, "betweenness_centrality")

ec = ox.plot.get_edge_colors_by_attr(G, 'betweenness_centrality', cmap='plasma')  # "RdYlGn_r"
ew = [G.get_edge_data(*edge).get('betweenness_centrality', 0) * 10   0.25 for edge in G.edges]
fig, ax = ox.plot_graph(G, edge_color=ec, edge_linewidth=ew, node_size=0)
fig.savefig(f"images/{place}_betweenness_centrality.png", facecolor="w", dpi=1000, bbox_inches="tight")

hornsea_betweenness_centrality.png

CodePudding user response:

Took a little exploring the source code... not sure if this is exactly right, since I'm not sure how the desired outcome is supposed to look, but hopefully this gets you most of the way there... had to do some poking in the source code....

from pathlib import Path

import geopandas as gpd
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
import osmnx as ox
from shapely.geometry import LineString
from shapely.geometry import Point

# From your example:
place = 'Hornsea'
G = ox.graph_from_place(place, network_type='drive')
line_graph = nx.line_graph(ox.get_digraph(G))
betweenness_centrality = nx.betweenness_centrality(
    line_graph, weight='travel_time')
for edge in G.edges:
    if edge not in betweenness_centrality:
        nx.set_edge_attributes(G, {edge: 0}, 'betweenness_centrality')
betweenness_centrality = {
    (k[0], k[1], 0): v for k, v in betweenness_centrality.items()}
nx.set_edge_attributes(
    G, betweenness_centrality, 'betweenness_centrality')
ec = ox.plot.get_edge_colors_by_attr(
    G, 'betweenness_centrality', cmap='plasma')

From digging here: enter image description here

CodePudding user response:

Thanks to Damian Satterthwaite-Phillips, I was able to find a solution to the problem. His answer can be cut down a lot, so I thought I'd share what I ended up using in case anyone else has the same issue. However, I have made a pull request to have edge_zorder be an argument for osmnx.plot_graph, so hopefully soon this won't be necessary.

def plot_graph(G, edge_colour=None, edge_linewidth=None, edge_zorder=None, edge_alpha=None, show=True, save=False, close=False, filepath=None, dpi=300):
    fig, ax = plt.subplots(figsize=(8, 8), facecolor='#111111', frameon=False)
    ax.set_facecolor('#111111')
    
    gs_edges = ox.utils_graph.graph_to_gdfs(G, nodes=False)["geometry"]
    gdf_edges = gpd.GeoDataFrame(gs_edges)
    gdf_edges['zorder'] = edge_zorder
    gdf_edges['colour'] = edge_colour
    gdf_edges['linewidth'] = edge_linewidth
    gdf_edges['alpha'] = edge_alpha
    gdf_edges.sort_values('zorder', inplace=True)
    
    colour = gdf_edges.colour if edge_colour is not None else None
    linewidth = gdf_edges.linewidth if edge_linewidth is not None else None
    alpha = gdf_edges.alpha if edge_alpha is not None else None
    
    ax = gdf_edges.plot(ax=ax, color=colour, lw=linewidth, alpha=alpha, zorder=1)
    west, south, east, north = gdf_edges.total_bounds
    bbox = north, south, east, west
    ax = ox.plot._config_ax(ax, G.graph["crs"], bbox, 0.02)
    return ox.plot._save_and_show(fig, ax, save, show, close, filepath, dpi)

Now this doesn't plot nodes, but it would be easily modified by looking at the plot_graph method in osmnx.plot.py

  • Related