Visualization#

NPAP provides interactive network visualization using Plotly, enabling geographic map displays with customizable styling for nodes, edges, and clusters.

Overview#

The visualization system renders networks on interactive maps with support for:

  • Geographic coordinates (lat/lon)

  • Edge type differentiation (lines, transformers, DC links)

  • Voltage level coloring

  • Cluster visualization

  • Customizable styling

Basic Usage#

Quick Plot#

import npap

manager = npap.PartitionAggregatorManager()
manager.load_data("networkx_direct", graph=G)

# Simple visualization
manager.plot_network()

With Partition Coloring#

# Partition first
partition = manager.partition("geographical_kmeans", n_clusters=10)

# Plot with cluster colors
manager.plot_network(style="clustered")

Plot Styles#

NPAP provides three built-in plot styles:

SIMPLE Style#

Basic visualization with uniform edge styling:

manager.plot_network(style="simple")
  • All edges have the same color

  • Fastest rendering

  • Best for quick inspection

VOLTAGE_AWARE Style#

Edges colored by type and voltage level:

manager.plot_network(style="voltage_aware")
  • Lines colored by voltage (high/low)

  • Transformers highlighted

  • DC links distinguished

  • Best for power system visualization

CLUSTERED Style#

Nodes colored by cluster assignment:

partition = manager.partition("geographical_kmeans", n_clusters=10)
manager.plot_network(style="clustered")
  • Each cluster has a distinct color

  • Shows partition results

  • Best for partition analysis

Plot Configuration#

Fine-tune visualizations with PlotConfig:

from npap.visualization import PlotConfig

config = PlotConfig(
    # Display toggles
    show_lines=True,
    show_trafos=True,
    show_dc_links=True,
    show_nodes=True,

    # Voltage threshold (kV)
    line_voltage_threshold=300,

    # Colors (hex)
    line_high_voltage_color="#e74c3c",   # Red for HV
    line_low_voltage_color="#3498db",    # Blue for LV
    trafo_color="#9b59b6",               # Purple for transformers
    dc_link_color="#2ecc71",             # Green for DC links
    node_color="#34495e",                # Dark gray for nodes

    # Geometry
    edge_width=1.5,
    node_size=6,

    # Map settings
    map_center_lat=47.0,
    map_center_lon=15.0,
    map_zoom=5,
    map_style="carto-positron"
)

manager.plot_network(style="voltage_aware", config=config)

Configuration Parameters#

Display Toggles#

Parameter

Type

Default

Description

show_lines

bool

True

Show transmission lines

show_trafos

bool

True

Show transformers

show_dc_links

bool

True

Show DC links

show_nodes

bool

True

Show nodes

Voltage Settings#

Parameter

Type

Default

Description

line_voltage_threshold

float

300.0

Threshold (kV) for high/low voltage

Colors#

Parameter

Default

Description

line_high_voltage_color

"#029E73"

High voltage lines (>threshold)

line_low_voltage_color

"#CA9161"

Low voltage lines (≤threshold)

trafo_color

"#ECE133"

Transformers

dc_link_color

"#CC78BC"

DC links

node_color

"#0173B2"

Nodes (non-clustered)

Geometry#

Parameter

Type

Default

Description

edge_width

float

1.5

Line width in pixels

node_size

int

5

Node marker size

Map Settings#

Parameter

Type

Default

Description

map_center_lat

float

57.5

Map center latitude

map_center_lon

float

14.0

Map center longitude

map_zoom

float

3.7

Initial zoom level

map_style

str

"carto-positron"

Map tile style

Map Styles#

Available Plotly map styles:

Style

Description

"carto-positron"

Light, clean style (default)

"carto-darkmatter"

Dark theme

"open-street-map"

OpenStreetMap tiles

"stamen-terrain"

Terrain with shading

"stamen-toner"

High-contrast black/white

Customizing Cluster Colors#

For clustered plots, customize the color scale:

config = PlotConfig(
    cluster_colorscale="Viridis"  # Plotly colorscale name
)

manager.plot_network(style="clustered", config=config)

Available colorscales include:

  • "Viridis" (default)

  • "Plasma"

  • "Inferno"

  • "Magma"

  • "Cividis"

  • "Rainbow"

  • "Jet"

  • "Turbo"

Working with Figures#

Getting the Figure Object#

# Get the Plotly figure without displaying
fig = manager.plot_network(style="voltage_aware", show=False)

# Customize further
fig.update_layout(
    title="My Network",
    title_x=0.5
)

# Save to file
fig.write_html("network.html")
fig.write_image("network.png")

# Display
fig.show()

Adding Custom Traces#

import plotly.graph_objects as go

fig = manager.plot_network(style="simple", show=False)

# Add custom markers
fig.add_trace(go.Scattermapbox(
    lat=[47.0, 47.5],
    lon=[15.0, 15.5],
    mode="markers",
    marker=dict(size=15, color="red"),
    name="Critical Nodes"
))

