Skip to content

Commit

Permalink
Fixes and improvements for Point mappings
Browse files Browse the repository at this point in the history
  • Loading branch information
philippjfr committed Sep 24, 2018
1 parent 6e612fd commit 5dd5a7b
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 12 deletions.
41 changes: 32 additions & 9 deletions holoviews/plotting/bokeh/element.py
Expand Up @@ -47,6 +47,8 @@
legend_dimensions = ['label_standoff', 'label_width', 'label_height', 'glyph_width',
'glyph_height', 'legend_padding', 'legend_spacing', 'click_policy']

no_op_styles = ['cmap', 'palette', 'marker']


class ElementPlot(BokehPlot, GenericElementPlot):

Expand Down Expand Up @@ -610,26 +612,43 @@ def _init_glyph(self, plot, mapping, properties):
def _apply_ops(self, element, source, ranges, style, group=None):
new_style = dict(style)
for k, v in dict(style).items():
if isinstance(v, util.basestring) and v in element:
v = op(v)
if isinstance(v, util.basestring):
if v in element:
v = op(v)
elif any(d==v for d in self.overlay_dims):
v = op([d for d in self.overlay_dims if d==v][0])
if not isinstance(v, op) or (group is not None and not k.startswith(group)):
continue
dname = v.dimension.name
if dname not in element:
print(v.dimension)
if dname not in element and v.dimension not in self.overlay_dims:
new_style.pop(k)
self.warning('Specified %s op %r could not be applied, %s dimension '
'could not be found' % (k, v, v.dimension))
continue
vrange = ranges.get(dname)

val = v.eval(element, ranges)
length = [len(v) for v in source.data.values()][0]
if len(v.ops) == 0 and v.dimension in self.overlay_dims:
val = self.overlay_dims[v.dimension]
else:
val = v.eval(element, ranges)

if len(np.unique(val)) == 1:
val = val if np.isscalar(val) else val[0]

if not np.isscalar(val) and len(val) != length:
continue
if not np.isscalar(val):
lengths = [len(v) for v in source.data.values()]
if k in no_op_styles:
raise ValueError('Mapping the a dimension to the "{style}" '
'style option is not supported. To '
'map the {dim} dimension to the {style} '
'use a groupby operation to overlay '
'your data along the dimension.'.format(
style=k, dim=v.dimension))
elif source.data and len(val) != len(list(source.data.values())[0]):
continue

print(k, val)
if k == 'angle':
val = np.deg2rad(val)
if np.isscalar(val):
Expand All @@ -638,11 +657,15 @@ def _apply_ops(self, element, source, ranges, style, group=None):
key = k
source.data[k] = val
if ('color' in k and isinstance(val, np.ndarray) and
val.dtype.kind in 'if'):
not all(isinstance(v, util.basestring) and v.startswith('#') for v in val)):
kwargs = {}
if val.dtype.kind not in 'if':
kwargs['factors'] = np.unique(val)
cmapper = self._get_colormapper(v.dimension, element, ranges,
style, name=dname+'_color_mapper')
style, name=dname+'_color_mapper', **kwargs)
key = {'field': k, 'transform': cmapper}
new_style[k] = key
print(new_style)
return new_style


Expand Down
40 changes: 37 additions & 3 deletions tests/plotting/bokeh/testpointplot.py
Expand Up @@ -4,15 +4,16 @@
import numpy as np

from holoviews.core import NdOverlay
from holoviews.core.options import Cycle, AbbreviatedException
from holoviews.core.options import Cycle
from holoviews.core.util import pd
from holoviews.element import Points

from .testplot import TestBokehPlot, bokeh_renderer
from ..utils import ParamLogStream

try:
from bokeh.models import FactorRange, CategoricalColorMapper
from bokeh.models import FactorRange, LinearColorMapper, CategoricalColorMapper
from bokeh.models.glyphs import Circle, Triangle
except:
pass

Expand Down Expand Up @@ -331,6 +332,33 @@ def test_point_color_op(self):
self.assertEqual(glyph.fill_color, 'color')
self.assertEqual(glyph.line_color, 'color')

def test_point_linear_color_op(self):
points = Points([(0, 0, 0), (0, 1, 1), (0, 2, 2)],
vdims='color').options(color='color')
plot = bokeh_renderer.get_plot(points)
cds = plot.handles['cds']
glyph = plot.handles['glyph']
cmapper = plot.handles['color_color_mapper']
self.assertTrue(cmapper, LinearColorMapper)
self.assertEqual(cmapper.low, 0)
self.assertEqual(cmapper.high, 2)
self.assertEqual(cds.data['color'], np.array([0, 1, 2]))
self.assertEqual(glyph.fill_color, {'field': 'color', 'transform': cmapper})
self.assertEqual(glyph.line_color, {'field': 'color', 'transform': cmapper})

def test_point_categorical_color_op(self):
points = Points([(0, 0, 'A'), (0, 1, 'B'), (0, 2, 'C')],
vdims='color').options(color='color')
plot = bokeh_renderer.get_plot(points)
cds = plot.handles['cds']
glyph = plot.handles['glyph']
cmapper = plot.handles['color_color_mapper']
self.assertTrue(cmapper, CategoricalColorMapper)
self.assertEqual(cmapper.factors, np.array(['A', 'B', 'C']))
self.assertEqual(cds.data['color'], np.array(['A', 'B', 'C']))
self.assertEqual(glyph.fill_color, {'field': 'color', 'transform': cmapper})
self.assertEqual(glyph.line_color, {'field': 'color', 'transform': cmapper})

def test_point_line_color_op(self):
points = Points([(0, 0, '#000'), (0, 1, '#F00'), (0, 2, '#0F0')],
vdims='color').options(line_color='color')
Expand Down Expand Up @@ -410,5 +438,11 @@ def test_point_line_width_op(self):
def test_point_marker_op(self):
points = Points([(0, 0, 'circle'), (0, 1, 'triangle'), (0, 2, 'square')],
vdims='marker').options(marker='marker')
with self.assertRaises(AbbreviatedException):
with self.assertRaises(ValueError):
plot = bokeh_renderer.get_plot(points)

def test_op_ndoverlay_value(self):
overlay = NdOverlay({marker: Points(np.arange(i)) for i, marker in enumerate(['circle', 'triangle'])}, 'Marker').options('Points', marker='Marker')
plot = bokeh_renderer.get_plot(overlay)
for subplot, glyph_type in zip(plot.subplots.values(), [Circle, Triangle]):
self.assertIsInstance(subplot.handles['glyph'], glyph_type)

0 comments on commit 5dd5a7b

Please sign in to comment.