def create_edge_center_trace(line_trace, size=1, patch_type="circle", color="grey", infofunc=None, trace_name='edge_center'): """ Creates a plotly trace of pandapower buses. INPUT: **line traces** (from pandapowerNet) - The already generated line traces with center geodata OPTIONAL: **size** (int, 5) - patch size **patch_type** (str, "circle") - patch type, can be - "circle" for a circle - "square" for a rectangle - "diamond" for a diamond - much more pathc types at https://plot.ly/python/reference/#scatter-marker **infofunc** (list, None) - hoverinfo for each trace element **trace_name** (String, "buses") - name of the trace which will appear in the legend **color** (String, "blue") - color of buses in the trace """ color = get_plotly_color(color) bus_trace = dict(type='scatter', text=[], mode='markers', hoverinfo='text', name=trace_name, marker=dict(color=color, size=size, symbol=patch_type)) bus_trace['x'], bus_trace['y'] = (line_trace[0]["x"][1::4], line_trace[0]["y"][1::4]) bus_trace['text'] = infofunc return [bus_trace]
def create_bus_trace(net, buses=None, size=5, patch_type="circle", color="blue", infofunc=None, trace_name='buses', legendgroup=None, cmap=None, cmap_vals=None, cbar_title=None, cmin=None, cmax=None, colormap_column="vm_pu"): """ Creates a plotly trace of pandapower buses. INPUT: **net** (pandapowerNet) - The pandapower network OPTIONAL: **buses** (list, None) - The buses for which the collections are created. If None, all buses in the network are considered. **size** (int, 5) - patch size **patch_type** (str, "circle") - patch type, can be - "circle" for a circle - "square" for a rectangle - "diamond" for a diamond - much more pathc types at https://plot.ly/python/reference/#scatter-marker **infofunc** (list, None) - hoverinfo for each trace element **trace_name** (String, "buses") - name of the trace which will appear in the legend **color** (String, "blue") - color of buses in the trace **cmap** (String, None) - name of a colormap which exists within plotly (Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis) alternatively a custom discrete colormap can be used **cmap_vals** (list, None) - values used for coloring using colormap **cbar_title** (String, None) - title for the colorbar **cmin** (float, None) - colorbar range minimum **cmax** (float, None) - colorbar range maximum **colormap_column** (str, "vm_pu") - set color of bus according to this variable """ color = get_plotly_color(color) bus_trace = dict(type='scatter', text=[], mode='markers', hoverinfo='text', name=trace_name, marker=dict(color=color, size=size, symbol=patch_type)) buses = net.bus.index.tolist() if buses is None else list(buses) buses2plot = net.bus.index.isin(buses) buses_with_geodata = net.bus.index.isin(net.bus_geodata.index) buses2plot = buses2plot & buses_with_geodata bus_plot_index = net.bus.index[buses2plot] bus_trace['x'], bus_trace['y'] = (net.bus_geodata.loc[bus_plot_index, 'x'].tolist(), net.bus_geodata.loc[bus_plot_index, 'y'].tolist()) bus_trace['text'] = net.bus.loc[bus_plot_index, 'name'] if infofunc is None else infofunc if legendgroup: bus_trace['legendgroup'] = legendgroup # if color map is set if cmap is not None: # TODO introduce discrete colormaps (see contour plots in plotly) # if cmap_vals are not given cmap = 'Jet' if cmap is True else cmap if cmap_vals is not None: cmap_vals = cmap_vals else: if net.res_line.shape[0] == 0: logger.error( "There are no power flow results for buses voltage magnitudes which are default for bus " "colormap coloring..." "set cmap_vals input argument if you want colormap according to some specific values..." ) cmap_vals = net.res_bus.loc[bus_plot_index, colormap_column].values cmap_vals = net.res_bus.loc[ bus_plot_index, colormap_column] if cmap_vals is None else cmap_vals cmin = cmin if cmin else cmap_vals.min() cmax = cmax if cmax else cmap_vals.max() bus_trace['marker'] = Marker( size=size, color=cmap_vals, cmin=cmin, cmax=cmax, colorscale=cmap, colorbar=ColorBar(thickness=10, x=1.0, titleside='right'), ) if cbar_title: bus_trace['marker']['colorbar']['title'] = cbar_title return [bus_trace]
def create_trafo_trace(net, trafos=None, color='green', width=5, infofunc=None, cmap=None, trace_name='trafos', cmin=None, cmax=None, cmap_vals=None): """ Creates a plotly trace of pandapower trafos. INPUT: **net** (pandapowerNet) - The pandapower network OPTIONAL: **trafos** (list, None) - The trafos for which the collections are created. If None, all trafos in the network are considered. **width** (int, 5) - line width **infofunc** (list, None) - hoverinfo for each line **trace_name** (String, "lines") - name of the trace which will appear in the legend **color** (String, "green") - color of lines in the trace **cmap** (bool, False) - name of a colormap which exists within plotly (Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis) **cmap_vals** (list, None) - values used for coloring using colormap **cbar_title** (String, None) - title for the colorbar **cmin** (float, None) - colorbar range minimum **cmax** (float, None) - colorbar range maximum """ color = get_plotly_color(color) # defining lines to be plot trafos = net.trafo.index.tolist() if trafos is None else list(trafos) if len(trafos) == 0: return [] trafo_buses_with_geodata = net.trafo.hv_bus.isin(net.bus_geodata.index) &\ net.trafo.lv_bus.isin(net.bus_geodata.index) trafos_mask = net.trafo.index.isin(trafos) tarfo2plot = net.trafo[trafo_buses_with_geodata & trafos_mask] if cmap is not None: cmap = 'jet' if cmap is None else cmap cmin = 0 if cmin is None else cmin cmax = 100 if cmin is None else cmax if cmap_vals is not None: cmap_vals = cmap_vals else: if net.res_trafo.shape[0] == 0: logger.error( "There are no power flow results for lines which are default for line colormap coloring..." "set cmap_vals input argument if you want colormap according to some specific values..." ) cmap_vals = net.res_trafo.loc[tarfo2plot.index, 'loading_percent'].values cmap_colors = get_plotly_cmap(cmap_vals, cmap_name=cmap, cmin=cmin, cmax=cmax) trafo_traces = [] col_i = 0 for _, trafo in tarfo2plot.iterrows(): trafo_trace = dict(type='scatter', text=[], line=Line(width=width, color=cmap_colors[col_i]), hoverinfo='text', mode='lines', name=trace_name) trafo_trace['text'] = trafo['name'].tolist( ) if infofunc is None else infofunc[col_i] from_bus = net.bus_geodata.loc[trafo.hv_bus, 'x'] to_bus = net.bus_geodata.loc[trafo.lv_bus, 'x'] trafo_trace['x'] = [from_bus, (from_bus + to_bus) / 2, to_bus] from_bus = net.bus_geodata.loc[trafo.hv_bus, 'y'] to_bus = net.bus_geodata.loc[trafo.lv_bus, 'y'] trafo_trace['y'] = [from_bus, (from_bus + to_bus) / 2, to_bus] trafo_traces.append(trafo_trace) col_i += 1 else: trafo_trace = dict(type='scatter', text=[], line=dict(width=width, color=color), hoverinfo='text', mode='lines', name=trace_name) trafo_trace['text'] = tarfo2plot['name'].tolist( ) if infofunc is None else infofunc from_bus = net.bus_geodata.loc[tarfo2plot.hv_bus, 'x'].tolist() to_bus = net.bus_geodata.loc[tarfo2plot.lv_bus, 'x'].tolist() # center point added because of the hovertool center = (np.array(from_bus) + np.array(to_bus)) / 2 None_list = [None] * len(from_bus) trafo_trace['x'] = np.array([from_bus, center, to_bus, None_list]).T.flatten().tolist() trafo_trace['x'] = trafo_trace['x'][:-1] from_bus = net.bus_geodata.loc[tarfo2plot.hv_bus, 'y'].tolist() to_bus = net.bus_geodata.loc[tarfo2plot.lv_bus, 'y'].tolist() # center point added because of the hovertool center = (np.array(from_bus) + np.array(to_bus)) / 2 None_list = [None] * len(from_bus) trafo_trace['y'] = np.array([from_bus, center, to_bus, None_list]).T.flatten().tolist() trafo_trace['y'] = trafo_trace['y'][:-1] trafo_traces = [trafo_trace] return trafo_traces
def create_line_trace(net, lines=None, use_line_geodata=True, respect_switches=False, width=1.0, color='grey', infofunc=None, trace_name='lines', legendgroup=None, cmap=None, cbar_title=None, show_colorbar=True, cmap_vals=None, cmin=None, cmax=None): """ Creates a plotly trace of pandapower lines. INPUT: **net** (pandapowerNet) - The pandapower network OPTIONAL: **lines** (list, None) - The lines for which the collections are created. If None, all lines in the network are considered. **width** (int, 1) - line width **respect_switches** (bool, False) - flag for consideration of disconnected lines **infofunc** (list, None) - hoverinfo for each line **trace_name** (String, "lines") - name of the trace which will appear in the legend **color** (String, "grey") - color of lines in the trace **legendgroup** (String, None) - defines groups of layers that will be displayed in a legend e.g. groups according to voltage level (as used in `vlevel_plotly`) **cmap** (String, None) - name of a colormap which exists within plotly if set to True default `Jet` colormap is used, alternative colormaps : Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis **cmap_vals** (list, None) - values used for coloring using colormap **show_colorbar** (bool, False) - flag for showing or not corresponding colorbar **cbar_title** (String, None) - title for the colorbar **cmin** (float, None) - colorbar range minimum **cmax** (float, None) - colorbar range maximum """ color = get_plotly_color(color) # defining lines to be plot lines = net.line.index.tolist() if lines is None else list(lines) if len(lines) == 0: return [] nogolines = set() if respect_switches: nogolines = set(net.switch.element[(net.switch.et == "l") & (net.switch.closed == 0)]) nogolines_mask = net.line.index.isin(nogolines) lines_mask = net.line.index.isin(lines) lines2plot_mask = ~nogolines_mask & lines_mask lines2plot = net.line[lines2plot_mask] use_line_geodata = use_line_geodata if net.line_geodata.shape[ 0] > 0 else False if use_line_geodata: lines_with_geodata = lines2plot.index.isin(net.line_geodata.index) lines2plot = lines2plot[lines_with_geodata] else: lines_with_geodata = lines2plot.from_bus.isin(net.bus_geodata.index) & \ lines2plot.to_bus.isin(net.bus_geodata.index) lines2plot = lines2plot[lines_with_geodata] if cmap is not None: # workaround: if colormap plot is used, each line need to be separate scatter object because # plotly still doesn't support appropriately colormap for line objects # TODO correct this when plotly solves existing github issue about Line colorbar cmap = 'jet' if cmap is True else cmap if cmap_vals is not None: cmap_vals = cmap_vals else: if net.res_line.shape[0] == 0: logger.error( "There are no power flow results for lines which are default for line colormap coloring..." "set cmap_vals input argument if you want colormap according to some specific values..." ) cmap_vals = net.res_line.loc[lines2plot.index, 'loading_percent'].values cmap_lines = get_plotly_cmap(cmap_vals, cmap_name=cmap, cmin=cmin, cmax=cmax) cmap_lines = list(compress( cmap_lines, lines2plot_mask)) # select with mask from cmap_lines if infofunc is not None: infofunc = list(compress(infofunc, lines2plot_mask)) line_traces = [] col_i = 0 for idx, line in lines2plot.iterrows(): line_trace = dict(type='scatter', text=[], hoverinfo='text', mode='lines', name=trace_name, line=Line(width=width, color=color)) line_trace['x'], line_trace['y'] = _get_line_geodata_plotly( net, lines2plot.loc[idx:idx], use_line_geodata) line_trace['line']['color'] = cmap_lines[col_i] line_trace[ 'text'] = line['name'] if infofunc is None else infofunc[col_i] line_traces.append(line_trace) col_i += 1 cmin = cmin if cmin else cmap_vals.min() cmax = cmax if cmax else cmap_vals.max() if show_colorbar: try: # TODO for custom colormaps cbar_cmap_name = 'Jet' if cmap is 'jet' else cmap # workaround to get colorbar for lines (an unvisible node is added) lines_cbar = dict(type='scatter', x=[net.bus_geodata.x[0]], y=[net.bus_geodata.y[0]], mode='markers', marker=Marker( size=0, cmin=cmin, cmax=cmax, color='rgb(255,255,255)', colorscale=cbar_cmap_name, colorbar=ColorBar(thickness=10, x=1.1, titleside='right'), )) if cbar_title: lines_cbar['marker']['colorbar']['title'] = cbar_title line_traces.append(lines_cbar) except: pass else: line_trace = dict(type='scatter', text=[], hoverinfo='text', mode='lines', name=trace_name, line=Line(width=width, color=color)) line_trace['x'], line_trace['y'] = _get_line_geodata_plotly( net, lines2plot, use_line_geodata) line_trace['text'] = lines2plot['name'].tolist( ) if infofunc is None else infofunc if legendgroup: line_trace['legendgroup'] = legendgroup line_traces = [line_trace] if len(nogolines) > 0: line_trace = dict(type='scatter', text=[], hoverinfo='text', mode='lines', name='disconnected lines', line=Line(width=width / 2, color='grey', dash='dot')) lines2plot = net.line.loc[nogolines] line_trace['x'], line_trace['y'] = _get_line_geodata_plotly( net, lines2plot, use_line_geodata) line_trace['text'] = lines2plot['name'].tolist() if legendgroup: line_trace['legendgroup'] = legendgroup line_traces.append(line_trace) return line_traces
def create_trafo_trace(net, trafos=None, color='green', width=5, infofunc=None, cmap=None, trace_name='trafos', cmin=None, cmax=None, cmap_vals=None, use_line_geodata=None): """ Creates a plotly trace of pandapower trafos. INPUT: **net** (pandapowerNet) - The pandapower network OPTIONAL: **trafos** (list, None) - The trafos for which the collections are created. If None, all trafos in the network are considered. **width** (int, 5) - line width **infofunc** (pd.Series, None) - hoverinfo for trafo elements. Indices should correspond to the pandapower element indices **trace_name** (String, "lines") - name of the trace which will appear in the legend **color** (String, "green") - color of lines in the trace **cmap** (bool, False) - name of a colormap which exists within plotly (Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis) **cmap_vals** (list, None) - values used for coloring using colormap **cbar_title** (String, None) - title for the colorbar **cmin** (float, None) - colorbar range minimum **cmax** (float, None) - colorbar range maximum """ color = get_plotly_color(color) # defining lines to be plot trafos = net.trafo.index.tolist() if trafos is None else list(trafos) if len(trafos) == 0: return [] trafo_buses_with_geodata = net.trafo.hv_bus.isin(net.bus_geodata.index) & \ net.trafo.lv_bus.isin(net.bus_geodata.index) trafos_mask = net.trafo.index.isin(trafos) trafos_to_plot = net.trafo[trafo_buses_with_geodata & trafos_mask] if infofunc is not None: if not isinstance(infofunc, pd.Series) and isinstance(infofunc, Iterable) and \ len(infofunc) == len(trafos): infofunc = pd.Series(index=trafos, data=infofunc) assert isinstance(infofunc, pd.Series), \ "infofunc should be a pandas series with the net.trafo.index to the infofunc contents" infofunc = infofunc.loc[trafos_to_plot.index] cmap_colors = [] if cmap is not None: cmap = 'jet' if cmap is None else cmap cmin = 0 if cmin is None else cmin cmax = 100 if cmin is None else cmax if cmap_vals is not None: cmap_vals = cmap_vals else: if net.res_trafo.shape[0] == 0: logger.error( "There are no power flow results for lines which are default for line colormap coloring..." "set cmap_vals input argument if you want colormap according to some specific values..." ) cmap_vals = net.res_trafo.loc[trafos_to_plot.index, 'loading_percent'].values cmap_colors = get_plotly_cmap(cmap_vals, cmap_name=cmap, cmin=cmin, cmax=cmax) trafo_traces = [] for col_i, (idx, trafo) in enumerate(trafos_to_plot.iterrows()): if cmap is not None: color = cmap_colors[col_i] trafo_trace = dict(type='scatter', text=[], line=Line(width=width, color=color), hoverinfo='text', mode='lines', name=trace_name) trafo_trace[ 'text'] = trafo['name'] if infofunc is None else infofunc.loc[idx] from_bus = net.bus_geodata.loc[trafo.hv_bus, 'x'] to_bus = net.bus_geodata.loc[trafo.lv_bus, 'x'] trafo_trace['x'] = [from_bus, (from_bus + to_bus) / 2, to_bus] from_bus = net.bus_geodata.loc[trafo.hv_bus, 'y'] to_bus = net.bus_geodata.loc[trafo.lv_bus, 'y'] trafo_trace['y'] = [from_bus, (from_bus + to_bus) / 2, to_bus] trafo_traces.append(trafo_trace) center_trace = create_edge_center_trace(trafo_traces, color=color, infofunc=infofunc, use_line_geodata=use_line_geodata) trafo_traces.append(center_trace) return trafo_traces
def create_line_trace(net, lines=None, use_line_geodata=True, respect_switches=False, width=1.0, color='grey', infofunc=None, trace_name='lines', legendgroup=None, cmap=None, cbar_title=None, show_colorbar=True, cmap_vals=None, cmin=None, cmax=None, cpos=1.1): """ Creates a plotly trace of pandapower lines. INPUT: **net** (pandapowerNet) - The pandapower network OPTIONAL: **lines** (list, None) - The lines for which the collections are created. If None, all lines in the network are considered. **width** (int, 1) - line width **respect_switches** (bool, False) - flag for consideration of disconnected lines **infofunc** (pd.Series, None) - hoverinfo for line elements. Indices should correspond to the pandapower element indices **trace_name** (String, "lines") - name of the trace which will appear in the legend **color** (String, "grey") - color of lines in the trace **legendgroup** (String, None) - defines groups of layers that will be displayed in a legend e.g. groups according to voltage level (as used in `vlevel_plotly`) **cmap** (String, None) - name of a colormap which exists within plotly if set to True default `Jet` colormap is used, alternative colormaps : Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis **cmap_vals** (list, None) - values used for coloring using colormap **show_colorbar** (bool, False) - flag for showing or not corresponding colorbar **cbar_title** (String, None) - title for the colorbar **cmin** (float, None) - colorbar range minimum **cmax** (float, None) - colorbar range maximum **cpos** (float, 1.1) - position of the colorbar """ color = get_plotly_color(color) # defining lines to be plot lines = net.line.index.tolist() if lines is None else list(lines) if len(lines) == 0: return [] if infofunc is not None: if not isinstance(infofunc, pd.Series) and isinstance(infofunc, Iterable) and \ len(infofunc) == len(lines): infofunc = pd.Series(index=lines, data=infofunc) if len(infofunc) != len(lines) and len(infofunc) != len(net.line): raise UserWarning( "Different amount of hover info than lines to plot") assert isinstance(infofunc, pd.Series), \ "infofunc should be a pandas series with the net.line.index to the infofunc contents" no_go_lines = set() if respect_switches: no_go_lines = set(lines) & set(net.switch.element[ (net.switch.et == "l") & (net.switch.closed == 0)]) lines_to_plot = net.line.loc[set(net.line.index) & (set(lines) - no_go_lines)] no_go_lines_to_plot = None use_line_geodata = use_line_geodata if net.line_geodata.shape[ 0] > 0 else False if use_line_geodata: lines_to_plot = lines_to_plot.loc[set(lines_to_plot.index) & set(net.line_geodata.index)] else: lines_with_geodata = lines_to_plot.from_bus.isin(net.bus_geodata.index) & \ lines_to_plot.to_bus.isin(net.bus_geodata.index) lines_to_plot = lines_to_plot.loc[lines_with_geodata] cmap_lines = None if cmap is not None: # workaround: if colormap plot is used, each line need to be separate scatter object because # plotly still doesn't support appropriately colormap for line objects # TODO correct this when plotly solves existing github issue about Line colorbar cmap = 'jet' if cmap is True else cmap if cmap_vals is not None: if not isinstance(cmap_vals, np.ndarray): cmap_vals = np.asarray(cmap_vals) else: if net.res_line.shape[0] == 0: logger.error( "There are no power flow results for lines which are default for line colormap coloring..." "set cmap_vals input argument if you want colormap according to some specific values..." ) cmap_vals = net.res_line.loc[lines_to_plot.index, 'loading_percent'].values cmap_lines = get_plotly_cmap(cmap_vals, cmap_name=cmap, cmin=cmin, cmax=cmax) if len(cmap_lines) == len(net.line): # some lines are not plotted although cmap_value were provided for all lines line_idx_map = dict( zip(net.line.loc[lines].index.tolist(), range(len(lines)))) cmap_lines = [ cmap_lines[line_idx_map[idx]] for idx in lines_to_plot.index ] else: assert len(cmap_lines) == len(lines_to_plot), \ "Different amounts of cmap values and lines to plot were supplied" line_traces = [] for col_i, (idx, line) in enumerate(lines_to_plot.iterrows()): line_color = color line_info = line['name'] if cmap is not None: try: line_color = cmap_lines[col_i] line_info = line['name'] if infofunc is None else infofunc.loc[ idx] except IndexError: logger.warning( "No color and info for line {:d} (name: {}) available". format(idx, line['name'])) line_trace = dict(type='scatter', text=[], hoverinfo='text', mode='lines', name=trace_name, line=Line(width=width, color=color)) line_trace['x'], line_trace['y'] = _get_line_geodata_plotly( net, lines_to_plot.loc[idx:idx], use_line_geodata) line_trace['line']['color'] = line_color line_trace['text'] = line_info line_traces.append(line_trace) if show_colorbar and cmap is not None: cmin = cmap_vals.min() if cmin is None else cmin cmax = cmap_vals.max() if cmax is None else cmax try: # TODO for custom colormaps cbar_cmap_name = 'Jet' if cmap == 'jet' else cmap # workaround to get colorbar for lines (an unvisible node is added) # get x and y of first line.from_bus: x = [net.bus_geodata.x[net.line.from_bus[net.line.index[0]]]] y = [net.bus_geodata.y[net.line.from_bus[net.line.index[0]]]] lines_cbar = dict(type='scatter', x=x, y=y, mode='markers', marker=Marker( size=0, cmin=cmin, cmax=cmax, color='rgb(255,255,255)', opacity=0, colorscale=cbar_cmap_name, colorbar=ColorBar(thickness=10, x=cpos), )) if cbar_title: lines_cbar['marker']['colorbar']['title'] = cbar_title lines_cbar['marker']['colorbar']['title']['side'] = 'right' line_traces.append(lines_cbar) except: pass if len(no_go_lines) > 0: no_go_lines_to_plot = net.line.loc[no_go_lines] for idx, line in no_go_lines_to_plot.iterrows(): line_color = color line_trace = dict(type='scatter', text=[], hoverinfo='text', mode='lines', name='disconnected lines', line=Line(width=width / 2, color='grey', dash='dot')) line_trace['x'], line_trace['y'] = _get_line_geodata_plotly( net, no_go_lines_to_plot.loc[idx:idx], use_line_geodata) line_trace['line']['color'] = line_color try: line_trace['text'] = infofunc.loc[idx] except (KeyError, IndexError, AttributeError): line_trace["text"] = line['name'] line_traces.append(line_trace) if legendgroup: line_trace['legendgroup'] = legendgroup # sort infofunc so that it is the correct order lines_to_plot + no_go_lines_to_plot if infofunc is not None: if not isinstance(infofunc, pd.Series) and isinstance(infofunc, Iterable) and \ len(infofunc) == len(net.line): infofunc = pd.Series(index=net.line.index, data=infofunc) assert isinstance(infofunc, pd.Series), \ "infofunc should be a pandas series with the net.line.index to the infofunc contents" sorted_idx = lines_to_plot.index.tolist() if no_go_lines_to_plot is not None: sorted_idx += no_go_lines_to_plot.index.tolist() infofunc = infofunc.loc[sorted_idx] center_trace = create_edge_center_trace(line_traces, color=color, infofunc=infofunc, use_line_geodata=use_line_geodata) line_traces.append(center_trace) return line_traces
def _create_branch_trace(net, branches=None, use_branch_geodata=True, respect_separators=False, width=1.0, color='grey', infofunc=None, trace_name='lines', legendgroup=None, cmap=None, cbar_title=None, show_colorbar=True, cmap_vals=None, cmin=None, cmax=None, cpos=1.1, branch_element='line', separator_element='switch', node_element='bus', cmap_vals_category='loading_percent'): """ Creates a plotly trace of branch elements. The rather generic, non-power net specific names were introduced to make it usable in other packages, e.g. for pipe networks. INPUT: **net** (pandapowerNet) - The network OPTIONAL: **branches** (list, None) - The branches for which the collections are created. If None, all branches in the network are considered. **use_branch_geodata** (bool, True) - whether the geodata of the branch tables should be used **respect_separators** (bool, True) - whether separating elements like switches should be considered **width** (int, 1) - branch width **color** (String, "grey") - color of lines in the trace **infofunc** (pd.Series, None) - hoverinfo for line elements. Indices should correspond to the pandapower element indices **trace_name** (String, "lines") - name of the trace which will appear in the legend **legendgroup** (String, None) - defines groups of layers that will be displayed in a legend e.g. groups according to voltage level (as used in `vlevel_plotly`) **cmap** (String, None) - name of a colormap which exists within plotly if set to True default `Jet` colormap is used, alternative colormaps : Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis **cmap_vals** (list, None) - values used for coloring using colormap **show_colorbar** (bool, False) - flag for showing or not corresponding colorbar **cbar_title** (String, None) - title for the colorbar **cmin** (float, None) - colorbar range minimum **cmax** (float, None) - colorbar range maximum **cpos** (float, 1.1) - position of the colorbar **branch_element** (str, "line") - name of the branch element in the net. In a pandapower net, this is alwas "line" **separator_element** (str, "switch") - name of the separator element in the net. In a pandapower net, this is alwas "switch" **node_element** (str, "bus") - name of the node element in the net. In a pandapower net, this is alwas "bus" (net.bus) """ color = get_plotly_color(color) # defining branches (lines) to be plot branches = net[branch_element].index.tolist( ) if branches is None else list(branches) if len(branches) == 0: return [] if infofunc is not None: if not isinstance(infofunc, pd.Series) and isinstance(infofunc, Iterable) and \ len(infofunc) == len(branches): infofunc = pd.Series(index=branches, data=infofunc) if len(infofunc) != len(branches) and len(infofunc) != len( net[branch_element]): raise UserWarning("Different amount of hover info than {}s to " "plot".format(branch_element)) assert isinstance(infofunc, pd.Series), \ "infofunc should be a pandas series with the net.{}.index to the infofunc " \ "contents".format(branch_element) no_go_branches = set() if respect_separators: if separator_element == "switch": no_go_branches = set(branches) & \ set(net[separator_element].element[(net[separator_element].et == "l") & (net[separator_element].closed == 0)]) elif separator_element == "valve": no_go_branches = set(branches) & \ set(net[separator_element][(~net[separator_element].in_service) | (net[separator_element].opened)]) else: raise NotImplementedError( "respect separtors is only implements for switches, " "not for {}s.".format(separator_element)) branches_to_plot = net[branch_element].loc[set(net[branch_element].index) & (set(branches) - no_go_branches)] no_go_branches_to_plot = None branch_geodata = branch_element + "_geodata" node_geodata = node_element + "_geodata" use_branch_geodata = use_branch_geodata if net[branch_geodata].shape[ 0] > 0 else False if use_branch_geodata: branches_to_plot = branches_to_plot.loc[ set(branches_to_plot.index) & set(net[branch_geodata].index)] else: branches_with_geodata = branches_to_plot['from_'+node_element].isin( net[node_geodata].index) & \ branches_to_plot['to_'+node_element].isin(net[node_geodata].index) branches_to_plot = branches_to_plot.loc[branches_with_geodata] cmap_branches = None if cmap is not None: # workaround: if colormap plot is used, each line need to be separate scatter object because # plotly still doesn't support appropriately colormap for line objects # TODO correct this when plotly solves existing github issue about Line colorbar cmap = 'jet' if cmap is True else cmap if cmap_vals is not None: if not isinstance(cmap_vals, np.ndarray): cmap_vals = np.asarray(cmap_vals) else: if net['res_' + branch_element].shape[0] == 0: logger.error( "There are no simulation results for branches which are default for {}" "colormap coloring..." "set cmap_vals input argument if you want colormap according to some specific " "values...".format(branch_element)) cmap_vals = net['res_' + branch_element].loc[branches_to_plot.index, cmap_vals_category].values cmap_branches = get_plotly_cmap(cmap_vals, cmap_name=cmap, cmin=cmin, cmax=cmax) if len(cmap_branches) == len(net[branch_element]): # some branches are not plotted although cmap_value were provided for all branches branch_idx_map = dict( zip(net[branch_element].loc[branches].index.tolist(), range(len(branches)))) cmap_branches = [ cmap_branches[branch_idx_map[idx]] for idx in branches_to_plot.index ] else: assert len(cmap_branches) == len(branches_to_plot), \ "Different amounts of cmap values and branches to plot were supplied" branch_traces = [] for col_i, (idx, branch) in enumerate(branches_to_plot.iterrows()): line_color = color line_info = branch['name'] if cmap is not None: try: line_color = cmap_branches[col_i] line_info = branch[ 'name'] if infofunc is None else infofunc.loc[idx] except IndexError: logger.warning( "No color and info for {} {:d} (name: {}) available". format(branch_element, idx, branch['name'])) line_trace = dict(type='scatter', text=[], hoverinfo='text', mode='lines', name=trace_name, line=Line(width=width, color=color)) line_trace['x'], line_trace['y'] = _get_branch_geodata_plotly( net, branches_to_plot.loc[idx:idx], use_branch_geodata, branch_element, node_element) line_trace['line']['color'] = line_color line_trace['text'] = line_info branch_traces.append(line_trace) if show_colorbar and cmap is not None: cmin = cmap_vals.min() if cmin is None else cmin cmax = cmap_vals.max() if cmax is None else cmax try: # TODO for custom colormaps cbar_cmap_name = 'Jet' if cmap == 'jet' else cmap # workaround to get colorbar for branches (an unvisible node is added) # get x and y of first line.from_bus: x = [ net[node_geodata].x[net[branch_element]["from_" + node_element] [net[branch_element].index[0]]] ] y = [ net[node_geodata].y[net[branch_element]["from_" + node_element] [net[branch_element].index[0]]] ] branches_cbar = dict(type='scatter', x=x, y=y, mode='markers', marker=Marker( size=0, cmin=cmin, cmax=cmax, color='rgb(255,255,255)', opacity=0, colorscale=cbar_cmap_name, colorbar=ColorBar(thickness=10, x=cpos), )) if cbar_title: branches_cbar['marker']['colorbar']['title'] = cbar_title branches_cbar['marker']['colorbar']['title']['side'] = 'right' branch_traces.append(branches_cbar) except: pass if len(no_go_branches) > 0: no_go_branches_to_plot = net[branch_element].loc[no_go_branches] for idx, branch in no_go_branches_to_plot.iterrows(): line_color = color line_trace = dict(type='scatter', text=[], hoverinfo='text', mode='lines', name='disconnected branches', line=Line(width=width / 2, color='grey', dash='dot')) line_trace['x'], line_trace['y'] = _get_branch_geodata_plotly( net, no_go_branches_to_plot.loc[idx:idx], use_branch_geodata, branch_element, node_element) line_trace['line']['color'] = line_color try: line_trace['text'] = infofunc.loc[idx] except (KeyError, IndexError, AttributeError): line_trace["text"] = branch['name'] branch_traces.append(line_trace) if legendgroup: line_trace['legendgroup'] = legendgroup # sort infofunc so that it is the correct order lines_to_plot + no_go_lines_to_plot if infofunc is not None: if not isinstance(infofunc, pd.Series) and isinstance(infofunc, Iterable) and \ len(infofunc) == len(net[branch_element]): infofunc = pd.Series(index=net[branch_element].index, data=infofunc) assert isinstance(infofunc, pd.Series), \ "infofunc should be a pandas series with the net.{}.index to the infofunc contents" \ .format(branch_element) sorted_idx = branches_to_plot.index.tolist() if no_go_branches_to_plot is not None: sorted_idx += no_go_branches_to_plot.index.tolist() infofunc = infofunc.loc[sorted_idx] center_trace = create_edge_center_trace( branch_traces, color=color, infofunc=infofunc, use_line_geodata=use_branch_geodata) branch_traces.append(center_trace) return branch_traces
def _create_node_trace(net, nodes=None, size=5, patch_type='circle', color='blue', infofunc=None, trace_name='nodes', legendgroup=None, cmap=None, cmap_vals=None, cbar_title=None, cmin=None, cmax=None, cpos=1.0, colormap_column='vm_pu', node_element='bus', branch_element='line'): """ Creates a plotly trace of node elements. In pandapower, it should be called by create_bus_traces. The rather generic, non-power net specific names were introduced to make it usable in other packages, e.g. for pipe networks. INPUT: **net** (pandapowerNet) - The network OPTIONAL: **nodes** (list, None) - The nodes for which the collections are created. If None, all nodes in the network are considered. **size** (int, 5) - patch size **patch_type** (str, "circle") - patch type, can be - "circle" for a circle - "square" for a rectangle - "diamond" for a diamond - much more pathc types at https://plot.ly/python/reference/#scatter-marker **infofunc** (pd.Series, None) - hoverinfo for node elements. Indices should correspond to the node element indices **trace_name** (String, "buses") - name of the trace which will appear in the legend **color** (String, "blue") - color of nodes in the trace **cmap** (String, None) - name of a colormap which exists within plotly (Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis) alternatively a custom discrete colormap can be used **cmap_vals** (list, None) - values used for coloring using colormap **cbar_title** (String, None) - title for the colorbar **cmin** (float, None) - colorbar range minimum **cmax** (float, None) - colorbar range maximum **cpos** (float, 1.1) - position of the colorbar **colormap_column** (str, "vm_pu") - set color of bus according to this variable **node_element** (str, "bus") - name of the node element in the net. In a pandapower net, this is alwas "bus" **branch_element** (str, "line") - name of the branch element in the net. In a pandapower net, this is alwas "line" """ color = get_plotly_color(color) node_trace = dict(type='scatter', text=[], mode='markers', hoverinfo='text', name=trace_name, marker=dict(color=color, size=size, symbol=patch_type)) nodes = net[node_element].index.tolist() if nodes is None else list(nodes) node_geodata = node_element + "_geodata" node_plot_index = [ b for b in nodes if b in list(set(nodes) & set(net[node_geodata].index)) ] node_trace['x'], node_trace['y'] = \ (net[node_geodata].loc[node_plot_index, 'x'].tolist(), net[node_geodata].loc[node_plot_index, 'y'].tolist()) if not isinstance(infofunc, pd.Series) and isinstance(infofunc, Iterable) and \ len(infofunc) == len(nodes): infofunc = pd.Series(index=nodes, data=infofunc) node_trace['text'] = net[node_element].loc[node_plot_index, 'name'] if infofunc is None else \ infofunc.loc[nodes] if legendgroup: node_trace['legendgroup'] = legendgroup # if color map is set if cmap is not None: # TODO introduce discrete colormaps (see contour plots in plotly) # if cmap_vals are not given cmap = 'Jet' if cmap is True else cmap if cmap_vals is not None: cmap_vals = cmap_vals else: if net["res_" + branch_element].shape[0] == 0: if branch_element == "line": logger.error( "There are no power flow results for buses voltage magnitudes which are" "default for bus colormap coloring..." "set cmap_vals input argument if you want colormap according to some " "specific values...") else: logger.error( "There are no simulation results which are default for %s colormap coloring..." "set cmap_vals input argument if you want colormap according to some " "specific values..." % node_element) cmap_vals = net["res_" + node_element].loc[node_plot_index, colormap_column].values cmap_vals = net["res_" + node_element].loc[ node_plot_index, colormap_column] if cmap_vals is None else cmap_vals cmin = cmap_vals.min() if cmin is None else cmin cmax = cmap_vals.max() if cmax is None else cmax node_trace['marker'] = Marker(size=size, color=cmap_vals, cmin=cmin, cmax=cmax, colorscale=cmap, colorbar=ColorBar(thickness=10, x=cpos), symbol=patch_type) if cbar_title: node_trace['marker']['colorbar']['title'] = cbar_title node_trace['marker']['colorbar']['title']['side'] = 'right' return [node_trace]