Skip to content

Commit e37e271

Browse files
committed
add graphs extension
1 parent 98e97e0 commit e37e271

File tree

11 files changed

+193
-11
lines changed

11 files changed

+193
-11
lines changed

Project.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,18 @@ REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
1212
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
1313
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
1414

15+
[weakdeps]
16+
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
17+
NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a"
18+
19+
[extensions]
20+
GraphsExt = ["Graphs", "NetworkLayout"]
21+
1522
[compat]
1623
DefaultApplication = "1.1"
24+
Graphs = "1.12.0"
1725
JSON = "0.21"
1826
MultilineStrings = "0.1.1"
27+
NetworkLayout = "0.4.7"
1928
Tables = "1"
2029
julia = "1.6"

docs/Project.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
33
DemoCards = "311a05b2-6137-4a5a-b473-18580a3d38b5"
44
Deneb = "b5a61f88-a05b-4725-9526-4eca17cec623"
55
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
6+
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
7+
NetworkLayout = "46757867-2c16-5918-afeb-47bfcb05e46a"
68

79
[compat]
810
DemoCards = "0.5.4"
911
Documenter = "1.1"
12+
Graphs = "1.12.0"
13+
NetworkLayout = "0.4.7"

docs/examples/config.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"composite_marks",
99
"multi_view_plots",
1010
"layered_plots",
11-
"interactive"
11+
"interactive",
12+
"graphs",
1213
]
1314
}

