Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed hover tooltips for Curve and Path types #1004

Merged
merged 2 commits into from
Dec 8, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions holoviews/plotting/bokeh/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,24 +141,40 @@ def get_data(self, element, ranges=None, empty=False):
y: [] if empty else element.dimension_values(yidx)},
dict(x=x, y=y))

def _hover_tooltips(self, element):
if self.batched:
return list(self.hmap.last.kdims)
else:
return list(self.overlay_dims.keys())

def get_batched_data(self, overlay, ranges=None, empty=False):
data = defaultdict(list)
for key, el in overlay.items():
opts = ['color', 'line_alpha', 'line_color']
for key, el in overlay.data.items():
eldata, elmapping = self.get_data(el, ranges, empty)
for k, eld in eldata.items():
data[k].append(eld)

# Add options
style = self.lookup_options(el, 'style')
style = style.max_cycles(len(self.ordering))
zorder = self.get_zorder(overlay, key, el)
for opt in self._mapping:
if opt in ['xs', 'ys']:
index = {'xs': 0, 'ys': 1}[opt]
val = el.dimension_values(index)
else:
val = style[zorder].get(opt)
style = style[zorder]
for opt in opts:
if opt not in style:
continue
val = style[opt]
if opt == 'color' and isinstance(val, tuple):
val = rgb2hex(val)
data[opt].append(val)
data[opt].append([val])

for d, k in zip(overlay.kdims, key):
sanitized = dimension_sanitizer(d.name)
data[sanitized].append([k])
data = {opt: vals for opt, vals in data.items()
if not any(v is None for v in vals)}
return data, {k: k for k in data}
return data, dict(xs=elmapping['x'], ys=elmapping['y'],
**{o: o for o in opts if o in data})


class AreaPlot(PolygonPlot):
Expand Down
28 changes: 18 additions & 10 deletions holoviews/plotting/bokeh/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,18 +195,21 @@ def _construct_callbacks(self):
cbs.append(cb(self, cb_streams, source))
return cbs


def _init_tools(self, element, callbacks=[]):
"""
Processes the list of tools to be supplied to the plot.
"""
def _hover_tooltips(self, element):
if self.batched:
dims = list(self.hmap.last.kdims)
else:
dims = list(self.overlay_dims.keys())
dims += element.dimensions()
return dims

def _init_tools(self, element, callbacks=[]):
"""
Processes the list of tools to be supplied to the plot.
"""
tooltip_dims = self._hover_tooltips(element)
tooltips = [(d.pprint_label, '@'+util.dimension_sanitizer(d.name))
for d in dims]
for d in tooltip_dims]

callbacks = callbacks+self.callbacks
cb_tools, tool_names = [], []
Expand All @@ -233,10 +236,15 @@ def _get_hover_data(self, data, element, empty=False):
Initializes hover data based on Element dimension values.
If empty initializes with no data.
"""
if 'hover' in self.default_tools + self.tools:
for d in element.dimensions(label=True):
sanitized = util.dimension_sanitizer(d)
data[sanitized] = [] if empty else element.dimension_values(d)
if 'hover' not in self.default_tools + self.tools:
return

for d in element.dimensions(label=True):
sanitized = util.dimension_sanitizer(d)
data[sanitized] = [] if empty else element.dimension_values(d)
for k, v in self.overlay_dims.items():
dim = util.dimension_sanitizer(k.name)
data[dim] = [v for _ in range(len(data.values()[0]))]


def _axes_props(self, plots, subplots, element, ranges):
Expand Down
14 changes: 14 additions & 0 deletions holoviews/plotting/bokeh/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ class PathPlot(ElementPlot):
_plot_methods = dict(single='multi_line', batched='multi_line')
_mapping = dict(xs='xs', ys='ys')

def _hover_tooltips(self, element):
if self.batched:
return list(self.hmap.last.kdims)
else:
return list(self.overlay_dims.keys())

def get_data(self, element, ranges=None, empty=False):
xidx, yidx = (1, 0) if self.invert_axes else (0, 1)
xs = [] if empty else [path[:, xidx] for path in element.data]
Expand Down Expand Up @@ -50,6 +56,14 @@ class PolygonPlot(ColorbarPlot, PathPlot):
style_opts = ['color', 'cmap', 'palette'] + line_properties + fill_properties
_plot_methods = dict(single='patches', batched='patches')

def _hover_tooltips(self, element):
if self.batched:
dims = list(self.hmap.last.kdims)
else:
dims = list(self.overlay_dims.keys())
dims += element.vdims
return dims

def get_data(self, element, ranges=None, empty=False):
xs = [] if empty else [path[:, 0] for path in element.data]
ys = [] if empty else [path[:, 1] for path in element.data]
Expand Down
46 changes: 45 additions & 1 deletion tests/testplotinstantiation.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
NdOverlay, GridSpace)
from holoviews.element import (Curve, Scatter, Image, VLine, Points,
HeatMap, QuadMesh, Spikes, ErrorBars,
Scatter3D)
Scatter3D, Path, Polygons)
from holoviews.element.comparison import ComparisonTestCase
from holoviews.streams import PositionXY, PositionX
from holoviews.plotting import comms
Expand All @@ -30,6 +30,7 @@
bokeh_renderer = Store.renderers['bokeh']
from holoviews.plotting.bokeh.callbacks import Callback
from bokeh.models.mappers import LinearColorMapper, LogColorMapper
from bokeh.models.tools import HoverTool
except:
bokeh_renderer = None

Expand Down Expand Up @@ -125,6 +126,49 @@ def test_batched_plot(self):
extents = plot.get_extents(overlay, {})
self.assertEqual(extents, (0, 0, 98, 98))

def _test_hover_info(self, element, tooltips):
plot = bokeh_renderer.get_plot(element)
plot.initialize_plot()
fig = plot.state
hover = fig.select(dict(type=HoverTool))
self.assertTrue(len(hover))
self.assertEqual(hover[0].tooltips, tooltips)

def test_curve_overlay_hover(self):
obj = NdOverlay({i: Curve(np.random.rand(10,2)) for i in range(5)},
kdims=['Test'])
opts = {'Curve': {'tools': ['hover']},
'NdOverlay': {'legend_limit': 0}}
obj = obj(plot=opts)
self._test_hover_info(obj, [('Test', '@Test')])


def test_points_overlay_hover(self):
obj = NdOverlay({i: Points(np.random.rand(10,2)) for i in range(5)},
kdims=['Test'])
opts = {'Points': {'tools': ['hover']},
'NdOverlay': {'legend_limit': 0}}
obj = obj(plot=opts)
self._test_hover_info(obj, [('Test', '@Test'), ('x', '@x'),
('y', '@y')])

def test_path_overlay_hover(self):
obj = NdOverlay({i: Path([np.random.rand(10,2)]) for i in range(5)},
kdims=['Test'])
opts = {'Path': {'tools': ['hover']},
'NdOverlay': {'legend_limit': 0}}
obj = obj(plot=opts)
self._test_hover_info(obj, [('Test', '@Test')])

def test_polygons_overlay_hover(self):
obj = NdOverlay({i: Polygons([np.random.rand(10,2)], vdims=['z'], level=0)
for i in range(5)}, kdims=['Test'])
opts = {'Polygons': {'tools': ['hover']},
'NdOverlay': {'legend_limit': 0}}
obj = obj(plot=opts)
self._test_hover_info(obj, [('Test', '@Test'), ('z', '@z')])


def _test_colormapping(self, element, dim, log=False):
plot = bokeh_renderer.get_plot(element)
plot.initialize_plot()
Expand Down