From 8d7f701cfe7aee8ad4e77c68bbe9b1fc1519cf92 Mon Sep 17 00:00:00 2001 From: Philipp Rudiger Date: Tue, 26 Mar 2019 18:14:30 +0000 Subject: [PATCH] Improved support for style mapping constant values (#3578) --- holoviews/plotting/bokeh/element.py | 10 ++-- holoviews/plotting/mpl/element.py | 6 ++- .../tests/plotting/bokeh/testcurveplot.py | 53 +++++++++++++++++++ .../plotting/matplotlib/testcurveplot.py | 53 +++++++++++++++++++ 4 files changed, 116 insertions(+), 6 deletions(-) diff --git a/holoviews/plotting/bokeh/element.py b/holoviews/plotting/bokeh/element.py index a343132d21..57652202a3 100644 --- a/holoviews/plotting/bokeh/element.py +++ b/holoviews/plotting/bokeh/element.py @@ -23,7 +23,7 @@ from bokeh.models.widgets import Panel, Tabs from bokeh.plotting.helpers import _known_tools as known_tools -from ...core import DynamicMap, CompositeOverlay, Element, Dimension +from ...core import DynamicMap, CompositeOverlay, Element, Dimension, Dataset from ...core.options import abbreviated_exception, SkipRendering from ...core import util from ...element import Graph, VectorField, Path, Contours, Tiles @@ -953,8 +953,10 @@ def _apply_transforms(self, element, data, ranges, style, group=None): 'as not all dimensions could be resolved.' % (k, v)) continue - if len(v.ops) == 0 and v.dimension in self.overlay_dims: - val = self.overlay_dims[v.dimension] + if v.dimension in self.overlay_dims: + ds = Dataset({d.name: v for d, v in self.overlay_dims.items()}, + list(self.overlay_dims)) + val = v.apply(ds, ranges=ranges, flat=True)[0] elif isinstance(element, Path) and not isinstance(element, Contours): val = np.concatenate([v.apply(el, ranges=ranges, flat=True)[:-1] for el in element.split()]) @@ -962,7 +964,7 @@ def _apply_transforms(self, element, data, ranges, style, group=None): val = v.apply(element, ranges=ranges, flat=True) if (not util.isscalar(val) and len(util.unique_array(val)) == 1 and - (not 'color' in k or validate('color', val))): + ((not 'color' in k or validate('color', val)) or k in self._nonvectorized_styles)): val = val[0] if not util.isscalar(val): diff --git a/holoviews/plotting/mpl/element.py b/holoviews/plotting/mpl/element.py index 77f5a4f8da..94d952e862 100644 --- a/holoviews/plotting/mpl/element.py +++ b/holoviews/plotting/mpl/element.py @@ -558,8 +558,10 @@ def _apply_transforms(self, element, ranges, style): % (k, v)) continue - if len(v.ops) == 0 and v.dimension in self.overlay_dims: - val = self.overlay_dims[v.dimension] + if v.dimension in self.overlay_dims: + ds = Dataset({d.name: v for d, v in self.overlay_dims.items()}, + list(self.overlay_dims)) + val = v.apply(ds, ranges=ranges, flat=True)[0] elif type(element) is Path: val = np.concatenate([v.apply(el, ranges=ranges, flat=True)[:-1] for el in element.split()]) diff --git a/holoviews/tests/plotting/bokeh/testcurveplot.py b/holoviews/tests/plotting/bokeh/testcurveplot.py index b9ed795c67..b9516070c6 100644 --- a/holoviews/tests/plotting/bokeh/testcurveplot.py +++ b/holoviews/tests/plotting/bokeh/testcurveplot.py @@ -9,6 +9,7 @@ from holoviews.element import Curve from holoviews.plotting.util import rgb2hex from holoviews.streams import PointerX +from holoviews.util.transform import dim from .testplot import TestBokehPlot, bokeh_renderer @@ -377,3 +378,55 @@ def test_curve_line_width_op(self): vdims=['y', 'linewidth']).options(line_width='linewidth') with self.assertRaises(Exception): bokeh_renderer.get_plot(curve) + + def test_curve_style_mapping_ndoverlay_dimensions(self): + ndoverlay = NdOverlay({ + (0, 'A'): Curve([1, 2, 0]), (0, 'B'): Curve([1, 2, 1]), + (1, 'A'): Curve([1, 2, 2]), (1, 'B'): Curve([1, 2, 3])}, + ['num', 'cat'] + ).opts({ + 'Curve': dict( + color=dim('num').categorize({0: 'red', 1: 'blue'}), + line_dash=dim('cat').categorize({'A': 'solid', 'B': 'dashed'}) + ) + }) + plot = bokeh_renderer.get_plot(ndoverlay) + for (num, cat), sp in plot.subplots.items(): + glyph = sp.handles['glyph'] + color = glyph.line_color + if num == 0: + self.assertEqual(color, 'red') + else: + self.assertEqual(color, 'blue') + linestyle = glyph.line_dash + if cat == 'A': + self.assertEqual(linestyle, []) + else: + self.assertEqual(linestyle, [6]) + + def test_curve_style_mapping_constant_value_dimensions(self): + vdims = ['y', 'num', 'cat'] + ndoverlay = NdOverlay({ + 0: Curve([(0, 1, 0, 'A'), (1, 0, 0, 'A')], vdims=vdims), + 1: Curve([(0, 1, 0, 'B'), (1, 1, 0, 'B')], vdims=vdims), + 2: Curve([(0, 1, 1, 'A'), (1, 2, 1, 'A')], vdims=vdims), + 3: Curve([(0, 1, 1, 'B'), (1, 3, 1, 'B')], vdims=vdims)} + ).opts({ + 'Curve': dict( + color=dim('num').categorize({0: 'red', 1: 'blue'}), + line_dash=dim('cat').categorize({'A': 'solid', 'B': 'dashed'}) + ) + }) + plot = bokeh_renderer.get_plot(ndoverlay) + for k, sp in plot.subplots.items(): + glyph = sp.handles['glyph'] + color = glyph.line_color + if ndoverlay[k].iloc[0, 2] == 0: + self.assertEqual(color, 'red') + else: + self.assertEqual(color, 'blue') + linestyle = glyph.line_dash + if ndoverlay[k].iloc[0, 3] == 'A': + self.assertEqual(linestyle, []) + else: + self.assertEqual(linestyle, [6]) diff --git a/holoviews/tests/plotting/matplotlib/testcurveplot.py b/holoviews/tests/plotting/matplotlib/testcurveplot.py index 4a57a8c6fd..21ae9ab750 100644 --- a/holoviews/tests/plotting/matplotlib/testcurveplot.py +++ b/holoviews/tests/plotting/matplotlib/testcurveplot.py @@ -6,6 +6,7 @@ from holoviews.core.overlay import NdOverlay from holoviews.core.util import pd from holoviews.element import Curve +from holoviews.util.transform import dim from .testplot import TestMPLPlot, mpl_renderer @@ -185,3 +186,55 @@ def test_curve_linewidth_op(self): vdims=['y', 'linewidth']).options(linewidth='linewidth') with self.assertRaises(Exception): mpl_renderer.get_plot(curve) + + def test_curve_style_mapping_ndoverlay_dimensions(self): + ndoverlay = NdOverlay({ + (0, 'A'): Curve([1, 2, 0]), (0, 'B'): Curve([1, 2, 1]), + (1, 'A'): Curve([1, 2, 2]), (1, 'B'): Curve([1, 2, 3])}, + ['num', 'cat'] + ).opts({ + 'Curve': dict( + color=dim('num').categorize({0: 'red', 1: 'blue'}), + linestyle=dim('cat').categorize({'A': '-.', 'B': '-'}) + ) + }) + plot = mpl_renderer.get_plot(ndoverlay) + for (num, cat), sp in plot.subplots.items(): + artist = sp.handles['artist'] + color = artist.get_color() + if num == 0: + self.assertEqual(color, 'red') + else: + self.assertEqual(color, 'blue') + linestyle = artist.get_linestyle() + if cat == 'A': + self.assertEqual(linestyle, '-.') + else: + self.assertEqual(linestyle, '-') + + def test_curve_style_mapping_constant_value_dimensions(self): + vdims = ['y', 'num', 'cat'] + ndoverlay = NdOverlay({ + 0: Curve([(0, 1, 0, 'A'), (1, 0, 0, 'A')], vdims=vdims), + 1: Curve([(0, 1, 0, 'B'), (1, 1, 0, 'B')], vdims=vdims), + 2: Curve([(0, 1, 1, 'A'), (1, 2, 1, 'A')], vdims=vdims), + 3: Curve([(0, 1, 1, 'B'), (1, 3, 1, 'B')], vdims=vdims)} + ).opts({ + 'Curve': dict( + color=dim('num').categorize({0: 'red', 1: 'blue'}), + linestyle=dim('cat').categorize({'A': '-.', 'B': '-'}) + ) + }) + plot = mpl_renderer.get_plot(ndoverlay) + for k, sp in plot.subplots.items(): + artist = sp.handles['artist'] + color = artist.get_color() + if ndoverlay[k].iloc[0, 2] == 0: + self.assertEqual(color, 'red') + else: + self.assertEqual(color, 'blue') + linestyle = artist.get_linestyle() + if ndoverlay[k].iloc[0, 3] == 'A': + self.assertEqual(linestyle, '-.') + else: + self.assertEqual(linestyle, '-')