d3graph 2.7.0__tar.gz → 2.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: d3graph
3
- Version: 2.7.0
3
+ Version: 2.7.2
4
4
  Summary: Python package to create interactive network based on d3js.
5
5
  Author-email: Erdogan Taskesen <erdogant@gmail.com>
6
6
  License-Expression: BSD-3-Clause
@@ -24,7 +24,7 @@ Requires-Dist: networkx>2
24
24
  Requires-Dist: ismember
25
25
  Requires-Dist: jinja2
26
26
  Requires-Dist: packaging
27
- Requires-Dist: markupsafe==2.0.1
27
+ Requires-Dist: markupsafe
28
28
  Requires-Dist: python-louvain
29
29
  Requires-Dist: datazets
30
30
  Dynamic: license-file
@@ -11,11 +11,13 @@ from d3graph.d3graph import (
11
11
  adjmat2dict,
12
12
  data_checks,
13
13
  check_logger,
14
+ get_hex_color,
15
+ import_example,
14
16
  )
15
17
 
16
18
  __author__ = 'Erdogan Tasksen'
17
19
  __email__ = 'erdogant@gmail.com'
18
- __version__ = '2.7.0'
20
+ __version__ = '2.7.2'
19
21
 
20
22
  # Setup root logger
21
23
  _logger = logging.getLogger('d3graph')
@@ -39,13 +41,13 @@ The ouput is a html file that is interactive and stand alone.
39
41
 
40
42
  Examples
41
43
  --------
42
- >>> from d3graph import d3graph, vec2adjmat
44
+ >>> from d3graph import d3graph, vec2adjmat, import_example
43
45
  >>>
44
46
  >>> # Initialize
45
47
  >>> d3 = d3graph()
46
48
  >>>
47
49
  >>> # Load karate example
48
- >>> df = d3.import_example('energy')
50
+ >>> df = import_example('energy')
49
51
  >>> adjmat = vec2adjmat(source=df['source'], target=df['target'], weight=df['weight'])
50
52
  >>>
51
53
  >>> # Initialize
@@ -21,6 +21,7 @@ import colourmap as cm
21
21
  import networkx as nx
22
22
  import numpy as np
23
23
  import pandas as pd
24
+ from pandas.arrays import StringArray
24
25
  from ismember import ismember
25
26
  from jinja2 import Environment, PackageLoader
26
27
  from packaging import version
@@ -192,7 +193,8 @@ class d3graph:
192
193
  if sticky is not None:
193
194
  self.config['sticky'] = sticky
194
195
  # if self.config.get('filepath', None) != 'd3graph.html':
195
- self.config['filepath'] = self.set_path(filepath)
196
+ if filepath is not None or self.config.get('filepath') is None:
197
+ self.set_path(filepath)
196
198
 
197
199
  # Create dataframe from co-occurrence matrix
198
200
  self.G = make_graph(self.node_properties, self.edge_properties)
@@ -480,13 +482,13 @@ class d3graph:
480
482
  nodecount = self.adjmat.shape[0]
481
483
  group = np.zeros_like(node_names).astype(int)
482
484
  # Check validity of color.
483
- _check_hex_color(color, nodecount)
485
+ color = _check_hex_color(color, nodecount, cmap=cmap)
484
486
  # Store in config
485
487
  self.config['cmap'] = 'Paired' if cmap is None else cmap
486
488
  self.config['node_scaler'] = scaler
487
489
 
488
490
  # ############ Set node label #############
489
- if isinstance(label, list):
491
+ if isinstance(label, (list, np.ndarray, pd.Series, pd.Series, StringArray)):
490
492
  label = np.array(label).astype(str)
491
493
  elif 'numpy' in str(type(label)):
492
494
  pass
@@ -499,7 +501,7 @@ class d3graph:
499
501
  if len(label) != nodecount: raise ValueError("[label] must be of same length as the number of nodes")
500
502
 
501
503
  # ############ tooltip text #############
502
- if isinstance(tooltip, list):
504
+ if isinstance(tooltip, (list, np.ndarray, pd.Series, StringArray)):
503
505
  tooltip = np.array(tooltip).astype(str)
504
506
  elif 'numpy' in str(type(tooltip)):
505
507
  pass
@@ -512,7 +514,7 @@ class d3graph:
512
514
  if len(tooltip) != nodecount: raise ValueError("[tooltip text] must be of same length as the number of nodes")
513
515
 
514
516
  # ############ Set node color #############
515
- if isinstance(color, list) and len(color) == nodecount:
517
+ if isinstance(color, (list, np.ndarray, pd.Series, StringArray)) and len(color) == nodecount:
516
518
  color = np.array(color)
517
519
  elif 'numpy' in str(type(color)):
518
520
  color = _get_hexcolor(color, cmap=self.config['cmap'])
@@ -542,7 +544,7 @@ class d3graph:
542
544
  fontsize = _set_node_fontsize(self, fontsize, nodecount)
543
545
 
544
546
  # ########## Set node color edge #############
545
- if isinstance(edge_color, list):
547
+ if isinstance(edge_color, (list, np.ndarray, pd.Series, StringArray)):
546
548
  edge_color = np.array(edge_color)
547
549
  elif 'numpy' in str(type(edge_color)):
548
550
  pass
@@ -574,7 +576,7 @@ class d3graph:
574
576
  marker = _set_marker(self, marker, nodecount)
575
577
 
576
578
  # ############ Set node edge size #############
577
- if isinstance(edge_size, list):
579
+ if isinstance(edge_size, (list, np.ndarray, pd.Series, StringArray)):
578
580
  edge_size = np.array(edge_size)
579
581
  elif 'numpy' in str(type(edge_size)):
580
582
  pass
@@ -597,16 +599,16 @@ class d3graph:
597
599
  'marker': marker[i],
598
600
  'label': label[i],
599
601
  'tooltip': tooltip[i],
600
- 'color': color[i].astype(str),
601
- 'opacity': opacity[i].astype(str),
602
- 'fontcolor': fontcolor[i].astype(str),
603
- 'fontsize': fontsize[i].astype(int),
602
+ 'color': str(color[i]),
603
+ 'opacity': str(opacity[i]),
604
+ 'fontcolor': str(fontcolor[i]),
605
+ 'fontsize': str(fontsize[i]),
604
606
  'size': size[i],
605
607
  'edge_size': edge_size[i],
606
608
  'edge_color': edge_color[i],
607
609
  'group': group[i]}
608
610
 
