-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathd3shims.py
164 lines (139 loc) · 4.14 KB
/
d3shims.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# Quickie D3JS-NX interface for Jupyter Notebooks
# Copyright (c) Nick Timkovich 2015
# MIT Licensed
from __future__ import absolute_import, division, print_function
import random
import json
from IPython.display import HTML
import jinja2
__all__ = ['nx_force']
_LETTERS = 'bcdghjklmnpqrtvwyz'
_CHARS = _LETTERS + '346789'
_ENV = jinja2.Environment()
_TEMPLATE = _ENV.from_string('''
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
.node text {
pointer-events: none;
font: 10px sans-serif;
color: #000;
stroke-width: 0;
}
</style>
<script>
+function() {
var options = {
'divid': '#{{ divid }}',
'data': {{ data }},
'width': {{ width }},
'height': {{ height }},
'linkdistance': {{ linkdistance }}
}
if (window.d3 === undefined) {
console.log('Loading D3 from CDN');
$.getScript('https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.7/d3.js')
.done(function() {
plot_force(options);
});
} else {
plot_force(options);
}
function plot_force(o) {
var color = d3.scale.category20();
var svg = d3.select(o.divid).append("svg")
.attr("width", o.width)
.attr("height", o.height);
var force = d3.layout.force()
.charge(-120)
.linkDistance(o.linkdistance)
.size([o.width, o.height])
.nodes(o.data.nodes)
.links(o.data.links)
.start();
var link = svg.selectAll(".link")
.data(o.data.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.selectAll(".node")
.data(o.data.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag)
node.append("circle")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); });
/*node.append("title")
.text(function(d) { return d.name; });*/
{{ insert_a }}
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
}
}();
</script>
<div id="{{ divid }}">
</div>
'''.strip())
_LABEL_INSERTS = {
None: '',
'always': '''
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name; });
''',
}
def _rand_id(len=8):
return (random.choice(_LETTERS)
+ ''.join(random.choice(_CHARS) for _ in range(len - 1)))
def nx_force(G, size=(600, 400), labels=None, linkdistance=30):
"""
Provided a NetworkX graph, render it in JS using D3js.
*size* is a 2-ple with the width and height in pixels. *labels* can be
``None``, ``'always'``. *linkdistance* is the relaxed link distance.
"""
data = {
'nodes': [],
'links': [],
}
node_index_map = {}
for i, (node, ndata) in enumerate(G.nodes_iter(data=True)):
node_index_map[node] = i
data['nodes'].append({
'name': str(node),
'group': ndata.get('group', 0),
})
for u, v, edata in G.edges_iter(data=True):
data['links'].append({
'source': node_index_map[u],
'target': node_index_map[v],
'value': edata.get('weight', 1),
})
divid = 'd3shim-' + _rand_id()
html = _TEMPLATE.render(
width=size[0],
height=size[1],
divid=divid,
data=json.dumps(data),
insert_a=_LABEL_INSERTS[labels],
linkdistance=linkdistance,
)
# display(HTML(html))
# return divid
return HTML(html)