-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathplot.jl
275 lines (233 loc) · 9.11 KB
/
plot.jl
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
using Colors
# this function is copy from [GraphLayout.jl](https://github.com/IainNZ/GraphLayout.jl) and make some modifications.
"""
Given a graph and two vectors of X and Y coordinates, returns
a Compose tree of the graph layout
**Arguments**
`G`
Graph to draw
`layout`
Optional. Layout algorithm. Currently can be one of [`random_layout`,
`circular_layout`, `spring_layout`, `shell_layout`, `stressmajorize_layout`,
`spectral_layout`].
Default: `spring_layout`
`locs_x, locs_y`
Locations of the nodes. Can be any units you want,
but will be normalized and centered anyway
`NODESIZE`
Optional. Max size for the nodes. Default: `3.0/sqrt(N)`
`nodesize`
Optional. Relative size for the nodes, can be a Vector. Default: `1.0`
`nodelabel`
Optional. Labels for the vertices, a Vector or nothing. Default: `nothing`
`nodelabelc`
Optional. Color for the node labels, can be a Vector. Default: `colorant"black"`
`nodelabeldist`
Optional. Distances for the node labels from center of nodes. Default: `0.0`
`nodelabelangleoffset`
Optional. Angle offset for the node labels. Default: `π/4.0`
`NODELABELSIZE`
Optional. Largest fontsize for the vertice labels. Default: `4.0`
`nodelabelsize`
Optional. Relative fontsize for the vertice labels, can be a Vector. Default: `1.0`
`nodefillc`
Optional. Color to fill the nodes with, can be a Vector. Default: `colorant"turquoise"`
`nodestrokec`
Optional. Color for the nodes stroke, can be a Vector. Default: `nothing`
`nodestrokelw`
Optional. Line width for the nodes stroke, can be a Vector. Default: `0.0`
`edgelabel`
Optional. Labels for the edges, a Vector or nothing. Default: `[]`
`edgelabelc`
Optional. Color for the edge labels, can be a Vector. Default: `colorant"black"`
`edgelabeldistx, edgelabeldisty`
Optional. Distance for the edge label from center of edge. Default: `0.0`
`EDGELABELSIZE`
Optional. Largest fontsize for the edge labels. Default: `4.0`
`edgelabelsize`
Optional. Relative fontsize for the edge labels, can be a Vector. Default: `1.0`
`EDGELINEWIDTH`
Optional. Max line width for the edges. Default: `0.25/sqrt(N)`
`edgelinewidth`
Optional. Relative line width for the edges, can be a Vector. Default: `1.0`
`edgestrokec`
Optional. Color for the edge strokes, can be a Vector. Default: `colorant"lightgray"`
`arrowlengthfrac`
Optional. Fraction of line length to use for arrows.
Equal to 0 for undirected graphs. Default: `0.1` for the directed graphs
`arrowangleoffset`
Optional. Angular width in radians for the arrows. Default: `π/9 (20 degrees)`
`chainGraph`
Optional. Hides arrows for bidirectional edges in a directed graph. Default: false
"""
function gplot(g::AbstractGraph{T},
locs_x_in::Vector{R}, locs_y_in::Vector{R};
nodelabel = nothing,
nodelabelc = colorant"black",
nodelabelsize = 1.0,
NODELABELSIZE = 4.0,
nodelabeldist = 0.0,
nodelabelangleoffset = π / 4.0,
edgelabel = [],
edgelabelc = colorant"black",
edgelabelsize = 1.0,
EDGELABELSIZE = 4.0,
edgestrokec = colorant"lightgray",
edgelinewidth = 1.0,
EDGELINEWIDTH = 3.0 / sqrt(nv(g)),
edgelabeldistx = 0.0,
edgelabeldisty = 0.0,
nodesize = 1.0,
NODESIZE = 0.25 / sqrt(nv(g)),
nodefillc = colorant"turquoise",
nodestrokec = nothing,
nodestrokelw = 0.0,
arrowlengthfrac = is_directed(g) ? 0.1 : 0.0,
arrowangleoffset = π / 9.0,
linetype = "straight",
chainGraph = false,
outangle = pi/5) where {T <:Integer, R <: Real}
length(locs_x_in) != length(locs_y_in) && error("Vectors must be same length")
N = nv(g)
NE = ne(g)
if nodelabel != nothing && length(nodelabel) != N
error("Must have one label per node (or none)")
end
if !isempty(edgelabel) && length(edgelabel) != NE
error("Must have one label per edge (or none)")
end
locs_x = Float64.(locs_x_in)
locs_y = Float64.(locs_y_in)
# Scale to unit square
min_x, max_x = extrema(locs_x)
min_y, max_y = extrema(locs_y)
function scaler(z, a, b)
2.0 * ((z - a) / (b - a)) - 1.0
end
map!(z -> scaler(z, min_x, max_x), locs_x, locs_x)
map!(z -> scaler(z, min_y, max_y), locs_y, locs_y)
# Determine sizes
#NODESIZE = 0.25/sqrt(N)
#LINEWIDTH = 3.0/sqrt(N)
max_nodesize = NODESIZE / maximum(nodesize)
nodesize *= max_nodesize
max_edgelinewidth = EDGELINEWIDTH / maximum(edgelinewidth)
edgelinewidth *= max_edgelinewidth
max_edgelabelsize = EDGELABELSIZE / maximum(edgelabelsize)
edgelabelsize *= max_edgelabelsize
max_nodelabelsize = NODELABELSIZE / maximum(nodelabelsize)
nodelabelsize *= max_nodelabelsize
max_nodestrokelw = maximum(nodestrokelw)
if max_nodestrokelw > 0.0
max_nodestrokelw = EDGELINEWIDTH / max_nodestrokelw
nodestrokelw *= max_nodestrokelw
end
# Create nodes
nodecircle = fill(0.4Compose.w, length(locs_x))
if isa(nodesize, Real)
for i = 1:length(locs_x)
nodecircle[i] *= nodesize
end
else
for i = 1:length(locs_x)
nodecircle[i] *= nodesize[i]
end
end
nodes = circle(locs_x, locs_y, nodecircle)
# Create node labels if provided
texts = nothing
if nodelabel != nothing
text_locs_x = deepcopy(locs_x)
text_locs_y = deepcopy(locs_y)
texts = text(text_locs_x .+ nodesize .* (nodelabeldist * cos(nodelabelangleoffset)),
text_locs_y .- nodesize .* (nodelabeldist * sin(nodelabelangleoffset)),
map(string, nodelabel), [hcenter], [vcenter])
end
# Create edge labels if provided
edgetexts = nothing
if !isempty(edgelabel)
edge_locs_x = zeros(R, NE)
edge_locs_y = zeros(R, NE)
for (e_idx, e) in enumerate(edges(g))
i = src(e)
j = dst(e)
mid_x = (locs_x[i]+locs_x[j]) / 2.0
mid_y = (locs_y[i]+locs_y[j]) / 2.0
edge_locs_x[e_idx] = (is_directed(g) ? (mid_x+locs_x[j]) / 2.0 : mid_x) + edgelabeldistx * NODESIZE
edge_locs_y[e_idx] = (is_directed(g) ? (mid_y+locs_y[j]) / 2.0 : mid_y) + edgelabeldisty * NODESIZE
end
edgetexts = text(edge_locs_x, edge_locs_y, map(string, edgelabel), [hcenter], [vcenter])
end
# Create lines and arrow heads
lines, arrows = nothing, nothing
if linetype == "curve"
if arrowlengthfrac > 0.0
curves_cord, arrows_cord = graphcurve(g, locs_x, locs_y, nodesize, arrowlengthfrac, arrowangleoffset, outangle)
lines = curve(curves_cord[:,1], curves_cord[:,2], curves_cord[:,3], curves_cord[:,4])
arrows = line(arrows_cord)
else
curves_cord = graphcurve(g, locs_x, locs_y, nodesize, outangle)
lines = curve(curves_cord[:,1], curves_cord[:,2], curves_cord[:,3], curves_cord[:,4])
end
else
if arrowlengthfrac > 0.0
lines_cord, arrows_cord = graphline(g, locs_x, locs_y, nodesize, arrowlengthfrac, arrowangleoffset, chainGraph)
lines = line(lines_cord)
arrows = line(arrows_cord)
else
lines_cord = graphline(g, locs_x, locs_y, nodesize)
lines = line(lines_cord)
end
end
compose(context(units=UnitBox(-1.2, -1.2, +2.4, +2.4)),
compose(context(), texts, fill(nodelabelc), stroke(nothing), fontsize(nodelabelsize)),
compose(context(), nodes, fill(nodefillc), stroke(nodestrokec), linewidth(nodestrokelw)),
compose(context(), edgetexts, fill(edgelabelc), stroke(nothing), fontsize(edgelabelsize)),
compose(context(), arrows, stroke(edgestrokec), linewidth(edgelinewidth)),
compose(context(), lines, stroke(edgestrokec), fill(nothing), linewidth(edgelinewidth)))
end
function gplot(g; layout::Function=spring_layout, keyargs...)
gplot(g, layout(g)...; keyargs...)
end
# take from [Gadfly.jl](https://github.com/dcjones/Gadfly.jl)
function open_file(filename)
if Sys.KERNEL == :Darwin
run(`open $(filename)`)
elseif Sys.KERNEL == :Linux || Sys.KERNEL == :FreeBSD
run(`xdg-open $(filename)`)
elseif Sys.KERNEL == :Windows
run(`$(ENV["COMSPEC"]) /c start $(filename)`)
else
@warn("Showing plots is not supported on OS $(string(Sys.KERNEL))")
end
end
# taken from [Gadfly.jl](https://github.com/dcjones/Gadfly.jl)
function gplothtml(g; layout::Function=spring_layout, keyargs...)
filename = string(tempname(), ".html")
output = open(filename, "w")
plot_output = IOBuffer()
draw(SVGJS(plot_output, Compose.default_graphic_width,
Compose.default_graphic_width, false), gplot(g, layout(g)...; keyargs...))
plotsvg = String(take!(plot_output))
write(output,
"""
<!DOCTYPE html>
<html>
<head>
<title>GraphPlot Plot</title>
<meta charset="utf-8">
</head>
<body>
<script charset="utf-8">
$(read(Compose.snapsvgjs, String))
</script>
<script charset="utf-8">
$(read(gadflyjs, String))
</script>
$(plotsvg)
</body>
</html>
""")
close(output)
open_file(filename)
end