Improve robustness

More generic Array support.
Add support for ColorGradient.
Don't crash if plot specifies GridLayout.
(Does not properly restore info on layout).
This commit is contained in:
MA Laforge 2017-05-16 19:21:35 -04:00
parent 8b8de6bcd3
commit 64dd69f550

View File

@ -30,7 +30,14 @@ Read from .hdf5 file using:
import FixedPointNumbers: N0f8 #In core Julia import FixedPointNumbers: N0f8 #In core Julia
immutable HDF5PlotNative; end #Dispatch type
#Dispatch types:
immutable HDF5PlotNative; end #Indentifies a data element that can natively be handled by HDF5
immutable HDF5CTuple; end #Identifies a "complex" tuple structure
type HDF5Plot_PlotRef
ref::Union{Plot, Void}
end
#==Useful constants #==Useful constants
@ -40,26 +47,18 @@ const _hdf5_dataroot = "data" #TODO: Eventually move data to different root (eas
const _hdf5plot_datatypeid = "TYPE" #Attribute identifying type const _hdf5plot_datatypeid = "TYPE" #Attribute identifying type
const _hdf5plot_countid = "COUNT" #Attribute for storing count const _hdf5plot_countid = "COUNT" #Attribute for storing count
#Possible element types of high-level data types:
const HDF5PLOT_MAP_STR2TELEM = Dict{String, Type}(
"NATIVE" => HDF5PlotNative,
"VOID" => Void,
"BOOL" => Bool,
"SYMBOL" => Symbol,
"TUPLE" => Tuple,
"RGBA" => ARGB{N0f8},
"EXTREMA" => Extrema,
"LENGTH" => Length,
#Sub-structure types:
"FONT" => Font,
"AXIS" => Axis,
)
#Dict has problems using "Types" as keys. Initialize in "_initialize_backend": #Dict has problems using "Types" as keys. Initialize in "_initialize_backend":
#const HDF5PLOT_MAP_TELEM2STR = Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM) const HDF5PLOT_MAP_STR2TELEM = Dict{String, Type}()
const HDF5PLOT_MAP_TELEM2STR = Dict{Type, String}() const HDF5PLOT_MAP_TELEM2STR = Dict{Type, String}()
#Don't really like this global variable... Very hacky
const HDF5PLOT_PLOTREF = HDF5Plot_PlotRef(nothing)
#Simple sub-structures that can just be written out using _hdf5plot_gwritefields:
const HDF5PLOT_SIMPLESUBSTRUCT = Union{Font, BoundingBox,
GridLayout, RootLayout, ColorGradient, SeriesAnnotations, PlotText
}
#== #==
===============================================================================# ===============================================================================#
@ -143,6 +142,32 @@ function _initialize_backend(::HDF5Backend)
import HDF5 import HDF5
export HDF5 export HDF5
if length(HDF5PLOT_MAP_TELEM2STR) < 1 if length(HDF5PLOT_MAP_TELEM2STR) < 1
#Possible element types of high-level data types:
const telem2str = Dict{String, Type}(
"NATIVE" => HDF5PlotNative,
"VOID" => Void,
"BOOL" => Bool,
"SYMBOL" => Symbol,
"TUPLE" => Tuple,
"CTUPLE" => HDF5CTuple, #Tuple of complex structures
"RGBA" => ARGB{N0f8},
"EXTREMA" => Extrema,
"LENGTH" => Length,
"ARRAY" => Array, #Dict won't allow Array to be key in HDF5PLOT_MAP_TELEM2STR
#Sub-structure types:
"FONT" => Font,
"BOUNDINGBOX" => BoundingBox,
"GRIDLAYOUT" => GridLayout,
"ROOTLAYOUT" => RootLayout,
"SERIESANNOTATIONS" => SeriesAnnotations,
# "PLOTTEXT" => PlotText,
"COLORGRADIENT" => ColorGradient,
"AXIS" => Axis,
"SUBPLOT" => Subplot,
"NULLABLE" => Nullable,
)
merge!(HDF5PLOT_MAP_STR2TELEM, telem2str)
merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM)) merge!(HDF5PLOT_MAP_TELEM2STR, Dict{Type, String}(v=>k for (k,v) in HDF5PLOT_MAP_STR2TELEM))
end end
end end
@ -236,10 +261,29 @@ function _hdf5plot_writetype(grp, k::String, T::Type)
d = HDF5.d_open(grp, k) d = HDF5.d_open(grp, k)
HDF5.a_write(d, _hdf5plot_datatypeid, tstr) HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
end end
function _hdf5plot_overwritetype(grp, k::String, T::Type)
tstr = HDF5PLOT_MAP_TELEM2STR[T]
d = HDF5.d_open(grp, k)
HDF5.a_delete(d, _hdf5plot_datatypeid)
HDF5.a_write(d, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype(grp, T::Type) #Write directly to group function _hdf5plot_writetype(grp, T::Type) #Write directly to group
tstr = HDF5PLOT_MAP_TELEM2STR[T] tstr = HDF5PLOT_MAP_TELEM2STR[T]
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr) HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end end
function _hdf5plot_overwritetype(grp, T::Type) #Write directly to group
tstr = HDF5PLOT_MAP_TELEM2STR[T]
HDF5.a_delete(grp, _hdf5plot_datatypeid)
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype{T<:Any}(grp, ::Type{Array{T}})
tstr = HDF5PLOT_MAP_TELEM2STR[Array] #ANY
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writetype{T<:BoundingBox}(grp, ::Type{T})
tstr = HDF5PLOT_MAP_TELEM2STR[BoundingBox]
HDF5.a_write(grp, _hdf5plot_datatypeid, tstr)
end
function _hdf5plot_writecount(grp, n::Int) #Write directly to group function _hdf5plot_writecount(grp, n::Int) #Write directly to group
HDF5.a_write(grp, _hdf5plot_countid, n) HDF5.a_write(grp, _hdf5plot_countid, n)
end end
@ -261,10 +305,16 @@ function _hdf5plot_gwrite(grp, k::String, v) #Default
grp[k] = v grp[k] = v
_hdf5plot_writetype(grp, k, HDF5PlotNative) _hdf5plot_writetype(grp, k, HDF5PlotNative)
end end
function _hdf5plot_gwrite{T<:Number}(grp, k::String, v::Array{T}) #Default for arrays
grp[k] = v
_hdf5plot_writetype(grp, k, HDF5PlotNative)
end
#=
function _hdf5plot_gwrite(grp, k::String, v::Array{Any}) function _hdf5plot_gwrite(grp, k::String, v::Array{Any})
# @show grp, k # @show grp, k
# warn("Cannot write Array: $k=$v") warn("Cannot write Array: $k=$v")
end end
=#
function _hdf5plot_gwrite(grp, k::String, v::Void) function _hdf5plot_gwrite(grp, k::String, v::Void)
grp[k] = 0 grp[k] = 0
_hdf5plot_writetype(grp, k, Void) _hdf5plot_writetype(grp, k, Void)
@ -279,12 +329,17 @@ function _hdf5plot_gwrite(grp, k::String, v::Symbol)
end end
function _hdf5plot_gwrite(grp, k::String, v::Tuple) function _hdf5plot_gwrite(grp, k::String, v::Tuple)
varr = [v...] varr = [v...]
if isleaftype(eltype(varr)) elt = eltype(varr)
grp[k] = [v...] # if isleaftype(elt)
_hdf5plot_writetype(grp, k, Tuple)
else _hdf5plot_gwrite(grp, k, varr)
warn("Cannot write tuple: $k=$v") if elt <: Number
#We just wrote a simple dataset
_hdf5plot_overwritetype(grp, k, Tuple)
else #Used a more complex scheme (using subgroups):
_hdf5plot_overwritetype(grp[k], HDF5CTuple)
end end
#NOTE: _hdf5plot_overwritetype overwrites "Array" type with "Tuple".
end end
function _hdf5plot_gwrite(grp, k::String, d::Dict) function _hdf5plot_gwrite(grp, k::String, d::Dict)
# warn("Cannot write dict: $k=$d") # warn("Cannot write dict: $k=$d")
@ -299,9 +354,28 @@ end
function _hdf5plot_gwrite(grp, k::String, v::Colorant) function _hdf5plot_gwrite(grp, k::String, v::Colorant)
_hdf5plot_gwrite(grp, k, ARGB{N0f8}(v)) _hdf5plot_gwrite(grp, k, ARGB{N0f8}(v))
end end
function _hdf5plot_gwrite{T<:Colorant}(grp, k::String, v::Vector{T}) #Custom vector (when not using simple numeric type):
#TODO function _hdf5plot_gwritearray{T}(grp, k::String, v::Array{T})
if "annotations" == k;
return #Hack. Does not yet support annotations.
end
vgrp = HDF5.g_create(grp, k)
_hdf5plot_writetype(vgrp, Array) #ANY
sz = size(v)
for iter in eachindex(v)
coord = ind2sub(sz, iter)
elem = v[iter]
idxstr = join(coord, "_")
_hdf5plot_gwrite(vgrp, "v$idxstr", v[iter])
end
_hdf5plot_gwrite(vgrp, "dim", [sz...])
return
end end
_hdf5plot_gwrite(grp, k::String, v::Array) =
_hdf5plot_gwritearray(grp, k, v)
function _hdf5plot_gwrite(grp, k::String, v::Extrema) function _hdf5plot_gwrite(grp, k::String, v::Extrema)
grp[k] = [v.emin, v.emax] grp[k] = [v.emin, v.emax]
_hdf5plot_writetype(grp, k, Extrema) _hdf5plot_writetype(grp, k, Extrema)
@ -314,11 +388,10 @@ end
# Write more complex structures: # Write more complex structures:
# ---------------------------------------------------------------- # ----------------------------------------------------------------
function _hdf5plot_gwrite(grp, k::String, v::Union{Plot,Subplot}) function _hdf5plot_gwrite(grp, k::String, v::Plot)
# @show :PLOTREF, k
#Don't write plot references #Don't write plot references
end end
function _hdf5plot_gwrite(grp, k::String, v::Font) function _hdf5plot_gwrite(grp, k::String, v::HDF5PLOT_SIMPLESUBSTRUCT)
_hdf5plot_gwritefields(grp, k, v) _hdf5plot_gwritefields(grp, k, v)
return return
end end
@ -331,7 +404,26 @@ function _hdf5plot_gwrite(grp, k::String, v::Axis)
_hdf5plot_writetype(grp, Axis) _hdf5plot_writetype(grp, Axis)
return return
end end
#TODO: "Properly" support Nullable using _hdf5plot_writetype?
function _hdf5plot_gwrite(grp, k::String, v::Nullable)
if isnull(v)
_hdf5plot_gwrite(grp, k, nothing)
else
_hdf5plot_gwrite(grp, k, v.value)
end
return
end
function _hdf5plot_gwrite(grp, k::String, v::SeriesAnnotations)
#Currently no support for SeriesAnnotations
return
end
function _hdf5plot_gwrite(grp, k::String, v::Subplot)
grp = HDF5.g_create(grp, k)
_hdf5plot_gwrite(grp, "index", v[:subplot_index])
_hdf5plot_writetype(grp, Subplot)
return
end
function _hdf5plot_write(grp, d::Dict) function _hdf5plot_write(grp, d::Dict)
for (k, v) in d for (k, v) in d
kstr = string(k) kstr = string(k)
@ -391,7 +483,7 @@ _hdf5plot_convert(T::Type{HDF5PlotNative}, v) = v
_hdf5plot_convert(T::Type{Void}, v) = nothing _hdf5plot_convert(T::Type{Void}, v) = nothing
_hdf5plot_convert(T::Type{Bool}, v) = (v!=0) _hdf5plot_convert(T::Type{Bool}, v) = (v!=0)
_hdf5plot_convert(T::Type{Symbol}, v) = Symbol(v) _hdf5plot_convert(T::Type{Symbol}, v) = Symbol(v)
_hdf5plot_convert(T::Type{Tuple}, v) = (v...) _hdf5plot_convert(T::Type{Tuple}, v) = tuple(v...) #With Vector{T<:Number}
function _hdf5plot_convert(T::Type{ARGB{N0f8}}, v) function _hdf5plot_convert(T::Type{ARGB{N0f8}}, v)
r, g, b, a = reinterpret(N0f8, v) r, g, b, a = reinterpret(N0f8, v)
return Colors.ARGB{N0f8}(r, g, b, a) return Colors.ARGB{N0f8}(r, g, b, a)
@ -425,13 +517,68 @@ function _hdf5plot_read(grp, k::String, T::Type{Font}, dtid)
color = _hdf5plot_read(grp, "color") color = _hdf5plot_read(grp, "color")
return Font(family, pointsize, halign, valign, rotation, color) return Font(family, pointsize, halign, valign, rotation, color)
end end
function _hdf5plot_read(grp, k::String, T::Type{Array}, dtid) #ANY
grp = HDF5.g_open(grp, k)
sz = _hdf5plot_read(grp, "dim")
if [0] == sz; return []; end
sz = tuple(sz...)
result = Array{Any}(sz)
for iter in eachindex(result)
coord = ind2sub(sz, iter)
idxstr = join(coord, "_")
result[iter] = _hdf5plot_read(grp, "v$idxstr")
end
#Hack: Implicitly make Julia detect element type.
# (Should probably write it explicitly to file)
result = [result[iter] for iter in eachindex(result)] #Potentially make more specific
return reshape(result, sz)
end
function _hdf5plot_read(grp, k::String, T::Type{HDF5CTuple}, dtid)
v = _hdf5plot_read(grp, k, Array, dtid)
return tuple(v...)
end
function _hdf5plot_read(grp, k::String, T::Type{ColorGradient}, dtid)
grp = HDF5.g_open(grp, k)
colors = _hdf5plot_read(grp, "colors")
values = _hdf5plot_read(grp, "values")
return ColorGradient(colors, values)
end
function _hdf5plot_read(grp, k::String, T::Type{BoundingBox}, dtid)
grp = HDF5.g_open(grp, k)
x0 = _hdf5plot_read(grp, "x0")
a = _hdf5plot_read(grp, "a")
return BoundingBox(x0, a)
end
_hdf5plot_read(grp, k::String, T::Type{RootLayout}, dtid) = RootLayout()
function _hdf5plot_read(grp, k::String, T::Type{GridLayout}, dtid)
grp = HDF5.g_open(grp, k)
# parent = _hdf5plot_read(grp, "parent")
parent = RootLayout()
minpad = _hdf5plot_read(grp, "minpad")
bbox = _hdf5plot_read(grp, "bbox")
grid = _hdf5plot_read(grp, "grid")
widths = _hdf5plot_read(grp, "widths")
heights = _hdf5plot_read(grp, "heights")
attr = KW() #TODO support attr: _hdf5plot_read(grp, "attr")
return GridLayout(parent, minpad, bbox, grid, widths, heights, attr)
end
function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid) function _hdf5plot_read(grp, k::String, T::Type{Axis}, dtid)
grp = HDF5.g_open(grp, k) grp = HDF5.g_open(grp, k)
kwlist = KW() kwlist = KW()
_hdf5plot_read(grp, kwlist) _hdf5plot_read(grp, kwlist)
return Axis([], kwlist) return Axis([], kwlist)
end end
function _hdf5plot_read(grp, k::String, T::Type{Subplot}, dtid)
grp = HDF5.g_open(grp, k)
idx = _hdf5plot_read(grp, "index")
return HDF5PLOT_PLOTREF.ref.subplots[idx]
end
function _hdf5plot_read(grp, k::String) function _hdf5plot_read(grp, k::String)
dtid = HDF5.a_read(grp[k], _hdf5plot_datatypeid) dtid = HDF5.a_read(grp[k], _hdf5plot_datatypeid)
T = _hdf5_map_str2telem(dtid) #expect exception T = _hdf5_map_str2telem(dtid) #expect exception
@ -445,8 +592,10 @@ function _hdf5plot_read(grp, d::Dict)
try try
v = _hdf5plot_read(grp, k) v = _hdf5plot_read(grp, k)
d[Symbol(k)] = v d[Symbol(k)] = v
catch catch e
warn(k) @show e
@show grp
warn("Could not read field $k")
end end
end end
return return
@ -479,7 +628,9 @@ end
function _hdf5plot_read(plt::Plot, f) function _hdf5plot_read(plt::Plot, f)
f = f::HDF5.HDF5File #Assert f = f::HDF5.HDF5File #Assert
#Assumpltion: subplots are already allocated (plt.subplots)
HDF5PLOT_PLOTREF.ref = plt #Used when reading "layout"
grp = HDF5.g_open(f, _hdf5_plotelempath("attr")) grp = HDF5.g_open(f, _hdf5_plotelempath("attr"))
_hdf5plot_read(grp, plt.attr) _hdf5plot_read(grp, plt.attr)