609
- logger.info('Number of unique nodes: %.0d', len(self.node_properties.keys()))
611
+ logger.info(f'Number of unique nodes: {len(self.node_properties.keys())}')
610
612
 
611
613
  # compute clusters
612
614
  def get_cluster_color(self, node_names: list = None, color: str = '#000080') -> tuple:
@@ -713,13 +715,13 @@ class d3graph:
713
715
 
714
716
  Examples
715
717
  --------
716
- >>> from d3graph import d3graph
718
+ >>> from d3graph import d3graph, import_example
717
719
  >>>
718
720
  >>> # Initialize
719
721
  >>> d3 = d3graph()
720
722
  >>>
721
723
  >>> # Load karate example
722
- >>> adjmat, df = d3.import_example('karate')
724
+ >>> adjmat, df = import_example('karate')
723
725
  >>>
724
726
  >>> # Initialize
725
727
  >>> d3.graph(adjmat)
@@ -863,7 +865,8 @@ class d3graph:
863
865
  os.makedirs(dirname, exist_ok=True)
864
866
  filepath = os.path.abspath(os.path.join(dirname, filename))
865
867
  logger.debug(f'filepath is set to [{filepath}]')
866
- return Path(filepath)
868
+ # Set to config
869
+ self.config['filepath'] = Path(filepath)
867
870
 
868
871
  def import_example(self, data='energy', url=None, sep=','):
869
872
  """Import example dataset from github source.
@@ -887,38 +890,7 @@ class d3graph:
887
890
  * https://github.com/erdogant/datazets
888
891
 
889
892
  """
890
- if data == 'small':
891
- source = ['node A', 'node F', 'node B', 'node B', 'node B', 'node A', 'node C', 'node Z']
892
- target = ['node F', 'node B', 'node J', 'node F', 'node F', 'node M', 'node M', 'node A']
893
- weight = [5.56, 0.5, 0.64, 0.23, 0.9, 3.28, 0.5, 0.45]
894
- adjmat = vec2adjmat(source, target, weight=weight)
895
- return adjmat, None
896
- elif data == 'bigbang':
897
- df = dz.get(data=data)
898
- adjmat = vec2adjmat(df['source'], df['target'], weight=df['weight'])
899
- return adjmat
900
- elif data == 'karate':
901
- import scipy
902
- if version.parse(scipy.__version__) < version.parse('1.8.0'):
903
- raise ImportError(
904
- '[d3graph] >Error: This release requires scipy version >= 1.8.0. Try: pip install -U scipy>=1.8.0')
905
-
906
- G = nx.karate_club_graph()
907
- adjmat = nx.adjacency_matrix(G).todense()
908
- adjmat = pd.DataFrame(index=range(adjmat.shape[0]), data=adjmat, columns=range(adjmat.shape[0]))
909
- adjmat.columns = adjmat.columns.astype(str)
910
- adjmat.index = adjmat.index.astype(str)
911
- adjmat.iloc[3, 4] = 5
912
- adjmat.iloc[4, 5] = 6
913
- adjmat.iloc[5, 6] = 7
914
-
915
- df = pd.DataFrame(index=adjmat.index)
916
- df['degree'] = np.array([*G.degree()])[:, 1]
917
- df['label'] = [G.nodes[i]['club'] for i in range(len(G.nodes))]
918
-
919
- return adjmat, df
920
- else:
921
- return dz.get(data=data, url=url, sep=sep)
893
+ return import_example(data=data, url=url, sep=sep)
922
894
 
923
895
 
924
896
  # %%
@@ -1427,6 +1399,8 @@ def _get_hexcolor(label, cmap: str = 'Paired'):
1427
1399
 
1428
1400
  return label
1429
1401
 
1402
+ def get_hex_color(labels, cmap='Set1', opaque_type='per_class', gradient=None):
1403
+ return cm.fromlist(labels, scheme='hex', opaque_type=opaque_type, gradient=gradient)
1430
1404
 
1431
1405
  # %% Do checks
1432
1406
  def library_compatibility_checks() -> None:
@@ -1623,15 +1597,21 @@ def adjmat2vec(adjmat, min_weight: float = 1.0) -> pd.DataFrame:
1623
1597
  return adjmat
1624
1598
 
1625
1599
 
1626
- def _check_hex_color(color, n=None):
1627
- if isinstance(color, str) and len(color) != 7: raise ValueError(
1628
- 'Input parameter [color] has wrong format. Must be like color="#000000"')
1629
- if isinstance(color, list) and len(color) == 0: raise ValueError(
1630
- 'Input parameter [color] has wrong format and length. Must be like: color=["#000000", "...", "#000000"]')
1631
- if isinstance(color, list) and (not np.all(list(map(lambda x: len(x) == 7, color)))): raise ValueError(
1632
- '[color] contains incorrect length of hex-color! Hex must be of length 7: ["#000000", "#000000", etc]')
1633
- if (n is not None) and isinstance(color, list) and len(color) != n:
1634
- raise ValueError(f'Input parameter [color] has wrong length. Must be of length: {str(n)}')
1600
+ def _check_hex_color(color, n=None, cmap='Set1'):
1601
+ if isinstance(color, str) and len(color) != 7:
1602
+ logger.warning('Input parameter [color] has wrong format. Must be like color="#000000" <auto-fixing>')
1603
+ return get_hex_color(color, cmap=cmap)[0]
1604
+ if isinstance(color, (list, np.ndarray, pd.Series, pd.Series, StringArray)) and len(color) == 0:
1605
+ logger.warning('Input parameter [color] has wrong format and length. Must be like: color=["#000000", "...", "#000000"] <auto-fixing>')
1606
+ return get_hex_color(color, cmap=cmap)[0]
1607
+ if isinstance(color, (list, np.ndarray, pd.Series, pd.Series, StringArray)) and (not np.all(list(map(lambda x: len(x) == 7, color)))):
1608
+ logger.warning('[color] contains incorrect hex-colors. Hex must be of length 7: ["#000000", "#000000", etc] <auto-fixing>')
1609
+ return get_hex_color(color, cmap=cmap)[0]
1610
+ if (n is not None) and isinstance(color, (list, np.ndarray, pd.Series, pd.Series, StringArray)) and len(color) != n:
1611
+ logger.warning(f'Input parameter [color] has wrong length. Must be of length: {str(n)} <auto-fixing>')
1612
+ return get_hex_color(color, cmap=cmap)[0]
1613
+ # Return original input
1614
+ return color
1635
1615
 
