Skip to content

Commit

Permalink
Factored out functions to find closest matching overlaid Element
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Dec 19, 2015
1 parent 53d9f58 commit 9fa1ec2
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 49 deletions.
13 changes: 11 additions & 2 deletions holoviews/plotting/bokeh/element.py
Expand Up @@ -18,6 +18,7 @@
from ...core import util
from ...element import RGB
from ..plot import GenericElementPlot, GenericOverlayPlot
from ..util import dynamic_update
from .callbacks import Callbacks
from .plot import BokehPlot
from .renderer import old_bokeh
Expand Down Expand Up @@ -694,10 +695,18 @@ def update_frame(self, key, ranges=None, element=None):
for k, subplot in self.subplots.items():
empty = False
if isinstance(self.hmap, DynamicMap):
el = self.dynamic_update(subplot, k, element, items)
empty = el is None
idx = dynamic_update(self, subplot, k, element, items)
empty = idx is None
if empty:
_, el = items.pop(idx)
subplot.update_frame(key, ranges, element=el, empty=empty)


if isinstance(self.hmap, DynamicMap) and items:
raise Exception("Some Elements returned by the dynamic callback "
"were not initialized correctly and could not be "
"rendered.")

if not self.overlaid and not self.tabs:
self._update_ranges(element, ranges)
self._update_plot(key, self.handles['plot'], element)
10 changes: 9 additions & 1 deletion holoviews/plotting/mpl/element.py
Expand Up @@ -11,6 +11,7 @@
CompositeOverlay, Element3D, Columns, NdElement)
from ...element import Table, ItemTable, Raster
from ..plot import GenericElementPlot, GenericOverlayPlot
from ..util import dynamic_update
from .plot import MPLPlot
from .util import wrap_formatter

Expand Down Expand Up @@ -684,9 +685,16 @@ def update_frame(self, key, ranges=None, element=None):
for k, subplot in self.subplots.items():
el = element.get(k, None)
if isinstance(self.hmap, DynamicMap):
el = self.dynamic_update(subplot, k, element, items)
idx = dynamic_update(self, subplot, k, element, items)
if idx is not None:
_, el = items.pop(idx)
subplot.update_frame(key, ranges, el)

if isinstance(self.hmap, DynamicMap) and items:
raise Exception("Some Elements returned by the dynamic callback "
"were not initialized correctly and could not be "
"rendered.")

self._finalize_axis(key, ranges=ranges)


Expand Down
45 changes: 0 additions & 45 deletions holoviews/plotting/plot.py
Expand Up @@ -734,51 +734,6 @@ def _format_title(self, key, separator='\n'):
return separator.join([title, dim_title])


def dynamic_update(self, subplot, key, overlay, items):
"""
Function to assign layers in an Overlay to a new plot.
"""
layer = overlay.get(key, None)
if layer is None:
match_spec = util.get_overlay_spec(self.current_frame,
util.wrap_tuple(key),
subplot.current_frame)
specs = [(i, util.get_overlay_spec(overlay, util.wrap_tuple(k), el))
for i, (k, el) in enumerate(items)]
idx = self.closest_match(match_spec, specs)
k, layer = items.pop(idx)
return layer


def closest_match(self, match, specs, depth=0):
new_specs = []
match_lengths = []
for i, spec in specs:
if spec[0] == match[0]:
new_specs.append((i, spec[1:]))
else:
if util.isnumber(match[0]) and util.isnumber(spec[0]):
match_length = -abs(match[0]-spec[0])
elif all(isinstance(s[0], basestring) for s in [spec, match]):
match_length = max(i for i in range(len(match[0]))
if match[0].startswith(spec[0][:i]))
else:
match_length = 0
match_lengths.append((i, match_length, spec[0]))
if not new_specs:
if depth == 0:
raise Exception("No plot with matching type found, ensure "
"the first frame of the DynamicMap initializes "
"all required plots.")
else:
return sorted(match_lengths, key=lambda x: -x[1])[0][0]
elif new_specs == 1:
return new_specs[0][0]
else:
depth = depth+1
return self.closest_match(match[1:], new_specs, depth)



class GenericCompositePlot(DimensionedPlot):

Expand Down
49 changes: 48 additions & 1 deletion holoviews/plotting/util.py
Expand Up @@ -2,7 +2,7 @@

from ..core import (HoloMap, DynamicMap, CompositeOverlay, Layout,
GridSpace, NdLayout, Store)
from ..core.util import match_spec
from ..core.util import match_spec, is_number, wrap_tuple, get_overlay_spec


def displayable(obj):
Expand Down Expand Up @@ -159,3 +159,50 @@ def save_frames(obj, filename, fmt=None, backend=None, options=None):
for i in range(len(plot)):
plot.update(i)
renderer.save(plot, '%s_%s' % (filename, i), fmt=fmt, options=options)


def dynamic_update(plot, subplot, key, overlay, items):
"""
Given a plot, subplot and dynamically generated (Nd)Overlay
find the closest matching Element for that plot.
"""
match_spec = get_overlay_spec(overlay,
wrap_tuple(key),
subplot.current_frame)
specs = [(i, get_overlay_spec(overlay, wrap_tuple(k), el))
for i, (k, el) in enumerate(items)]
return closest_match(match_spec, specs)


def closest_match(match, specs, depth=0):
"""
Recursively iterates over type, group, label and overlay key,
finding the closest matching spec.
"""
new_specs = []
match_lengths = []
for i, spec in specs:
print spec, match, depth
if spec[0] == match[0]:
new_specs.append((i, spec[1:]))
else:
if is_number(match[0]) and is_number(spec[0]):
match_length = -abs(match[0]-spec[0])
elif all(isinstance(s[0], basestring) for s in [spec, match]):
match_length = max(i for i in range(len(match[0]))
if match[0].startswith(spec[0][:i]))
else:
match_length = 0
match_lengths.append((i, match_length, spec[0]))

if len(new_specs) == 1:
return new_specs[0][0]
elif new_specs:
depth = depth+1
return closest_match(match[1:], new_specs, depth)
else:
if depth == 0 or not match_lengths:
return None
else:
return sorted(match_lengths, key=lambda x: -x[1])[0][0]

0 comments on commit 9fa1ec2

Please sign in to comment.