-
-
Notifications
You must be signed in to change notification settings - Fork 78
/
json.jl
113 lines (94 loc) · 3.44 KB
/
json.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
# -------------------------------- #
# Custom JSON output for our types #
# -------------------------------- #
JSON.lower(a::HasFields) = a.fields
function _apply_style_axis!(p::Plot, ax, force::Bool=false)
if haskey(p.style.layout.fields, Symbol(ax, "axis")) || force
ax_names = Iterators.filter(
_x-> startswith(string(_x), "$(ax)axis"),
keys(p.layout.fields)
)
for ax_name in ax_names
cur = p.layout.fields[ax_name]
cur = merge(p.style.layout[Symbol(ax, "axis")], cur)
p.layout.fields[ax_name] = cur
end
if isempty(ax_names)
nm = Symbol(ax, "axis")
p.layout.fields[nm] = deepcopy(p.style.layout[nm])
end
end
end
_maybe_set_attr!(hf::HasFields, k::Symbol, v::Any) =
get(hf, k, nothing) == nothing && setindex!(hf, v, k)
# special case for associative to get nested application
function _maybe_set_attr!(hf::HasFields, k1::Symbol, v::Associative)
for (k2, v2) in v
_maybe_set_attr!(hf, Symbol(k1, "_", k2), v2)
end
end
function _maybe_set_attr!(p::Plot, k1::Symbol, v::Associative)
for (k2, v2) in v
_maybe_set_attr!(p, Symbol(k1, "_", k2), v2)
end
end
function _maybe_set_attr!(p::Plot, k::Symbol, v)
foreach(t -> _maybe_set_attr!(t, k, v), p.data)
end
function _maybe_set_attr!(p::Plot, k::Symbol, v::Cycler)
ix = 0
for t in p.data
if t[k] == Dict() # was empty
t[k] = v[ix+=1]
end
end
end
function JSON.lower(p::Plot)
_is3d = any(_x -> contains(string(_x[:type]), "3d"), p.data)
# apply layout attrs
if !isempty(p.style.layout)
# force xaxis and yaxis if plot is 2d
_apply_style_axis!(p, "x", !_is3d)
_apply_style_axis!(p, "y", !_is3d)
_apply_style_axis!(p, "z")
# extract this so we can pop! off xaxis and yaxis so they aren't
# applied again
la = deepcopy(p.style.layout)
pop!(la.fields, :xaxis, nothing)
pop!(la.fields, :yaxis, nothing)
pop!(la.fields, :zaxis, nothing)
p.layout = merge(la, p.layout)
end
# apply global trace attrs
if !isempty(p.style.global_trace)
for (k, v) in p.style.global_trace.fields
_maybe_set_attr!(p, k, v)
end
end
# apply trace specific attrs
if !isempty(p.style.trace)
for t in p.data
t_type = Symbol(get(t, :type, :scatter))
for (k, v) in get(p.style.trace, t_type, Dict())
_maybe_set_attr!(p, k, v)
end
end
end
Dict(:data => p.data, :layout => p.layout)
end
JSON.lower(sp::SyncPlot) = JSON.lower(sp.plot)
JSON.lower(a::Colors.Colorant) = string("#", hex(a))
# Let string interpolation stringify to JSON format
Base.print(io::IO, a::Union{Shape,GenericTrace,PlotlyAttribute,Layout,Plot}) = print(io, JSON.json(a))
Base.print{T<:GenericTrace}(io::IO, a::Vector{T}) = print(io, JSON.json(a))
GenericTrace(d::Associative{Symbol}) = GenericTrace(pop!(d, :type, "scatter"), d)
GenericTrace{T<:AbstractString}(d::Associative{T}) = GenericTrace(_symbol_dict(d))
Layout{T<:AbstractString}(d::Associative{T}) = Layout(_symbol_dict(d))
function JSON.parse(::Type{Plot}, str::AbstractString)
d = JSON.parse(str)
data = GenericTrace[GenericTrace(tr) for tr in d["data"]]
layout = Layout(d["layout"])
Plot(data, layout)
end
JSON.parsefile(::Type{Plot}, fn) =
open(fn, "r") do f; JSON.parse(Plot, readstring(f)) end