1636
1616
 
1637
1617
  def _set_opacity(self, opacity, nodecount, node_names):
@@ -1755,6 +1735,61 @@ def _set_node_fontcolor(self, fontcolor, color, node_names, nodecount):
1755
1735
  # return
1756
1736
  return fontcolor
1757
1737
 
1738
+ def import_example(data='energy', url=None, sep=','):
1739
+ """Import example dataset from github source.
1740
+
1741
+ Import one of the few datasets from github source or specify your own download url link.
1742
+
1743
+ Parameters
1744
+ ----------
1745
+ data : str
1746
+ Name of datasets: 'sprinkler', 'titanic', 'student', 'fifa', 'cancer', 'waterpump', 'retail'
1747
+ url : str
1748
+ url link to to dataset.
1749
+
1750
+ Returns
1751
+ -------
1752
+ pd.DataFrame()
1753
+ Dataset containing mixed features.
1754
+
1755
+ References
1756
+ ----------
1757
+ * https://github.com/erdogant/datazets
1758
+
1759
+ """
1760
+ if data == 'small':
1761
+ source = ['node A', 'node F', 'node B', 'node B', 'node B', 'node A', 'node C', 'node Z']
1762
+ target = ['node F', 'node B', 'node J', 'node F', 'node F', 'node M', 'node M', 'node A']
1763
+ weight = [5.56, 0.5, 0.64, 0.23, 0.9, 3.28, 0.5, 0.45]
1764
+ adjmat = vec2adjmat(source, target, weight=weight)
1765
+ return adjmat, None
1766
+ elif data == 'bigbang':
1767
+ df = dz.get(data=data)
1768
+ adjmat = vec2adjmat(df['source'], df['target'], weight=df['weight'])
1769
+ return adjmat
1770
+ elif data == 'karate':
1771
+ import scipy
1772
+ if version.parse(scipy.__version__) < version.parse('1.8.0'):
1773
+ raise ImportError(
1774
+ '[d3graph] >Error: This release requires scipy version >= 1.8.0. Try: pip install -U scipy>=1.8.0')
1775
+
1776
+ G = nx.karate_club_graph()
1777
+ adjmat = nx.adjacency_matrix(G).todense()
1778
+ adjmat = pd.DataFrame(index=range(adjmat.shape[0]), data=adjmat, columns=range(adjmat.shape[0]))
1779
+ adjmat.columns = adjmat.columns.astype(str)
1780
+ adjmat.index = adjmat.index.astype(str)
1781
+ adjmat.iloc[3, 4] = 5
1782
+ adjmat.iloc[4, 5] = 6
1783
+ adjmat.iloc[5, 6] = 7
1784
+
1785
+ df = pd.DataFrame(index=adjmat.index)
1786
+ df['degree'] = np.array([*G.degree()])[:, 1]
1787
+ df['label'] = [G.nodes[i]['club'] for i in range(len(G.nodes))]
1788
+
1789
+ return adjmat, df
1790
+ else:
1791
+ return dz.get(data=data, url=url, sep=sep)
1792
+
1758
1793
 
1759
1794
  def get_support(support):
1760
1795
  """Support."""