docs/examples/graphs/graph_chart.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# ---
2+
# cover: assets/graph_chart.png
3+
# author: bruno
4+
# description: Simple Graph Chart
5+
# ---
6+
7+
using Deneb, Graphs, NetworkLayout
8+
9+
g = wheel_graph(10)
10+
11+
chart = plotgraph(g)
12+
13+
# save cover #src
14+
save("assets/graph_chart.png", chart) #src
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# ---
2+
# cover: assets/graph_chart_labels.png
3+
# author: bruno
4+
# description: Graph Chart wih Custom Labels
5+
# ---
6+
7+
using Deneb, Graphs, NetworkLayout
8+
9+
g = smallgraph(:cubical)
10+
11+
chart = plotgraph(g, node_labels='a':'h')
12+
13+
# save cover #src
14+
save("assets/graph_chart_labels.png", chart) #src
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# ---
2+
# cover: assets/graph_chart_layout.png
3+
# author: bruno
4+
# description: Graph Chart wih Custom Layout
5+
# ---
6+
7+
using Deneb, Graphs, NetworkLayout
8+
9+
g = smallgraph(:petersen)
10+
11+
chart = plotgraph(g, layout=Shell(nlist=[6:10, ]))
12+
13+
# save cover #src
14+
save("assets/graph_chart_layout.png", chart) #src

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ makedocs(
2727
"Multi-views" => "multiview.md",
2828
"Interactive Charts" => "interactive.md",
2929
"Data Transformations" => "transformations.md",
30+
#"Graphs" => "graphs.jl",
3031
# "Customization" => "customization.md",
3132
"Themes" => "themes.md",
3233
# "Internals" => "internals.md",

ext/GraphsExt.jl

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
module GraphsExt
2+
3+
using Deneb, Graphs, NetworkLayout
4+
5+
function node_data(g::AbstractGraph, pos; labels::Union{Nothing, AbstractVector}=nothing)
6+
isnothing(labels) && return [(id=v, x=pos[v][1], y=pos[v][2]) for v in vertices(g)]
7+
length(labels) == nv(g) || error("labels length must be equal to number of vertices")
8+
return [
9+
(id=v, x=pos[v][1], y=pos[v][2], label=l)
10+
for (v, l) in zip(vertices(g), labels)
11+
]
12+
end
13+
14+
function edge_data(g::AbstractGraph, pos; labels::Union{Nothing, AbstractVector}= nothing)
15+
isnothing(labels) && return [
16+
(src=src(e), dst=dst(e), x=pos[src(e)][1], y=pos[src(e)][2], x2=pos[dst(e)][1], y2=pos[dst(e)][2], label=repr(e))
17+
for e in edges(g)
18+
]
19+
length(labels) == ne(g) || error("labels length must be equal to number of edges")
20+
return [
21+
(src=src(e), dst=dst(e), x=pos[src(e)][1], y=pos[src(e)][2], x2=pos[dst(e)][1], y2=pos[dst(e)][2], label=l)
22+
for (e, l) in zip(edges(g), labels)
23+
]
24+
end
25+
26+
function Deneb.graph_data(
27+
g::AbstractGraph;
28+
layout::NetworkLayout.AbstractLayout=Spring(),
29+
node_labels::Union{Nothing, AbstractVector}=nothing,
30+
edge_labels::Union{Nothing, AbstractVector}=nothing,
31+
)
32+
pos = layout(g)
33+
nodes = node_data(g, pos; labels=node_labels)
34+
edges = edge_data(g, pos; labels=edge_labels)
35+
return nodes, edges
36+
end
37+
38+
function Deneb.Datasets(
39+
g::AbstractGraph;
40+
layout::NetworkLayout.AbstractLayout=Spring(),
41+
node_labels::Union{Nothing, AbstractVector}=nothing,
42+
edge_labels::Union{Nothing, AbstractVector}=nothing,
43+
)
44+
nodes, edges = graph_data(g; layout, node_labels, edge_labels)
45+
return Datasets(; nodes, edges)
46+
end
47+
48+
function Deneb.plotgraph(
49+
g::AbstractGraph;
50+
layout::NetworkLayout.AbstractLayout=Spring(),
51+
node_labels::Union{Nothing, AbstractVector}=nothing,
52+
edge_labels::Union{Nothing, AbstractVector}=nothing,
53+
)
54+
base = Datasets(g; layout, node_labels, edge_labels) * Encoding(
55+
x=field("x:q", axis=nothing),
56+
y=field("y:q", axis=nothing),
57+
)
58+
59+
ntooltip = isnothing(node_labels) ? :id : [field(:id), field(:label)]
60+
points = Mark(
61+
:point, size=400, fill=:lightblue, opacity=1,
62+
) * Encoding(tooltip=ntooltip)
63+
labels = Mark(:text) * Encoding(
64+
text=isnothing(node_labels) ? :id : :label,
65+
tooltip=ntooltip,
66+
)
67+
68+
lines = Mark(:rule, size=3) * Encoding(x2=:x2, y2=:y2, tooltip=:label)
69+
70+
return base * (
71+
Data(:name, :edges) * lines +
72+
Data(:name, :nodes) * (points + labels)
73+
)
74+
end
75+
76+
end

src/Deneb.jl

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ include("themes.jl")
2020
export
2121
# api
2222
spec, vlspec,
23-
Data, Mark, Encoding, Transform, Params, Facet, Repeat,
23+
Data, Datasets, Mark, Encoding, Transform, Params, Facet, Repeat,
2424
field, layout, projection,
2525
resolve, resolve_scale, resolve_axis, resolve_legend,
2626
config, title, expr, param,
@@ -41,4 +41,23 @@ export
4141
# themes
4242
set_theme!, print_theme
4343

44+
45+
# Graphs extension
46+
47+
"""
48+
plotgraph
49+
50+
Method from GraphsExt, requires Graphs and NetworkLayout
51+
"""
52+
function plotgraph end
53+
54+
"""
55+
grapdata
56+
57+
Method from GraphsExt, requires Graphs and NetworkLayout
58+
"""
59+
function graph_data end
60+
61+
export plotgraph, graph_data
62+
4463
end # module

src/api.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ vlspec(s::Spec) = VegaLiteSpec(; rawspec(s)...)
3333
vlspec(s::NamedTuple) = VegaLiteSpec(; s...)
3434
vlspec(; s...) = VegaLiteSpec(; s...)
3535
vlspec(s::ConstrainedSpec) = VegaLiteSpec(; rawspec(s)...)
36+
#vlspec(s::DatasetsSpec) = VegaLiteSpec(datasets = rawspec(s))
3637
vlspec(s::DataSpec) = VegaLiteSpec(data = rawspec(s))
3738
vlspec(s::TransformSpec) = VegaLiteSpec(transform = rawspec(s))
3839
vlspec(s::ParamsSpec) = VegaLiteSpec(params = rawspec(s))
@@ -64,6 +65,22 @@ function Data(generator::SymbolOrString; kw...)
6465
kw = isempty(kw) ? true : kw
6566
Data(NamedTuple{(generator,)}((kw, )))
6667
end
68+
Data(key::SymbolOrString, value::SymbolOrString) = Data(NamedTuple{(key,)}((value, )))
69+
70+
"""
71+
Datasets(; datasets...)
72+
73+
Creates a top-level `dataspec` with the given datasets (values) and names (keywords).
74+
The datasets can be given as tables that supports the [Tables.jl interface](https://github.com/JuliaData/Tables.jl).
75+
"""
76+
function Datasets(; datasets...)
77+
names = keys(datasets)
78+
tables = (_rowtable(t) for t in values(datasets))
79+
return vlspec(datasets=NamedTuple(zip(names, tables)))
80+
#return DatasetsSpec(spec(NamedTuple(zip(names, tables))))
81+
end
82+
83+
_rowtable(table) = Tables.istable(table) && !Tables.isrowtable(table) ? Tables.rowtable(table) : table
6784

6885
"""
6986
Transform(; spec...)

src/types.jl

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,29 @@ struct TopLevelProperties <: PropertiesSpec
8686
autosize::Spec
8787
config::Spec # TODO: create dedicated type?
8888
usermeta::Spec
89+
datasets::Spec
8990
end
9091
TopLevelProperties(; spec...) = ConstrainedSpec(TopLevelProperties; spec...)
9192

93+
#=
94+
"""
95+
Spec containing the `datasets` property of viewable specifications.
96+
https://vega.github.io/vega-lite/docs/data.html
97+
"""
98+
struct DatasetsSpec <: ConstrainedSpec
99+
datasets::Spec
100+
end
101+
DatasetsSpec(; datasets=nothing, kw...) = DatasetsSpec(Spec(datasets))
102+
rawspec(s::DatasetsSpec) = rawspec(s.datasets)
103+
=#
104+
92105
"""
93106
Vega-Lite specification.
94107
https://vega.github.io/vega-lite/docs/spec.html
95108
"""
96109
struct VegaLiteSpec{T<:ViewableSpec} <: ConstrainedSpec
97110
toplevel::TopLevelProperties
111+
#datasets::DatasetsSpec
98112
viewspec::T
99113
end
100114
VegaLiteSpec(; spec...) = ConstrainedSpec(VegaLiteSpec; spec...)
@@ -135,19 +149,18 @@ DataSpec(s::Union{Spec, DataSpec}) = DataSpec(rawspec(s))
135149
DataSpec(; data=nothing, kw...) = DataSpec(data)
136150
function rawspec(s::DataSpec)
137151
!Tables.istable(s.data) && return s.data
138-
Tables.isrowtable(s.data) && return (values=s.data, )
139152
# already in the VegaLite shape or a data generators
140-
if s.data isa NamedTuple && (
141-
haskey(s.data, :values)
142-
|| haskey(s.data, :graticule)
143-
|| haskey(s.data, :sequence)
144-
|| haskey(s.data, :sphere)
145-
)
146-
return s.data
147-
end
153+
s.data isa NamedTuple && (
154+
haskey(s.data, :values)
155+
|| haskey(s.data, :graticule)
156+
|| haskey(s.data, :sequence)
157+
|| haskey(s.data, :sphere)
158+
) && return s.data
159+
Tables.isrowtable(s.data) && return (values=s.data, )
148160
return (values=Tables.rowtable(s.data), )
149161
end
150162

163+
151164
"""
152165
Spec containing the `mark` property of a Single-View spec.
153166
https://vega.github.io/vega-lite/docs/mark.html

0 commit comments

Comments
 (0)