Rayugo
Rayugo

Reputation: 15

Is there an option to add two labels each at the other end of edge in netgraph?

I am working on the app to draw the computer network topology. I've wanted to use the Python netgraph library because it adds the possibility of interaction with the network.

I've tried to edit the code from the documentation so I can use two labels, each on the other end of the edge. I wanted it to be the description of the type of interface like eth0, gig0, etc., but with the code below, there is still one label for edge, e.g., '('eth0','eth1')'. Is it even possible to achive that with netgraph/networkx?

import matplotlib.pyplot as plt
import networkx as nx
from netgraph import InteractiveGraph

g = nx.house_x_graph()

edge_color = dict()
for ii, edge in enumerate(g.edges):
  edge_color[edge] = 'tab:gray' if ii%2 else 'tab:orange'

node_color = dict()
for node in g.nodes:
  node_color[node] = 'tab:red' if node%2 else 'tab:blue'

edge_labels = {(1, 2): ('eth0','eth1'), (2, 3): ('gig0','gig1') }

plot_instance = InteractiveGraph(
g, node_size=5, node_color=node_color,
node_labels=True, node_label_offset=0.1, node_label_fontdict=dict(size=20),
edge_color=edge_color, edge_width=1,edge_labels=edge_labels, edge_label_position=0.2)

plt.show()

(https://i.sstatic.net/OUs2M.png)

Upvotes: 1

Views: 60

Answers (1)

Paul Brodersen
Paul Brodersen

Reputation: 13021

Is it even possible to achieve that with netgraph/networkx?

Not out of the box, no. However, as Netgraph InteractiveGraph object expose the current node positions, it is not too complicated to implement the desired functionality using standard matplotlib event handling.

The code below initializes the InteractiveGraph object, computes separate interface label positions based InteractiveGraph.node_positions, and then updates the interface label positions whenever a node is dragged (on release, to be precise).

enter image description here

import numpy as np
import matplotlib.pyplot as plt
import networkx as nx

from netgraph import InteractiveGraph

g = nx.house_x_graph()

edge_color = dict()
for ii, edge in enumerate(g.edges):
    edge_color[edge] = 'tab:gray' if ii%2 else 'tab:orange'

node_color = dict()
for node in g.nodes:
    node_color[node] = 'tab:red' if node%2 else 'tab:blue'

fig, ax = plt.subplots()
plot_instance = InteractiveGraph(
    g, node_size=5, node_color=node_color,
    node_labels=True, node_label_offset=0.1, node_label_fontdict=dict(size=20),
    edge_color=edge_color, edge_width=1, ax=ax)

interface_labels = [
    ((1, 2), 0.2, 'eth0'),
    ((1, 2), 0.8, 'eth1'),
    ((2, 3), 0.2, 'gig0'),
    ((2, 3), 0.8, 'gig0'),
]

def get_label_position(edge, edge_label_position, node_positions):
    v1, v2 = edge
    dxy = node_positions[v2] - node_positions[v1]
    xy = edge_label_position * dxy + node_positions[v1]
    angle = np.arctan2(dxy[1], dxy[0]) * 360 / (2.0 * np.pi)

    # reduce overlap with nodes
    if 90 <= (angle % 360) < 270:
        if edge_label_position < 0.5:
            horizontal_alignment = "right"
        else:
            horizontal_alignment = "left"
    else:
        if edge_label_position < 0.5:
            horizontal_alignment = "left"
        else:
            horizontal_alignment = "right"

    # make label orientation "right-side-up"
    if angle > 90:
        angle -= 180
    if angle < - 90:
        angle += 180

    return xy, angle, horizontal_alignment

interface_label_text_objects = []
for edge, edge_label_position, label in interface_labels:
    (x, y), angle, ha = get_label_position(edge, edge_label_position, plot_instance.node_positions)
    text_object = ax.text(x, y, label, rotation=angle, rotation_mode="anchor", va="center", ha=ha)
    interface_label_text_objects.append(text_object)

def update_label_positions(event):
    for (edge, edge_label_position, _), text_object in zip(interface_labels, interface_label_text_objects):
        xy, angle, ha = get_label_position(
            edge, edge_label_position, plot_instance.node_positions)
        text_object.set_position(xy)
        text_object.set_rotation(angle)
        text_object.set_horizontalalignment(ha)

fig.canvas.mpl_connect('button_release_event', update_label_positions)

plt.show()

Upvotes: 0

Related Questions