@@ -0,0 +1,361 @@
1
+ function d3graphscript(config = {
2
+ // Default values
3
+ width: 800,
4
+ height: 600,
5
+ charge: -250,
6
+ distance: 0,
7
+ directed: false,
8
+ collision: 0.5,
9
+ link_tension: 1,
10
+ sticky: false,
11
+ background_color: '#FFFFFF'
12
+ }) {
13
+
14
+ //Constants for the SVG
15
+ var width = config.width;
16
+ var height = config.height;
17
+ var background_color = config.background_color || '#FFFFFF';
18
+ var sticky = config.sticky || false;
19
+
20
+ // Set the body background color
21
+ document.body.style.backgroundColor = background_color;
22
+
23
+ //Set up the colour scale
24
+ var color = d3.scale.category20();
25
+
26
+ var force = d3.layout.force()
27
+ .charge(config.charge)
28
+ .linkDistance((d) => d.edge_distance || config.distance)
29
+ //.linkDistance((d) => config.distance > 0 ? config.distance : d.edge_weight)
30
+ .linkStrength(config.link_tension !== undefined ? config.link_tension : 1)
31
+ .size([width, height]);
32
+
33
+ // ---- DRAGGING ----
34
+ // Sticky mode: dragstart fixes the node so the simulation stops pulling it.
35
+ // dragend keeps it pinned (dashed stroke indicator).
36
+ // Right-click a pinned node to release it back into the simulation.
37
+ // Normal mode: standard free-drag behaviour is preserved.
38
+
39
+ function dragstarted(d) {
40
+ d3.event.sourceEvent.stopPropagation();
41
+ d3.select(this).classed("dragging", true);
42
+ if (sticky) {
43
+ d.fixed = true;
44
+ force.start();
45
+ }
46
+ }
47
+
48
+ function dragged(d) {
49
+ if (sticky) {
50
+ d.x = d.px = d3.event.x;
51
+ d.y = d.py = d3.event.y;
52
+ } else {
53
+ d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
54
+ }
55
+ }
56
+
57
+ function dragended(d) {
58
+ d3.select(this).classed("dragging", false);
59
+ if (sticky) {
60
+ // Keep the node fixed and apply a visual "pinned" cue (dashed border)
61
+ d.fixed = true;
62
+ d3.select(this).select("circle")
63
+ .style("stroke-dasharray", "4,2")
64
+ .style("stroke-width", function(d) { return Math.max(parseFloat(d.node_size_edge) || 1, 2); });
65
+ }
66
+ }
67
+
68
+ var drag = force.drag()
69
+ .origin(function(d) { return d; })
70
+ .on("dragstart", dragstarted)
71
+ .on("drag", dragged)
72
+ .on("dragend", dragended);
73
+
74
+ // ---- END DRAGGING ----
75
+
76
+ //Append a SVG to the body of the html page. Assign this SVG as an object to svg
77
+ var svg = d3.select("body").append("svg")
78
+ .attr("width", width)
79
+ .attr("height", height)
80
+ .style("background-color", background_color)
81
+ .call(d3.behavior.zoom().on("zoom", function () { svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")") }))
82
+ .on("dblclick.zoom", null)
83
+ .append("g")
84
+
85
+ graphRec = JSON.parse(JSON.stringify(graph));
86
+
87
+ //Creates the graph data structure out of the json data
88
+ force.nodes(graph.nodes)
89
+ .links(graph.links)
90
+ .start();
91
+
92
+ // Create all the line svgs but without locations yet
93
+ var link = svg.selectAll(".link")
94
+ .data(graph.links)
95
+ .enter().append("line")
96
+ .attr("class", "link")
97
+ .attr('marker-start', function(d){ return 'url(#marker_' + d.marker_start + ')' })
98
+ .attr("marker-end", function(d) {
99
+ if (config.directed) {return 'url(#marker_' + d.marker_end + ')' }})
100
+ .style("stroke-width", function(d) {return d.edge_width;}) // LINK-WIDTH
101
+ .style("stroke", function(d) {return d.edge_color;}) // EDGE-COLORS
102
+ .style("stroke-dasharray", function(d) {return d.edge_style;}) // EDGE-STYLE
103
+ .style("opacity", function(d) {return d.edge_opacity;}) // EDGE-OPACITY
104
+ ;
105
+
106
+ link.append("title").text(function(d) { return d.tooltip; });
107
+
108
+ // ADD TEXT ON THE EDGES (PART 1/2)
109
+ var linkText = svg.selectAll(".link-text")
110
+ .data(graph.links)
111
+ .enter().append("text")
112
+ .attr("class", "link-text")
113
+ .attr("font-size", function(d) {return d.label_fontsize + "px";})
114
+ .style("fill", function(d) {return d.label_color;})
115
+ .style("font-family", "Arial")
116
+ .text(function(d) { return d.label; });
117
+
118
+ //Do the same with the circles for the nodes
119
+ var node = svg.selectAll(".node")
120
+ .data(graph.nodes)
121
+ .enter().append("g")
122
+ .attr("class", "node")
123
+ .call(drag)
124
+ .on('dblclick', connectedNodes); // HIGHLIGHT ON/OFF
125
+
126
+ // Right-click handler: release a pinned node back into the simulation (sticky mode only)
127
+ if (sticky) {
128
+ node.on('contextmenu', function(d) {
129
+ d3.event.preventDefault();
130
+ d.fixed = false;
131
+ // Remove pinned visual cue
132
+ d3.select(this).select("circle")
133
+ .style("stroke-dasharray", null)
134
+ .style("stroke-width", function(d) { return d.node_size_edge; });
135
+ force.resume();
136
+ });
137
+ }
138
+
139
+ {{ CLICK_COMMENT }} node.on('click', color_on_click); // ON CLICK HANDLER
140
+
141
+
142
+ node.append("circle")
143
+ .attr("r", function(d) { return d.node_size; }) // NODE SIZE
144
+ .style("fill", function(d) {return d.node_color;}) // NODE-COLOR
145
+ .style("opacity", function(d) {return d.node_opacity;}) // NODE-OPACITY
146
+ .style("stroke-width", function(d) {return d.node_size_edge;}) // NODE-EDGE-SIZE
147
+ .style("stroke", function(d) {return d.node_color_edge;}) // NODE-COLOR-EDGE
148
+
149
+ // Text in nodes
150
+ node.append("text")
151
+ .attr("dx", 10)
152
+ .attr("dy", ".35em")
153
+ .text(function(d) {return d.node_name}) // NODE-TEXT
154
+ .style("font-size", function(d) {return d.node_fontsize + "px";}) // NODE FONT SIZE
155
+ .style("fill", function(d) {return d.node_fontcolor;}) // NODE FONT COLOR
156
+ .style("font-family", "monospace");
157
+
158
+ let showInHover = ["node_tooltip"]; // Tooltip
159
+ node.append("title")
160
+ .text((d) => Object.keys(d)
161
+ .filter((key) => showInHover.indexOf(key) !== -1)
162
+ .map((key) => `${d[key]}`)
163
+ .join('\n')
164
+ )
165
+
166
+ //Now we are giving the SVGs co-ordinates - the force layout is generating the co-ordinates which this code is using to update the attributes of the SVG elements
167
+ force.on("tick", function() {
168
+ link.attr("x1", function(d) { return d.source.x; })
169
+ .attr("y1", function(d) { return d.source.y; })
170
+ .attr("x2", function(d) { return d.target.x; })
171
+ .attr("y2", function(d) { return d.target.y; });
172
+ d3.selectAll("circle").attr("cx", function(d) { return d.x; })
173
+ .attr("cy", function(d) { return d.y; });
174
+ d3.selectAll("text").attr("x", function(d) { return d.x; })
175
+ .attr("y", function(d) { return d.y; })
176
+ linkText.attr("x", function(d) { return (d.source.x + d.target.x) / 2; }) // ADD TEXT ON THE EDGES (PART 2/2)
177
+ .attr("y", function(d) { return (d.source.y + d.target.y) / 2; })
178
+ .attr("text-anchor", "middle");
179
+
180
+ node.each(collide(config.collision)); //COLLISION DETECTION. High means a big fight to get untouchable nodes (default=0.5)
181
+
182
+ });
183
+
184
+ // --------- MARKER FOR EDGE ENDINGS -----------
185
+
186
+ var data_marker = [
187
+ { id: 0, name: 'circle', path: 'M 0, 0 m -5, 0 a 5,5 0 1,0 10,0 a 5,5 0 1,0 -10,0', viewbox: '-6 -6 12 12' }
188
+ , { id: 1, name: 'square', path: 'M 0,0 m -5,-5 L 5,-5 L 5,5 L -5,5 Z', viewbox: '-5 -5 10 10' }
189
+ , { id: 2, name: 'arrow', path: 'M 0,0 m -5,-5 L 5,0 L -5,5 Z', viewbox: '-5 -5 10 10' }
190
+ , { id: 3, name: 'stub', path: 'M 0,0 m -1,-5 L 1,-5 L 1,5 L -1,5 Z', viewbox: '-1 -5 2 10' }
191
+ ]
192
+
193
+ svg.append("defs").selectAll("marker")
194
+ .data(data_marker)
195
+ .enter()
196
+ .append('svg:marker')
197
+ .attr('id', function(d){ return 'marker_' + d.name})
198
+ .attr('markerHeight', 10)
199
+ .attr('markerWidth', 10)
200
+ .attr("markerUnits", "userSpaceOnUse") // Fix marker width
201
+ .attr('orient', 'auto')
202
+ .attr('refX', 15) // Offset marker-end
203
+ .attr('refY', 0)
204
+ .attr('viewBox', function(d){ return d.viewbox })
205
+ .append('svg:path')
206
+ .attr('d', function(d){ return d.path }) // Marker type
207
+ .style("fill", '#808080') // Marker color
208
+ .style("stroke", '#808080') // Marker edge-color
209
+ .style("opacity", 0.95) // Marker opacity
210
+ .style("stroke-width", 1); // Marker edge thickness
211
+
212
+ // --------- END MARKER -----------
213
+
214
+
215
+ // collision detection
216
+
217
+ var padding = 1, // separation between circles
218
+ radius = 8;
219
+
220
+ function collide(alpha) {
221
+ var quadtree = d3.geom.quadtree(graph.nodes);
222
+ return function(d) {
223
+ var rb = 2 * radius + padding,
224
+ nx1 = d.x - rb,
225
+ nx2 = d.x + rb,
226
+ ny1 = d.y - rb,
227
+ ny2 = d.y + rb;
228
+ quadtree.visit(function(quad, x1, y1, x2, y2) {
229
+ if (quad.point && (quad.point !== d)) {
230
+ var x = d.x - quad.point.x,
231
+ y = d.y - quad.point.y,
232
+ l = Math.sqrt(x * x + y * y);
233
+ if (l < rb) {
234
+ l = (l - rb) / l * alpha;
235
+ d.x -= x *= l;
236
+ d.y -= y *= l;
237
+ quad.point.x += x;
238
+ quad.point.y += y;
239
+ }
240
+ }
241
+ return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
242
+ });
243
+ };
244
+ }
245
+ // collision detection end
246
+
247
+
248
+ //Toggle stores whether the highlighting is on
249
+ var toggle = 0;
250
+ //Create an array logging what is connected to what
251
+ var linkedByIndex = {};
252
+ for (i = 0; i < graph.nodes.length; i++) {
253
+ linkedByIndex[i + "," + i] = 1;
254
+ };
255
+ graph.links.forEach(function(d) {
256
+ linkedByIndex[d.source.index + "," + d.target.index] = 1;
257
+ });
258
+ //This function looks up whether a pair are neighbours
259
+ function neighboring(a, b) {
260
+ return linkedByIndex[a.index + "," + b.index];
261
+ }
262
+
263
+
264
+ // COLOR ON CLICK
265
+ function color_on_click() {
266
+ // Give the original color back to all nodes
267
+ d3.selectAll(".node")
268
+ .select("circle")
269
+ .style("fill", function(d) {return d.node_color;})
270
+ .style("opacity", function(d) {return d.node_opacity;})
271
+ .style("stroke", function(d) {return d.node_color_edge;})
272
+ .style("stroke-width", function(d) {return d.node_size_edge;})
273
+ // Restore pinned cue on still-fixed nodes
274
+ .style("stroke-dasharray", function(d) { return (sticky && d.fixed) ? "4,2" : null; })
275
+ .attr("r", function(d) { return d.node_size; })
276
+ ;
277
+
278
+ // Set the color on the clicked node
279
+ d3.select(this).select("circle")
280
+ .style("fill", {{ CLICK_FILL }})
281
+ .style("stroke", "{{ CLICK_STROKE }}")
282
+ .style("stroke-width", {{ CLICK_STROKEW }})
283
+ .attr("r", function(d) { return d.node_size*{{ CLICK_SIZE }}; })
284
+ ;}
285
+
286
+
287
+
288
+ function connectedNodes() {
289
+ if (toggle == 0) {
290
+ //Reduce the opacity of all but the neighbouring nodes
291
+ d = d3.select(this).node().__data__;
292
+ node.style("opacity", function(o) {
293
+ return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
294
+ });
295
+ link.style("opacity", function(o) {
296
+ return d.index == o.source.index | d.index == o.target.index ? 1 : 0.1;
297
+ });
298
+ toggle = 1;
299
+ } else {
300
+ //Put them back to opacity=1
301
+ node.style("opacity", 0.95);
302
+ link.style("opacity", 1);
303
+
304
+ toggle = 0;
305
+ }
306
+ }
307
+
308
+
309
+ //adjust threshold
310
+ function threshold() {
311
+ let thresh = this.value;
312
+
313
+ graph.links.splice(0, graph.links.length);
314
+ linkText = linkText.data([]); // CLEAR EDGE-LABELS: Clear the linkText selection
315
+ linkText.exit().remove(); // CLEAR EDGE-LABELS: Clear the linkText elements from the DOM
316
+
317
+ for (var i = 0; i < graphRec.links.length; i++) {
318
+ if (graphRec.links[i].edge_weight > thresh) {
319
+ graph.links.push(graphRec.links[i]);
320
+ }
321
+ }
322
+ restart();
323
+ }
324
+
325
+ // Set the initial value of the slider to the user-defined threshold
326
+ document.getElementById('thresholdSlider').value = {{ SET_SLIDER }};
327
+ // Call the threshold function to set the network state
328
+ threshold.call(document.getElementById('thresholdSlider'));
329
+
330
+ d3.select("#thresholdSlider").on("change", threshold);
331
+
332
+ //Restart the visualisation after any node and link changes
333
+ function restart() {
334
+
335
+ // Update EDGE-LINKS
336
+ link = link.data(graph.links);
337
+ link.exit().remove();
338
+ link.enter().insert("line", ".node").attr("class", "link");
339
+ link.style("stroke-width", function(d) {return d.edge_width;}); // LINK-WIDTH AFTER BREAKING WITH SLIDER
340
+ link.style("marker-end", function(d) { // Include the markers.
341
+ if (config.directed) {return 'url(#marker_' + d.marker_end + ')' }})
342
+ link.style("stroke", function(d) {return d.edge_color;}); // EDGE-COLOR AFTER BREAKING WITH SLIDER
343
+ link.style("stroke-dasharray", function(d) {return d.edge_style;}) // EDGE-STYLE
344
+ link.style("opacity", function(d) {return d.edge_opacity;}); // EDGE-OPACITY AFTER BREAKING WITH SLIDER
345
+
346
+ // Update EDGE-LABELS
347
+ linkText = linkText.data(graph.links);
348
+ linkText.exit().remove();
349
+ linkText.enter().append("text")
350
+ .attr("class", "link-text")
351
+ .attr("font-size", function(d) {return d.label_fontsize + "px";})
352
+ .style("fill", function(d) {return d.label_color;})
353
+ .style("font-family", "Arial")
354
+ .text(function(d) { return d.label; });
355
+
356
+ node = node.data(graph.nodes);
357
+ node.enter().insert("circle", ".cursor").attr("class", "node").attr("r", 5).call(force.drag);
358
+ force.start();
359
+ }
360
+
361
+ }
@@ -1,3 +1,57 @@
1
+ from d3graph import d3graph, import_example, get_hex_color
2
+
3
+ # Initialize
4
+ d3 = d3graph()
5
+
6
+ # Load karate example
7
+ adjmat, df = import_example('karate')
8
+
9
+ d3.graph(adjmat)
10
+
11
+ # Node properties
12
+ d3.set_node_properties(label=df['label'].values, color=df['label'].values, size=df['degree'].values, edge_size=df['degree'].values, cmap='Set1')
13
+
14
+ colors, labels = get_hex_color(df['label'].values)
15
+ colors=df['label'].values
16
+ d3.set_node_properties(label=df['label'].values, color=colors, size=df['degree'].values, edge_size=df['degree'].values, cmap='Set1')
17
+
18
+ # Edge properties
19
+ d3.set_edge_properties(directed=True)
20
+
21
+ # Plot
22
+ d3.show()
23
+
24
+
25
+ # %% SET PATH ISSUE https://github.com/erdogant/d3graph/issues/42
26
+ from pathlib import Path
27
+ from d3graph import d3graph, vec2adjmat, import_example
28
+ output_path = Path.cwd() / 'd3graph.html'
29
+
30
+ # Load example data
31
+ df = import_example('stormofswords')
32
+ adjmat = vec2adjmat(source=df['source'], target=df['target'], weight=df['weight'])
33
+
34
+
35
+ # sticky=True (default) — drag to pin, right-click to release
36
+ d3 = d3graph(sticky=True)
37
+ d3.set_path(output_path)
38
+ d3.config['filepath']
39
+
40
+ d3.graph(adjmat)
41
+ d3.show()
42
+
43
+ # sticky=False — classic spring-back behaviour
44
+ d3 = d3graph(adjmat, sticky=False)
45
+ d3.show()
46
+
47
+ # %%
48
+
49
+
50
+
51
+
52
+ # %%
53
+
54
+
1
55
  # Import library