fig.show()

Visualizing Different Graphs#

Original vs Aggregated#

# Load and partition
manager.load_data("csv_files", node_file="...", edge_file="...")
partition = manager.partition("geographical_kmeans", n_clusters=20)

# Plot original with clusters
fig_original = manager.plot_network(
    style="clustered",
    title="Original Network (Clustered)",
    show=False
)

# Aggregate
aggregated = manager.aggregate(mode=AggregationMode.GEOGRAPHICAL)

# Plot aggregated
fig_aggregated = manager.plot_network(
    graph=aggregated,
    style="simple",
    title="Aggregated Network",
    show=False
)

# Display side by side (in Jupyter)
from plotly.subplots import make_subplots
# ... or save both
fig_original.write_html("original.html")
fig_aggregated.write_html("aggregated.html")

Plotting External Graphs#

Plot any NetworkX graph with geographic coordinates:

import networkx as nx

# Create or load a graph
G = nx.DiGraph()
G.add_node("A", lat=47.0, lon=15.0)
G.add_node("B", lat=47.1, lon=15.1)
G.add_edge("A", "B")

# Plot it
fig = manager.plot_network(graph=G, style="simple")

Performance Optimization#

Large Networks#

For networks with >10,000 edges:

config = PlotConfig(
    show_nodes=False,    # Hide nodes for faster rendering
    edge_width=0.5       # Thinner lines
)

manager.plot_network(style="simple", config=config)

Edge Grouping#

NPAP automatically groups edges by type for efficient rendering:

  • All high-voltage lines → single trace

  • All low-voltage lines → single trace

  • All transformers → single trace

  • All DC links → single trace

This dramatically improves performance compared to rendering each edge separately.

Interactive Features#

Plotly figures are interactive:

  • Zoom: Scroll wheel or pinch

  • Pan: Click and drag

  • Hover: Shows node/edge information

  • Legend: Click to toggle visibility

  • Save: Download as PNG from toolbar

Hover Information#

Default hover shows:

  • Nodes: Node ID, coordinates

  • Edges: Source, target, type

Customize with Plotly’s hovertemplate:

fig = manager.plot_network(style="simple", show=False)

# Update node hover
for trace in fig.data:
    if trace.name == "Nodes":
        trace.hovertemplate = "Node: %{text}<br>Lat: %{lat}<br>Lon: %{lon}"

fig.show()

Examples#

Power System with All Edge Types#

# Load voltage-aware data
manager.load_data(
    "va_loader",
    node_file="buses.csv",
    line_file="lines.csv",
    transformer_file="transformers.csv",
    link_file="dc_links.csv"
)

# Configure for power system visualization
config = PlotConfig(
    line_voltage_threshold=300,        # 300 kV threshold
    line_high_voltage_color="#c0392b", # Dark red for EHV
    line_low_voltage_color="#2980b9",  # Blue for HV
    trafo_color="#8e44ad",             # Purple for transformers
    dc_link_color="#27ae60",           # Green for DC
    map_style="carto-darkmatter"       # Dark theme
)

manager.plot_network(style="voltage_aware", config=config)

Partition Analysis#

# Compare different partition sizes
for n_clusters in [5, 10, 20, 50]:
    partition = manager.partition("geographical_kmeans", n_clusters=n_clusters)

    fig = manager.plot_network(
        style="clustered",
        title=f"Partitioning with {n_clusters} Clusters",
        show=False
    )

    fig.write_html(f"partition_{n_clusters}.html")

Animation (Advanced)#

Create animated visualizations:

import plotly.graph_objects as go

# Create frames for different partition sizes
frames = []
for n_clusters in range(5, 51, 5):
    partition = manager.partition("geographical_kmeans", n_clusters=n_clusters)
    fig = manager.plot_network(style="clustered", show=False)
    frames.append(go.Frame(data=fig.data, name=str(n_clusters)))

# Create animated figure
animated_fig = go.Figure(
    data=frames[0].data,
    frames=frames,
    layout=go.Layout(
        updatemenus=[dict(
            type="buttons",
            buttons=[dict(label="Play", method="animate")]
        )]
    )
)

animated_fig.show()

Troubleshooting#

Missing Coordinates#

# Check for nodes without coordinates
missing = [n for n, d in graph.nodes(data=True)
           if "lat" not in d or "lon" not in d]

if missing:
    print(f"Nodes without coordinates: {missing[:5]}...")

Empty Plot#

If the plot appears empty:

  1. Check that nodes have lat and lon attributes

  2. Verify coordinate ranges are reasonable (lat: -90 to 90, lon: -180 to 180)

  3. Adjust map_center_lat, map_center_lon, and map_zoom

Slow Rendering#

For slow rendering:

  1. Reduce edge width

  2. Hide nodes

  3. Use style="simple" instead of "voltage_aware"

  4. Aggregate the network before plotting