{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": false, "jupyter": { "outputs_hidden": false } }, "source": [ "# Plotting traces (collections)\n", "This Tutorial explains how to customize network plots in pandapower using plotly. Each pandapower network element can be translated into a plotly trace with all corresponding properties." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:31.842639Z", "start_time": "2025-11-26T11:20:26.800990Z" } }, "outputs": [], "source": [ "from pandapower.create import (\n", " create_empty_network,\n", " create_bus,\n", " create_line,\n", " create_transformer3w,\n", " create_transformer,\n", " create_ext_grid,\n", " create_load,\n", ")\n", "import pandapower.networks as nw\n", "import pandapower.plotting.plotly as pplotly\n", "from pandas import Series\n", "import numpy as np\n", "\n", "# Prevent Dark Background on plots\n", "import plotly.io as pio\n", "pio.templates.default = \"plotly_white\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We created a network along with geodata in the buses." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:32.045746Z", "start_time": "2025-11-26T11:20:31.851890Z" } }, "outputs": [], "source": [ "net = create_empty_network()\n", "x = np.array([0, -2, 2, -3, -2, 2])\n", "y = np.array([4, 3, 3, -3, -2, -2])\n", "for i in range(6):\n", " if i < 3:\n", " v = 110\n", " elif i == 3:\n", " v = 20\n", " else:\n", " v = 10\n", " create_bus(net, vn_kv=v, geodata=(x[i], y[i]))\n", "\n", "create_line(net, 0, 1, 5, \"149-AL1/24-ST1A 110.0\", geodata=None, name=\"l1\")\n", "create_line(net, 0, 2, 5, \"149-AL1/24-ST1A 110.0\", geodata=None, name=\"l2\")\n", "create_transformer3w(net, 1, 3, 4, \"63/25/38 MVA 110/20/10 kV\", name=\"tr1\")\n", "\n", "create_transformer(net, 2, 5, \"0.25 MVA 20/0.4 kV\", name=\"tr2\")\n", "\n", "create_ext_grid(net, 0)\n", "create_load(net, 4, p_mw=20.0, q_mvar=10.0, name=\"load1\")\n", "create_load(net, 5, p_mw=20.0, q_mvar=10.0, name=\"load1\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you want to have full control over the layout of your plot, you can individually create and plot collections with the pandapower plotting module." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:33.645802Z", "start_time": "2025-11-26T11:20:32.055753Z" }, "scrolled": true }, "outputs": [], "source": [ "lc = pplotly.create_line_trace(net, net.line.index, color=\"black\", infofunc=net.line.name)\n", "bc = pplotly.create_bus_trace(net, net.bus.index, size=10, color=\"orange\", infofunc=net.bus.vn_kv)\n", "tc3 = pplotly.create_trafo_trace(\n", " net,\n", " net.trafo3w.index,\n", " trafotype=\"3W\",\n", " color=\"green\",\n", " infofunc=net.trafo3w.name,\n", " trace_name=\"trafo3ws\",\n", " cmin=None,\n", " cmax=None,\n", " cmap_vals=None,\n", " use_line_geo=None,\n", ")\n", "tc = pplotly.create_trafo_trace(\n", " net,\n", " net.trafo.index,\n", " trafotype=\"2W\",\n", " color=\"blue\",\n", " infofunc=net.trafo.name,\n", " trace_name=\"trafos\",\n", " cmin=None,\n", " cmax=None,\n", " cmap_vals=None,\n", " use_line_geo=None,\n", ")\n", "_ = pplotly.draw_traces(bc + lc + tc + tc3, figsize=1, aspectratio=(8, 4));" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:37.543880Z", "start_time": "2025-11-26T11:20:33.966953Z" } }, "outputs": [], "source": [ "net = nw.mv_oberrhein()\n", "\n", "lc = pplotly.create_line_trace(net, net.line.index, color=\"black\")\n", "bc = pplotly.create_bus_trace(\n", " net,\n", " net.bus.index,\n", " size=10,\n", " color=\"orange\",\n", " infofunc=Series(index=net.bus.index, data=net.bus.name + \"
\" + net.bus.vn_kv.astype(str) + \" kV\"),\n", ")\n", "pplotly.draw_traces(bc + lc, figsize=1, aspectratio=(8, 6));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Order of plotting traces is as ordered in the `draw_traces` function. So, in order to plot buses in front of lines first lines and then buses need to be set in the input traces list:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:38.035214Z", "start_time": "2025-11-26T11:20:37.788856Z" } }, "outputs": [], "source": [ "pplotly.draw_traces(lc + bc, figsize=1, aspectratio=(8, 6));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Highlighting\n", "Specific lines or buses can be highlighted by creating extra line collections in different colors.\n", "\n", "In this example, we plot lines that are longer than 2 km green and buses with a voltage below 0.98 p.u. red.\n", "\n", "First, we create a line collection for all lines in grey and a line collection for only the long lines in green:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:39.804905Z", "start_time": "2025-11-26T11:20:38.478010Z" } }, "outputs": [], "source": [ "long_lines = net.line[net.line.length_km > 2.0].index\n", "lc = pplotly.create_line_trace(net, net.line.index, color=\"grey\")\n", "lcl = pplotly.create_line_trace(\n", " net,\n", " long_lines,\n", " color=\"green\",\n", " width=2,\n", " infofunc=Series(\n", " index=net.line.index,\n", " data=net.line.name[long_lines] + \"
\" + net.line.length_km[long_lines].astype(str) + \" km\",\n", " ),\n", ")\n", "\n", "low_voltage_buses = net.res_bus[net.res_bus.vm_pu < 0.98].index\n", "bc = pplotly.create_bus_trace(net, net.bus.index, size=10, color=\"blue\")\n", "bch = pplotly.create_bus_trace(net, low_voltage_buses, size=10, color=\"red\")\n", "\n", "pplotly.draw_traces(bc + bch + lc + lcl);" ] }, { "cell_type": "markdown", "metadata": { "collapsed": true, "jupyter": { "outputs_hidden": true } }, "source": [ "## Highlighting with the Topology Package\n", "Colors palette for plotly can be obtained using function `plotting.plotly.get_plotly_color_palette(n)` where argument `n` defines number of colors that will be returned in a list." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:41.377690Z", "start_time": "2025-11-26T11:20:40.081365Z" } }, "outputs": [], "source": [ "from pandapower.plotting.plotly import get_plotly_color_palette\n", "from pandapower.topology import create_nxgraph, connected_components\n", "\n", "net = nw.mv_oberrhein()\n", "\n", "mg = create_nxgraph(net, nogobuses=set(net.trafo.lv_bus.values) | set(net.trafo.hv_bus.values))\n", "collections = []\n", "ai = 0\n", "islands = list(connected_components(mg)) # getting connected components of a graph\n", "colors = get_plotly_color_palette(len(islands)) # getting a color for each connected component\n", "for color, area in zip(colors, islands):\n", " collections += pplotly.create_bus_trace(net, area, size=5, color=color, trace_name=\"feeder {0}\".format(ai))\n", " ai += 1\n", "collections += pplotly.create_line_trace(net, net.line.index, color=\"grey\")\n", "collections += pplotly.create_bus_trace(net, net.ext_grid.bus.values, patch_type=\"square\", size=10, color=\"yellow\")\n", "pplotly.draw_traces(collections);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Collection on mapbox\n", "Plots on Mapbox maps are available only considering you have a Mapbox account and a Mapbox Access Token. After getting a mabox token it can be written set to pandapower as the following (where `''` needs to be replaced with provided mapbox token).\n", "If network geo-data are not in lat/long form, there is a function `geo_data_to_latlong` which can be used to transform entire network geodata from a specific projection to lat/long:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:43.441138Z", "start_time": "2025-11-26T11:20:41.656355Z" } }, "outputs": [], "source": [ "net = nw.mv_oberrhein()\n", "# convert_crs(net, epsg_out=31467) #transforming geodata to Gauß Krüger Zone 3\n", "\n", "lc = pplotly.create_line_trace(net, net.line.index, color=\"purple\")\n", "bc = pplotly.create_bus_trace(\n", " net,\n", " net.bus.index,\n", " size=10,\n", " color=\"orange\",\n", " infofunc=Series(index=net.bus.index, data=net.bus.name + \"
\" + net.bus.vn_kv.astype(str) + \" kV\"),\n", ")\n", "_ = pplotly.draw_traces(lc + bc, on_map=True, map_style=\"dark\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Colormaps\n", "Colormaps plots are available for plotly by setting `cmap=True` in create functions. By default, line loading and bus voltage magnitudes will be used for coloring." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:44.769454Z", "start_time": "2025-11-26T11:20:43.669112Z" } }, "outputs": [], "source": [ "net = nw.mv_oberrhein()\n", "bt = pplotly.create_bus_trace(net, cmap=True)\n", "lt = pplotly.create_line_trace(net, cmap=True)\n", "pplotly.draw_traces(lt + bt, showlegend=False);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Some customization is possible in regard to: limits, choosing a colormap, colorbar title, etc." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:45.368409Z", "start_time": "2025-11-26T11:20:45.093158Z" } }, "outputs": [], "source": [ "lt = pplotly.create_line_trace(net, cmap=\"Blues\", cmin=0, cmax=100, cbar_title=\"Line loading [%]\")\n", "pplotly.draw_traces(lt, showlegend=False);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Alternatively, any other values can be used for colormaps, with also some customization in regard to limits, choosing a colormap, colorbar title, etc. An example with coloring according to line length:" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:45.989452Z", "start_time": "2025-11-26T11:20:45.578423Z" } }, "outputs": [], "source": [ "lt = pplotly.create_line_trace(net, cmap_vals=net.line.length_km, cmap=True, cbar_title=\"Line length [km]\")\n", "pplotly.draw_traces(lt, showlegend=False);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Customized hover-info\n", "Information that pop-ups when a bus or a line is pointed with mouse-cursor is called hover-info. By default, it is set to bus/line name, but it can be customized by setting a list of strings to `infofunc` argument. A newline is defined using `
`. See a representative example hereafter (try getting hoverinfo by pointing a cursor above each bus):" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:46.381044Z", "start_time": "2025-11-26T11:20:46.196803Z" } }, "outputs": [], "source": [ "bc = pplotly.create_bus_trace(\n", " net,\n", " [1, 2, 3, 4, 5],\n", " size=15,\n", " infofunc=Series(\n", " index=net.bus.index,\n", " data=net.bus.name + \"
\" + net.bus.vn_kv.astype(str) + \" kV\" + \"
\" + \"this is one of the 5 buses...\",\n", " ),\n", ")\n", "pplotly.draw_traces(bc);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Weighted Marker Traces\n", "A trace with markers of value-dependent size can be created by the `create_weighted_marker_trace` function. Like other traces, this trace can be passed to `draw_traces` directly, or it can be passed as \"additional trace\" to `simple_plotly`. By default, the latter will also add a reference marker to indicate the value for one size." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:47.224355Z", "start_time": "2025-11-26T11:20:46.694026Z" } }, "outputs": [], "source": [ "net.sgen.scaling = 1\n", "marker_trace = pplotly.create_weighted_marker_trace(\n", " net, \"sgen\", column_to_plot=\"p_mw\", marker_scaling=200, color=\"green\", patch_type=\"circle-open\"\n", ")\n", "_ = pplotly.simple_plotly(net, bus_size=0.25, additional_traces=[marker_trace])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "ExecuteTime": { "end_time": "2025-11-26T11:20:47.930266Z", "start_time": "2025-11-26T11:20:47.405120Z" } }, "outputs": [], "source": [ "marker_trace = pplotly.create_weighted_marker_trace(\n", " net, \"sgen\", column_to_plot=\"p_mw\", marker_scaling=400, color=\"green\", patch_type=\"circle-open\", show_scale_legend=True, scale_legend_unit='MW', scale_marker_size=[0., 1, 2, 5]\n", ")\n", "pplotly.simple_plotly(net, bus_size=0.25, additional_traces=[marker_trace]);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "More tutorials about interactive plots using plotly:\n", "* [built-in interactive plots](./plotly_built-in.ipynb)\n", "* [interactive plots on maps](./plotly_maps.ipynb)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3" } }, "nbformat": 4, "nbformat_minor": 4 }