def visualize( temporal_networks, frame_dt, time_normalization_factor=1, time_unit=None, titles=None, config=None, port=8226, export_path=None, ): """ Visualize a temporal network or a list of temporal networks interactively. This routine starts up an HTTP server, bins the networks according to the time step ``frame_dt`` and copies them to ``~/.tacoma/web``. Subsequently, a the interaction is started in the standard browser. The visualization is stopped with KeyboardInterrupt. The temporary temporal network files will subsequently be deleted. Parameters ---------- temporal_networks : an instance of :class:`_tacoma.edge_changes`, :class:`_tacoma.edge_lists`, :class:`_tacoma.edge_lists_with_histograms`, :class:`_tacoma.edge_changes_with_histograms` or a list containing those. The temporal networks to visualize. If a list is provided, all networks need to have the same `t0` and `tmax`. frame_dt : float The duration of a frame in the visualization. .. note:: This has to be given in the original time units of the temporal network, disregarding any value of ``time_normalization_factor``. time_normalization_factor : float, default : 1.0 Rescale time with this factor. time_unit : string, default : None, Unit of time of the visualization. titles : string or list of strings, default : None Titles to put on the figures of the corresponding temporal networks. config : dict or str Configuration values for the JavaScript visualization. If this is a string, it can be either ``hs13``, ``dtu``, or ``ht09`` and the appropriate configuration is loaded. port : int, default : 8226 Port of the started HTTP server. export_path : string, default : None path to a directory to which the whole visualization is copied. Use ``os.get_cwd()+'/export_dir/'`` for the current working directory (after ``import os``). .. warning:: No subdirectory will be made for the export. All visualization files will be exported to ``export_path`` directly. Notes ----- The configuration dictionary is filled with values to control the appearance of the visualizations. The standard configuration is .. code:: python config = { "plot_width" : 320 , "network_plot_height" : 250, "edges_plot_height" : 100, "padding" : 10, "start_it" : 0, "node_radius" : 2.5, "link_distance" : 10, "node_charge": -8, "edge_line_width" : 1, "font_size_in_px" : 14, "link_width" : 1, "d3_format_string": ".3f", } """ if not hasattr(temporal_networks, '__len__'): temporal_networks = [temporal_networks] if titles is None: titles = ["" for _ in temporal_networks] elif type(titles) == str or not hasattr(titles, '__len__'): titles = [titles] this_config = copy.deepcopy(standard_config) if isinstance(config, str): if config == 'dtu': config = dict(dtu_config) elif config == 'hs13': config = dict(hs13_config) elif config == 'ht09': config = dict(ht09_config) else: raise ValueError("config", config, "is unknown.") if config is not None: this_config.update(config) # print(titles) # define the server address # server_address = ('127.0.0.1', port) path = '~/.tacoma/web/' web_dir = os.path.abspath(os.path.expanduser(path)) # download d3 if that did not happen yet download_d3() # copy the html and js files for the visualizations prepare_visualization_directory() # create a subfolder based on the current time subdir = "tmp_{:x}".format(int(time.time() * 1000)) mkdirp_customdir(directory=web_dir) subdir_path = os.path.join(web_dir, subdir) mkdirp_customdir(directory=subdir_path) # in case an export is demanded, prepare the export directory if export_path is not None: export_path = os.path.abspath(os.path.expanduser(export_path)) prepare_export_directory(export_path, subdir) # change directory to this directory print("changing directory to", web_dir) print("starting server here ...", web_dir) cwd = os.getcwd() os.chdir(web_dir) server = StoppableHTTPServer( ("127.0.0.1", port), http.server.SimpleHTTPRequestHandler, subdir_path, ) for itn, tn in enumerate(temporal_networks): print("preparing network", titles[itn]) tn_b = _get_prepared_network(tn, frame_dt, time_unit, time_normalization_factor) taco_fname = os.path.join(subdir, subdir + '_' + str(itn) + '.taco') edge_fname = os.path.join(subdir, subdir + '_' + str(itn) + '.json') tc.write_edge_trajectory_coordinates(tn_b, os.path.join(web_dir, edge_fname), filter_for_duration=frame_dt * time_normalization_factor) tc.write_json_taco(tn_b, os.path.join(web_dir, taco_fname)) this_config['temporal_network_files'].append(taco_fname) this_config['edges_coordinate_files'].append(edge_fname) this_config['titles'].append(titles[itn]) with open(os.path.join(web_dir, subdir + '_config.json'), 'w') as f: json.dump(this_config, f) if export_path is not None: copy_tree(subdir_path, os.path.join(export_path, subdir)) with open(os.path.join(export_path, 'default_config.json'), 'w') as f: json.dump(this_config, f) # ========= start server ============ thread = threading.Thread(None, server.run) thread.start() webbrowser.open("http://localhost:" + str(port) + "/?data=" + subdir) try: while True: time.sleep(2) except KeyboardInterrupt: # thread.join() print('stopping server ...') server.stop_this() thread.join() # time.sleep(1) print('changing directory back to', cwd) os.chdir(cwd)
def visualize( network, port=9853, verbose=False, config=None, plot_in_cell_below=True, is_test=False, ): """ Visualize a network interactively using Ulf Aslak's d3 web app. Saves the network as json, saves the passed config and runs a local HTTP server which then runs the web app. Parameters ---------- network : networkx.Graph or networkx.DiGraph or node-link dictionary The network to visualize port : int, default : 9853 The port at which to run the server locally. verbose : bool, default : False Be chatty. config : dict, default : None, In the default configuration, each key-value-pair will be overwritten with the key-value-pair provided in `config`. The default configuration is .. code:: python default_config = { # Input/output 'zoom': 1, # Physics 'node_charge': -45, 'node_gravity': 0.1, 'link_distance': 15, 'link_distance_variation': 0, 'node_collision': True, 'wiggle_nodes': False, 'freeze_nodes': False, # Nodes 'node_fill_color': '#79aaa0', 'node_stroke_color': '#555555', 'node_label_color': '#000000', 'display_node_labels': False, 'scale_node_size_by_strength': False, 'node_size': 5, 'node_stroke_width': 1, 'node_size_variation': 0.5, # Links 'link_color': '#7c7c7c', 'link_width': 2, 'link_alpha': 0.5, 'link_width_variation': 0.5, # Thresholding 'display_singleton_nodes': True, 'min_link_weight_percentile': 0, 'max_link_weight_percentile': 1 } When started from a Jupyter notebook, this will show a reproduced matplotlib figure of the stylized network in a cell below. Only works if ``verbose = False``. is_test : bool, default : False If ``True``, the interactive environment will post its visualization to Python automatically after 5 seconds. Returns ------- network_properties : dict contains all necessary information to redraw the figure which was created in the interactive visualization config : dict contains all configurational values of the interactive visualization """ this_config = deepcopy(default_config) if config is not None: this_config.update(config) path = netwulf_user_folder mkdirp_customdir() web_dir = pathlib.Path(path) # copy the html and js files for the visualizations prepare_visualization_directory() # create a json-file based on the current time file_id = "tmp_{:x}".format(int(time.time() * 1000)) + ".json" filename = file_id configname = "config_" + filename filepath = str(web_dir / filename) configpath = str(web_dir / configname) with open(filepath, 'w') as f: if type(network) in [nx.Graph, nx.DiGraph]: network = nx.node_link_data(network) if 'graph' in network: network.update(network['graph']) del network['graph'] json.dump(network, f, iterable_as_array=True, default=_json_default) with open(configpath, 'w') as f: json.dump(this_config, f, default=_json_default) # change directory to this directory if verbose: print("changing directory to", str(web_dir)) print("starting server here ...", str(web_dir)) cwd = os.getcwd() os.chdir(str(web_dir)) server = NetwulfHTTPServer( ("127.0.0.1", port), NetwulfHTTPRequestHandler, [filepath, configpath], verbose=verbose, ) # ========= start server ============ thread = threading.Thread(None, server.run) thread.start() url = "http://localhost:" + str( port) + "/?data=" + filename + "&config=" + configname if is_test: url += "&pytest" webbrowser.open(url) try: while not server.end_requested: time.sleep(0.1) is_keyboard_interrupted = False except KeyboardInterrupt: is_keyboard_interrupted = True server.end_requested = True if verbose: print('stopping server ...') server.stop_this() thread.join(0.2) posted_network_properties = server.posted_network_properties posted_config = server.posted_config if verbose: print('changing directory back to', cwd) os.chdir(cwd) # see whether or not the whole thing was started from a jupyter notebook and if yes, # actually re-draw the figure and display it env = os.environ try: is_jupyter = 'jupyter' in pathlib.PurePath(env['_']).name except: # this should actually be a key error # apparently this is how it has to be on Windows is_jupyter = 'JPY_PARENT_PID' in env if is_jupyter and plot_in_cell_below and not is_keyboard_interrupted: if verbose: print('recreating layout in matplotlib ...') fig, ax = wulf.draw_netwulf(posted_network_properties) return posted_network_properties, posted_config
def visualize(network, port=9853, config=None): """ Visualize a network interactively using Ulf Aslak's d3 web app. Saves the network as json, saves the passed config and runs a local HTTP server which then runs the web app. Parameters ========== network : networkx.Graph or networkx.DiGraph The network to visualize port : int, default : 9853 The port at which to run the server locally. config : dict, default : None, In the default configuration, each key-value-pair will be overwritten with the key-value-pair provided in `config`. The default configuration is ``` default_config = { 'Apply heat (wiggle)': false, 'Charge strength': -10, 'Center gravity': 0.1, 'Link distance': 10, 'Link width': 2, 'Link alpha': 0.5, 'Node size': 5, 'Node stroke size': 0.5, 'Node size exponent': 0.5, 'Link strength exponent': 0.1, 'Link width exponent': 0.5, 'Collision': False, 'Node fill': '#16a085', 'Node stroke': '#000000', 'Link stroke': '#7c7c7c', 'Label stroke': '#000000', 'Show labels': False, 'Zoom': 1.5, 'Min. link weight %': 0, 'Max. link weight %': 100 ``` """ this_config = deepcopy(default_config) if config is not None: this_config.update(config) path = "~/.netwulf/" mkdirp_customdir() web_dir = os.path.abspath(os.path.expanduser(path)) # copy the html and js files for the visualizations prepare_visualization_directory() # create a json-file based on the current time file_id = "tmp_{:x}".format(int(time.time()*1000)) + ".json" filename = file_id configname = "config_" + filename filepath = os.path.join(web_dir, filename) configpath = os.path.join(web_dir, configname) with open(filepath,'w') as f: json.dump(nx.node_link_data(network), f) with open(configpath,'w') as f: json.dump(this_config, f) # change directory to this directory print("changing directory to", web_dir) print("starting server here ...", web_dir) cwd = os.getcwd() os.chdir(web_dir) server = StoppableHTTPServer(("127.0.0.1", port), http.server.SimpleHTTPRequestHandler, [filepath, configpath], ) # ========= start server ============ thread = threading.Thread(None, server.run) thread.start() webbrowser.open("http://localhost:"+str(port)+"/?data=" + filename + "&config=" + configname) try: while True: time.sleep(2) except KeyboardInterrupt: print('stopping server ...') server.stop_this() thread.join() print('changing directory back to', cwd) os.chdir(cwd)