2
56
  from d3graph import d3graph, vec2adjmat
3
57
 
@@ -464,6 +518,7 @@ d3.set_node_properties(color=adjmat.columns.values, size=[10, 20, 10, 10, 15, 10
464
518
  d3.node_properties['Penny']['tooltip']='test\ntest2'
465
519
  d3.show(filepath='c:\\temp\\network3.html')
466
520
 
521
+
467
522
  # %% Checks with cluster label
468
523
  from d3graph import d3graph
469
524
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: d3graph
3
- Version: 2.7.0
3
+ Version: 2.7.2
4
4
  Summary: Python package to create interactive network based on d3js.
5
5
  Author-email: Erdogan Taskesen <erdogant@gmail.com>
6
6
  License-Expression: BSD-3-Clause
@@ -24,7 +24,7 @@ Requires-Dist: networkx>2
24
24
  Requires-Dist: ismember
25
25
  Requires-Dist: jinja2
26
26
  Requires-Dist: packaging
27
- Requires-Dist: markupsafe==2.0.1
27
+ Requires-Dist: markupsafe
28
28
  Requires-Dist: python-louvain
29
29
  Requires-Dist: datazets
30
30
  Dynamic: license-file
@@ -5,6 +5,6 @@ networkx>2
5
5
  ismember
6
6
  jinja2
7
7
  packaging
8
- markupsafe==2.0.1
8
+ markupsafe
9
9
  python-louvain
10
10
  datazets
@@ -41,7 +41,7 @@ dependencies = [
41
41
  'ismember',
42
42
  'jinja2',
43
43
  'packaging',
44
- 'markupsafe==2.0.1',
44
+ 'markupsafe',
45
45
  'python-louvain',
46
46
  'datazets',
47
47
  ]
@@ -1,361 +0,0 @@
1
- function d3graphscript(config = {
2
- // Default values
3
- width: 800,
4
- height: 600,
5
- charge: -250,
6
- distance: 0,
7
- directed: false,
8
- collision: 0.5,
9
- link_tension: 1,
10
- sticky: false,
11
- background_color: '#FFFFFF'
12
- }) {
13
-
14
- //Constants for the SVG
15
- var width = config.width;
16
- var height = config.height;
17
- var background_color = config.background_color || '#FFFFFF';
18
- var sticky = config.sticky || false;
19
-
20
- // Set the body background color
21
- document.body.style.backgroundColor = background_color;
22
-
23
- //Set up the colour scale
24
- var color = d3.scale.category20();
25
-
26
- var force = d3.layout.force()
27
- .charge(config.charge)
28
- .linkDistance((d) => d.edge_distance || config.distance)
29
- //.linkDistance((d) => config.distance > 0 ? config.distance : d.edge_weight)
30
- .linkStrength(config.link_tension !== undefined ? config.link_tension : 1)
31
- .size([width, height]);
32
-
33
- // ---- DRAGGING ----
34
- // Sticky mode: dragstart fixes the node so the simulation stops pulling it.
35
- // dragend keeps it pinned (dashed stroke indicator).
36
- // Right-click a pinned node to release it back into the simulation.
37
- // Normal mode: standard free-drag behaviour is preserved.
38
-
39
- function dragstarted(d) {
40
- d3.event.sourceEvent.stopPropagation();
41
- d3.select(this).classed("dragging", true);
42
- if (sticky) {
43
- d.fixed = true;
44
- force.start();
45
- }
46
- }
47
-
48
- function dragged(d) {
49
- if (sticky) {
50
- d.x = d.px = d3.event.x;
51
- d.y = d.py = d3.event.y;
52
- } else {
53
- d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
54
- }
55
- }
56
-
57
- function dragended(d) {
58
- d3.select(this).classed("dragging", false);
59
- if (sticky) {
60
- // Keep the node fixed and apply a visual "pinned" cue (dashed border)
61
- d.fixed = true;
62
- d3.select(this).select("circle")
63
- .style("stroke-dasharray", "4,2")
64
- .style("stroke-width", function(d) { return Math.max(parseFloat(d.node_size_edge) || 1, 2); });
65
- }
66
- }
67
-
68
- var drag = force.drag()
69
- .origin(function(d) { return d; })
70
- .on("dragstart", dragstarted)
71
- .on("drag", dragged)
72
- .on("dragend", dragended);
73
-
74
- // ---- END DRAGGING ----
75
-
76
- //Append a SVG to the body of the html page. Assign this SVG as an object to svg
77
- var svg = d3.select("body").append("svg")
78
- .attr("width", width)
79
- .attr("height", height)
80
- .style("background-color", background_color)
81
- .call(d3.behavior.zoom().on("zoom", function () { svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")") }))
82
- .on("dblclick.zoom", null)
83
- .append("g")
84
-
85
- graphRec = JSON.parse(JSON.stringify(graph));
86
-
87
- //Creates the graph data structure out of the json data
88
- force.nodes(graph.nodes)
89
- .links(graph.links)
90
- .start();
91
-
92
- // Create all the line svgs but without locations yet
93
- var link = svg.selectAll(".link")
94
- .data(graph.links)
95
- .enter().append("line")
96
- .attr("class", "link")
97
- .attr('marker-start', function(d){ return 'url(#marker_' + d.marker_start + ')' })
98
- .attr("marker-end", function(d) {
99
- if (config.directed) {return 'url(#marker_' + d.marker_end + ')' }})
100
- .style("stroke-width", function(d) {return d.edge_width;}) // LINK-WIDTH
101
- .style("stroke", function(d) {return d.edge_color;}) // EDGE-COLORS
102
- .style("stroke-dasharray", function(d) {return d.edge_style;}) // EDGE-STYLE
103
- .style("opacity", function(d) {return d.edge_opacity;}) // EDGE-OPACITY
104
- ;
105
-
106
- link.append("title").text(function(d) { return d.tooltip; });
107
-
108
- // ADD TEXT ON THE EDGES (PART 1/2)
109
- var linkText = svg.selectAll(".link-text")
110
- .data(graph.links)
111
- .enter().append("text")
112
- .attr("class", "link-text")
113
- .attr("font-size", function(d) {return d.label_fontsize + "px";})
114
- .style("fill", function(d) {return d.label_color;})
115
- .style("font-family", "Arial")
116
- .text(function(d) { return d.label; });
117
-
118
- //Do the same with the circles for the nodes
119
- var node = svg.selectAll(".node")
120
- .data(graph.nodes)
121
- .enter().append("g")
122
- .attr("class", "node")
123
- .call(drag)
124
- .on('dblclick', connectedNodes); // HIGHLIGHT ON/OFF
125
-
126
- // Right-click handler: release a pinned node back into the simulation (sticky mode only)
127
- if (sticky) {
128
- node.on('contextmenu', function(d) {
129
- d3.event.preventDefault();
130
- d.fixed = false;
131
- // Remove pinned visual cue
132
- d3.select(this).select("circle")
133
- .style("stroke-dasharray", null)
134
- .style("stroke-width", function(d) { return d.node_size_edge; });
135
- force.resume();
136
- });
137
- }
138
-
139
- {{ CLICK_COMMENT }} node.on('click', color_on_click); // ON CLICK HANDLER
140
-
141
-
142
- node.append("circle")
143
- .attr("r", function(d) { return d.node_size; }) // NODE SIZE
144
- .style("fill", function(d) {return d.node_color;}) // NODE-COLOR
145
- .style("opacity", function(d) {return d.node_opacity;}) // NODE-OPACITY
146
- .style("stroke-width", function(d) {return d.node_size_edge;}) // NODE-EDGE-SIZE
147
- .style("stroke", function(d) {return d.node_color_edge;}) // NODE-COLOR-EDGE
148
-
149
- // Text in nodes
150
- node.append("text")
151
- .attr("dx", 10)
152
- .attr("dy", ".35em")
153
- .text(function(d) {return d.node_name}) // NODE-TEXT
154
- .style("font-size", function(d) {return d.node_fontsize + "px";}) // NODE FONT SIZE
155
- .style("fill", function(d) {return d.node_fontcolor;}) // NODE FONT COLOR
156
- .style("font-family", "monospace");
157
-
158
- let showInHover = ["node_tooltip"]; // Tooltip
159
- node.append("title")
160
- .text((d) => Object.keys(d)
161
- .filter((key) => showInHover.indexOf(key) !== -1)
162
- .map((key) => `${d[key]}`)
163
- .join('\n')
164
- )
165
-
166
- //Now we are giving the SVGs co-ordinates - the force layout is generating the co-ordinates which this code is using to update the attributes of the SVG elements
167
- force.on("tick", function() {
168
- link.attr("x1", function(d) { return d.source.x; })
169
- .attr("y1", function(d) { return d.source.y; })
170
- .attr("x2", function(d) { return d.target.x; })
171
- .attr("y2", function(d) { return d.target.y; });
172
- d3.selectAll("circle").attr("cx", function(d) { return d.x; })
173
- .attr("cy", function(d) { return d.y; });
174
- d3.selectAll("text").attr("x", function(d) { return d.x; })
175
- .attr("y", function(d) { return d.y; })
176
- linkText.attr("x", function(d) { return (d.source.x + d.target.x) / 2; }) // ADD TEXT ON THE EDGES (PART 2/2)
177
- .attr("y", function(d) { return (d.source.y + d.target.y) / 2; })
178
- .attr("text-anchor", "middle");
179
-
180
- node.each(collide(config.collision)); //COLLISION DETECTION. High means a big fight to get untouchable nodes (default=0.5)
181
-
182
- });
183
-
184
- // --------- MARKER FOR EDGE ENDINGS -----------
185
-
186
- var data_marker = [
187
- { id: 0, name: 'circle', path: 'M 0, 0 m -5, 0 a 5,5 0 1,0 10,0 a 5,5 0 1,0 -10,0', viewbox: '-6 -6 12 12' }
188
- , { id: 1, name: 'square', path: 'M 0,0 m -5,-5 L 5,-5 L 5,5 L -5,5 Z', viewbox: '-5 -5 10 10' }
189
- , { id: 2, name: 'arrow', path: 'M 0,0 m -5,-5 L 5,0 L -5,5 Z', viewbox: '-5 -5 10 10' }
190
- , { id: 3, name: 'stub', path: 'M 0,0 m -1,-5 L 1,-5 L 1,5 L -1,5 Z', viewbox: '-1 -5 2 10' }
191
- ]
192
-
193
- svg.append("defs").selectAll("marker")
194
- .data(data_marker)
195
- .enter()
196
- .append('svg:marker')
197
- .attr('id', function(d){ return 'marker_' + d.name})
198
- .attr('markerHeight', 10)
199
- .attr('markerWidth', 10)
200
- .attr("markerUnits", "userSpaceOnUse") // Fix marker width
201
- .attr('orient', 'auto')
202
- .attr('refX', 15) // Offset marker-end
203
- .attr('refY', 0)
204
- .attr('viewBox', function(d){ return d.viewbox })
205
- .append('svg:path')
206
- .attr('d', function(d){ return d.path }) // Marker type
207
- .style("fill", '#808080') // Marker color
208
- .style("stroke", '#808080') // Marker edge-color
209
- .style("opacity", 0.95) // Marker opacity
210
- .style("stroke-width", 1); // Marker edge thickness
211
-
212
- // --------- END MARKER -----------
213
-
214
-
215
- // collision detection
216
-
217
- var padding = 1, // separation between circles
218
- radius = 8;
219
-
220
- function collide(alpha) {
221
- var quadtree = d3.geom.quadtree(graph.nodes);
222
- return function(d) {
223
- var rb = 2 * radius + padding,
224
- nx1 = d.x - rb,
225
- nx2 = d.x + rb,
226
- ny1 = d.y - rb,
227
- ny2 = d.y + rb;
228
- quadtree.visit(function(quad, x1, y1, x2, y2) {
229
- if (quad.point && (quad.point !== d)) {
230
- var x = d.x - quad.point.x,
231
- y = d.y - quad.point.y,
232
- l = Math.sqrt(x * x + y * y);
233
- if (l < rb) {
234
- l = (l - rb) / l * alpha;
235
- d.x -= x *= l;
236
- d.y -= y *= l;
237
- quad.point.x += x;
238
- quad.point.y += y;
239
- }
240
- }
241
- return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
242
- });
243
- };
244
- }
245
- // collision detection end
246
-
247
-
248
- //Toggle stores whether the highlighting is on
249
- var toggle = 0;
250
- //Create an array logging what is connected to what
251
- var linkedByIndex = {};
252
- for (i = 0; i < graph.nodes.length; i++) {
253
- linkedByIndex[i + "," + i] = 1;
254
- };
255
- graph.links.forEach(function(d) {
256
- linkedByIndex[d.source.index + "," + d.target.index] = 1;
257
- });
258
- //This function looks up whether a pair are neighbours
259
- function neighboring(a, b) {
260
- return linkedByIndex[a.index + "," + b.index];
261
- }
262
-
263
-
264
- // COLOR ON CLICK
265
- function color_on_click() {
266
- // Give the original color back to all nodes
267
- d3.selectAll(".node")
268
- .select("circle")
269
- .style("fill", function(d) {return d.node_color;})
270
- .style("opacity", function(d) {return d.node_opacity;})
271
- .style("stroke", function(d) {return d.node_color_edge;})
272
- .style("stroke-width", function(d) {return d.node_size_edge;})
273
- // Restore pinned cue on still-fixed nodes
274
- .style("stroke-dasharray", function(d) { return (sticky && d.fixed) ? "4,2" : null; })
275
- .attr("r", function(d) { return d.node_size; })
276
- ;
277
-
278
- // Set the color on the clicked node
279
- d3.select(this).select("circle")
280
- .style("fill", {{ CLICK_FILL }})
281
- .style("stroke", "{{ CLICK_STROKE }}")
282
- .style("stroke-width", {{ CLICK_STROKEW }})
283
- .attr("r", function(d) { return d.node_size*{{ CLICK_SIZE }}; })
284
- ;}
285
-
286
-
287
-
288
- function connectedNodes() {
289
- if (toggle == 0) {
290
- //Reduce the opacity of all but the neighbouring nodes
291
- d = d3.select(this).node().__data__;
292
- node.style("opacity", function(o) {
293
- return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
294
- });
295
- link.style("opacity", function(o) {
296
- return d.index == o.source.index | d.index == o.target.index ? 1 : 0.1;
297
- });
298
- toggle = 1;
299
- } else {
300
- //Put them back to opacity=1
301
- node.style("opacity", 0.95);
302
- link.style("opacity", 1);
303
-
304
- toggle = 0;
305
- }
306
- }
307
-
308
-
309
- //adjust threshold
310
- function threshold() {
311
- let thresh = this.value;
312
-
313
- graph.links.splice(0, graph.links.length);
314
- linkText = linkText.data([]); // CLEAR EDGE-LABELS: Clear the linkText selection
315
- linkText.exit().remove(); // CLEAR EDGE-LABELS: Clear the linkText elements from the DOM
316
-
317
- for (var i = 0; i < graphRec.links.length; i++) {
318
- if (graphRec.links[i].edge_weight > thresh) {
319
- graph.links.push(graphRec.links[i]);
320
- }
321
- }
322
- restart();
323
- }
324
-
325
- // Set the initial value of the slider to the user-defined threshold
326
- document.getElementById('thresholdSlider').value = {{ SET_SLIDER }};
327
- // Call the threshold function to set the network state
328
- threshold.call(document.getElementById('thresholdSlider'));
329
-
330
- d3.select("#thresholdSlider").on("change", threshold);
331
-
332
- //Restart the visualisation after any node and link changes
333
- function restart() {
334
-
335
- // Update EDGE-LINKS
336
- link = link.data(graph.links);
337
- link.exit().remove();
338
- link.enter().insert("line", ".node").attr("class", "link");
339
- link.style("stroke-width", function(d) {return d.edge_width;}); // LINK-WIDTH AFTER BREAKING WITH SLIDER
340
- link.style("marker-end", function(d) { // Include the markers.
341
- if (config.directed) {return 'url(#marker_' + d.marker_end + ')' }})
342
- link.style("stroke", function(d) {return d.edge_color;}); // EDGE-COLOR AFTER BREAKING WITH SLIDER
343
- link.style("stroke-dasharray", function(d) {return d.edge_style;}) // EDGE-STYLE
344
- link.style("opacity", function(d) {return d.edge_opacity;}); // EDGE-OPACITY AFTER BREAKING WITH SLIDER
345
-
346
- // Update EDGE-LABELS
347
- linkText = linkText.data(graph.links);
348
- linkText.exit().remove();
349
- linkText.enter().append("text")
350
- .attr("class", "link-text")
351
- .attr("font-size", function(d) {return d.label_fontsize + "px";})
352
- .style("fill", function(d) {return d.label_color;})
353
- .style("font-family", "Arial")
354
- .text(function(d) { return d.label; });
355
-
356
- node = node.data(graph.nodes);
357
- node.enter().insert("circle", ".cursor").attr("class", "node").attr("r", 5).call(force.drag);
358
- force.start();
359
- }
360
-
361
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes