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, cpos=1.0, 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** (pd.Series, None) - hoverinfo for bus elements. Indices should correspond to the pandapower element indices **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 **cpos** (float, 1.1) - position of the colorbar **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) bus_plot_index = [ b for b in buses if b in list(set(buses) & set(net.bus_geodata.index)) ] bus_trace['x'], bus_trace['y'] = (net.bus_geodata.loc[bus_plot_index, 'x'].tolist(), net.bus_geodata.loc[bus_plot_index, 'y'].tolist()) if not isinstance(infofunc, pd.Series) and isinstance(infofunc, Iterable) and \ len(infofunc) == len(buses): infofunc = pd.Series(index=buses, data=infofunc) bus_trace['text'] = net.bus.loc[bus_plot_index, 'name'] if infofunc is None else \ infofunc.loc[buses] 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 = cmap_vals.min() if cmin is None else cmin cmax = cmap_vals.max() if cmax is None else cmax bus_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: bus_trace['marker']['colorbar']['title'] = cbar_title bus_trace['marker']['colorbar']['title']['side'] = 'right' return [bus_trace]
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