From f8a0767e096ef75b94ee5c10329eaa33b53a9467 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sat, 23 Sep 2017 19:57:05 +0100
Subject: [PATCH 1/6] Implement axis inversions for matplotlib annotations
---
holoviews/plotting/mpl/annotation.py | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/holoviews/plotting/mpl/annotation.py b/holoviews/plotting/mpl/annotation.py
index b169ca59f6..92e41256b2 100644
--- a/holoviews/plotting/mpl/annotation.py
+++ b/holoviews/plotting/mpl/annotation.py
@@ -45,7 +45,10 @@ class VLinePlot(AnnotationPlot):
style_opts = ['alpha', 'color', 'linewidth', 'linestyle', 'visible']
def draw_annotation(self, axis, position, opts):
- return [axis.axvline(position, **opts)]
+ if self.invert_axes:
+ return [axis.axhline(position, **opts)]
+ else:
+ return [axis.axvline(position, **opts)]
@@ -56,7 +59,10 @@ class HLinePlot(AnnotationPlot):
def draw_annotation(self, axis, position, opts):
"Draw a horizontal line on the axis"
- return [axis.axhline(position, **opts)]
+ if self.invert_axes:
+ return [axis.axvline(position, **opts)]
+ else:
+ return [axis.axhline(position, **opts)]
class TextPlot(AnnotationPlot):
@@ -67,6 +73,7 @@ class TextPlot(AnnotationPlot):
def draw_annotation(self, axis, data, opts):
(x,y, text, fontsize,
horizontalalignment, verticalalignment, rotation) = data
+ if self.invert_axes: x, y = y, x
opts['fontsize'] = fontsize
return [axis.text(x,y, text,
horizontalalignment = horizontalalignment,
@@ -85,6 +92,7 @@ class ArrowPlot(AnnotationPlot):
def draw_annotation(self, axis, data, opts):
x, y, text, direction, points, arrowstyle = data
+ if self.invert_axes: x, y = y, x
direction = direction.lower()
arrowprops = dict({'arrowstyle':arrowstyle},
**{k: opts[k] for k in self._arrow_style_opts if k in opts})
From 245ae07003bcf24252fd31dd0648899010bb6941 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sat, 23 Sep 2017 20:16:24 +0100
Subject: [PATCH 2/6] Ensure VectorField y-values are inverted
---
holoviews/plotting/mpl/chart.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py
index d24676b818..46c547434f 100644
--- a/holoviews/plotting/mpl/chart.py
+++ b/holoviews/plotting/mpl/chart.py
@@ -621,7 +621,8 @@ def get_data(self, element, ranges, style):
xs = element.dimension_values(xidx) if len(element.data) else []
ys = element.dimension_values(yidx) if len(element.data) else []
radians = element.dimension_values(2) if len(element.data) else []
- angles = list(np.rad2deg(radians))
+ if self.invert_axes: radians = radians+np.pi/2.
+ angles = list(np.rad2deg(radians))
if self.rescale_lengths:
input_scale = input_scale / self._min_dist
From 6050bdea13c3b6669b3e30534a7edde2e4ed5ccb Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sat, 23 Sep 2017 20:16:45 +0100
Subject: [PATCH 3/6] Ensure raster types invert_axes works
---
holoviews/plotting/mpl/raster.py | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/holoviews/plotting/mpl/raster.py b/holoviews/plotting/mpl/raster.py
index fa4997a3f4..81ec7360d3 100644
--- a/holoviews/plotting/mpl/raster.py
+++ b/holoviews/plotting/mpl/raster.py
@@ -65,6 +65,10 @@ def get_data(self, element, ranges, style):
l, b, r, t = element.bounds.lbrt()
data = get_raster_array(element)
+ if self.invert_axes:
+ data = data[::-1]
+ data = data.transpose([1, 0, 2]) if isinstance(element, RGB) else data.T
+ l, b, r, t = b, l, t, r
vdim = element.vdims[0]
self._norm_kwargs(element, ranges, style, vdim)
style['extent'] = [l, r, b, t]
@@ -151,6 +155,7 @@ def get_data(self, element, ranges, style):
data = np.flipud(element.gridded.dimension_values(2, flat=False))
data = np.ma.array(data, mask=np.logical_not(np.isfinite(data)))
+ if self.invert_axes: data = data.T
shape = data.shape
style['aspect'] = shape[0]/shape[1]
style['extent'] = (0, shape[1], 0, shape[0])
@@ -187,9 +192,12 @@ class ImagePlot(RasterPlot):
def get_data(self, element, ranges, style):
data = np.flipud(element.dimension_values(2, flat=False))
data = np.ma.array(data, mask=np.logical_not(np.isfinite(data)))
+ l, b, r, t = element.bounds.lbrt()
+ if self.invert_axes:
+ data = data[::-1].T
+ l, b, r, t = b, l, t, r
vdim = element.vdims[0]
self._norm_kwargs(element, ranges, style, vdim)
- l, b, r, t = element.bounds.lbrt()
style['extent'] = [l, r, b, t]
return (data,), style, {}
@@ -211,7 +219,11 @@ class QuadMeshPlot(ColorbarPlot):
def get_data(self, element, ranges, style):
data = np.ma.array(element.data[2],
mask=np.logical_not(np.isfinite(element.data[2])))
- cmesh_data = list(element.data[:2]) + [data]
+ coords = list(element.data[:2])
+ if self.invert_axes:
+ coords = coords[::-1]
+ data = data.T
+ cmesh_data = coords + [data]
style['locs'] = np.concatenate(element.data[:2])
vdim = element.vdims[0]
self._norm_kwargs(element, ranges, style, vdim)
From e77ef1ce93c9a23ca4ad87dccfd5e97a87c978bf Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sun, 24 Sep 2017 01:27:37 +0100
Subject: [PATCH 4/6] Made invert_axes consistent in bokeh
---
holoviews/plotting/bokeh/annotation.py | 7 +++--
holoviews/plotting/bokeh/chart.py | 17 ++++++----
holoviews/plotting/bokeh/raster.py | 43 +++++++++++++++++++-------
3 files changed, 46 insertions(+), 21 deletions(-)
diff --git a/holoviews/plotting/bokeh/annotation.py b/holoviews/plotting/bokeh/annotation.py
index f84bc5ad6c..1ca7b36220 100644
--- a/holoviews/plotting/bokeh/annotation.py
+++ b/holoviews/plotting/bokeh/annotation.py
@@ -42,7 +42,6 @@ def get_data(self, element, ranges=None, empty=False):
data['text'] = [element.text]
return (data, mapping)
-
def get_batched_data(self, element, ranges=None, empty=False):
data = defaultdict(list)
for key, el in element.data.items():
@@ -51,7 +50,6 @@ def get_batched_data(self, element, ranges=None, empty=False):
data[k].extend(eld)
return data, elmapping
-
def get_extents(self, element, ranges=None):
return None, None, None, None
@@ -100,7 +98,10 @@ class SplinePlot(ElementPlot):
_plot_methods = dict(single='bezier')
def get_data(self, element, ranges=None, empty=False):
- data_attrs = ['x0', 'y0', 'cx0', 'cy0', 'cx1', 'cy1', 'x1', 'y1',]
+ if self.invert_axes:
+ data_attrs = ['y0', 'x0', 'cy0', 'cx0', 'cy1', 'cx1', 'y1', 'x1']
+ else:
+ data_attrs = ['x0', 'y0', 'cx0', 'cy0', 'cx1', 'cy1', 'x1', 'y1']
verts = np.array(element.data[0])
inds = np.where(np.array(element.data[1])==1)[0]
data = {da: [] for da in data_attrs}
diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py
index d85129d47c..5a87056e20 100644
--- a/holoviews/plotting/bokeh/chart.py
+++ b/holoviews/plotting/bokeh/chart.py
@@ -183,8 +183,12 @@ def get_data(self, element, ranges=None, empty=False):
input_scale = style.pop('scale', 1.0)
# Get x, y, angle, magnitude and color data
- xidx, yidx = (1, 0) if self.invert_axes else (0, 1)
rads = element.dimension_values(2)
+ if self.invert_axes:
+ xidx, yidx = (1, 0)
+ rads = rads+1.5*np.pi
+ else:
+ xidx, yidx = (0, 1)
lens = self._get_lengths(element, ranges)/input_scale
cdim = element.get_dimension(self.color_index)
cdata, cmapping = self._get_color_data(element, ranges, style,
@@ -516,6 +520,9 @@ def get_extents(self, element, ranges):
# from position and length plot options
for el in self.current_frame.values():
opts = self.lookup_options(el, 'plot').options
+ frame = self.current_frame or self.hmap.last
+ for el in frame.values():
+ opts = self.lookup_options(element, 'plot').options
pos = opts.get('position', self.position)
length = opts.get('spike_length', self.spike_length)
bs.append(pos)
@@ -950,7 +957,7 @@ def _get_factors(self, element):
Get factors for categorical axes.
"""
if not element.kdims:
- return [element.label], []
+ xfactors, yfactors = [element.label], []
else:
if bokeh_version < '0.12.7':
factors = [', '.join([d.pprint_value(v).replace(':', ';')
@@ -960,10 +967,8 @@ def _get_factors(self, element):
factors = [tuple(d.pprint_value(v) for d, v in zip(element.kdims, key))
for key in element.groupby(element.kdims).data.keys()]
factors = [f[0] if len(f) == 1 else f for f in factors]
- if self.invert_axes:
- return [], factors
- else:
- return factors, []
+ xfactors, yfactors = factors, []
+ return (yfactors, xfactors) if self.invert_axes else (xfactors, yfactors)
def get_data(self, element, ranges=None, empty=False):
if element.kdims:
diff --git a/holoviews/plotting/bokeh/raster.py b/holoviews/plotting/bokeh/raster.py
index ab6f631846..1ca387139e 100644
--- a/holoviews/plotting/bokeh/raster.py
+++ b/holoviews/plotting/bokeh/raster.py
@@ -43,6 +43,12 @@ def get_data(self, element, ranges=None, empty=False):
img = img[::-1]
dh, dw = t-b, r-l
+ if self.invert_axes:
+ dh, dw, l, b = dw, dh, b, l
+ if self.invert_yaxis: l = t
+ if self.invert_yaxis: b = r
+ img = np.rot90(img)
+
mapping = dict(image='image', x='x', y='y', dw='dw', dh='dh')
if empty:
data = dict(image=[], x=[], y=[], dw=[], dh=[])
@@ -93,6 +99,12 @@ def get_data(self, element, ranges=None, empty=False):
img = img[::-1]
dh, dw = t-b, r-l
+ if self.invert_axes:
+ dh, dw, l, b = dw, dh, b, l
+ if self.invert_yaxis: l = t
+ if self.invert_yaxis: b = r
+ img = np.rot90(img)
+
mapping = dict(image='image', x='x', y='y', dw='dw', dh='dh')
if empty:
data = dict(image=[], x=[], y=[], dw=[], dh=[])
@@ -138,16 +150,18 @@ def get_data(self, element, ranges=None, empty=False):
aggregate = element.gridded
style = self.style[self.cyclic_index]
cmapper = self._get_colormapper(element.vdims[0], element, ranges, style)
- if empty:
- data = {x: [], y: [], z: []}
- else:
- xdim, ydim = aggregate.dimensions()[:2]
- xvals, yvals, zvals = (aggregate.dimension_values(i) for i in range(3))
- if xvals.dtype.kind not in 'SU':
- xvals = [xdim.pprint_value(xv) for xv in xvals]
- if yvals.dtype.kind not in 'SU':
- yvals = [ydim.pprint_value(yv) for yv in yvals]
- data = {x: xvals, y: yvals, 'zvalues': zvals}
+
+ xdim, ydim = aggregate.dimensions()[:2]
+ xvals, yvals, zvals = (aggregate.dimension_values(i) for i in range(3))
+ if xvals.dtype.kind not in 'SU':
+ xvals = [xdim.pprint_value(xv) for xv in xvals]
+ if yvals.dtype.kind not in 'SU':
+ yvals = [ydim.pprint_value(yv) for yv in yvals]
+
+ if self.invert_axes:
+ x, y = y, x
+ xvals, yvals = yvals, xvals
+ data = {x: xvals, y: yvals, 'zvalues': zvals}
if any(isinstance(t, HoverTool) for t in self.state.tools):
for vdim in element.vdims:
@@ -168,18 +182,23 @@ class QuadMeshPlot(ColorbarPlot):
def get_data(self, element, ranges=None, empty=False):
x, y, z = element.dimensions(label=True)
+ if self.invert_axes: x, y = y, x
style = self.style[self.cyclic_index]
cmapper = self._get_colormapper(element.vdims[0], element, ranges, style)
if empty:
- data = {x: [], y: [], z: [], 'height': [], 'width': []}
+ xs, ys, zvals, ws, hs = []*5
else:
if len(set(v.shape for v in element.data)) == 1:
raise SkipRendering("Bokeh QuadMeshPlot only supports rectangular meshes")
- zvals = element.data[2].T.flatten()
xvals = element.dimension_values(0, False)
yvals = element.dimension_values(1, False)
widths = np.diff(element.data[0])
heights = np.diff(element.data[1])
+ if self.invert_axes:
+ zvals = element.data[2].flatten()
+ xvals, yvals, widths, heights = yvals, xvals, heights, widths
+ else:
+ zvals = element.data[2].T.flatten()
xs, ys = cartesian_product([xvals, yvals], copy=True)
ws, hs = cartesian_product([widths, heights], copy=True)
data = {x: xs, y: ys, z: zvals, 'widths': ws, 'heights': hs}
From 1040fe85debb6e3c0eb291303207eea02f0d39eb Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sun, 24 Sep 2017 01:27:58 +0100
Subject: [PATCH 5/6] Fixed VectorField invert_axes
---
holoviews/plotting/mpl/chart.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/holoviews/plotting/mpl/chart.py b/holoviews/plotting/mpl/chart.py
index 46c547434f..aad85b89de 100644
--- a/holoviews/plotting/mpl/chart.py
+++ b/holoviews/plotting/mpl/chart.py
@@ -621,7 +621,7 @@ def get_data(self, element, ranges, style):
xs = element.dimension_values(xidx) if len(element.data) else []
ys = element.dimension_values(yidx) if len(element.data) else []
radians = element.dimension_values(2) if len(element.data) else []
- if self.invert_axes: radians = radians+np.pi/2.
+ if self.invert_axes: radians = radians+1.5*np.pi
angles = list(np.rad2deg(radians))
if self.rescale_lengths:
input_scale = input_scale / self._min_dist
From 9ef9c641cda35a5da3e8d4db11873fd1c0eb9a71 Mon Sep 17 00:00:00 2001
From: Philipp Rudiger
Date: Sun, 24 Sep 2017 13:49:21 +0100
Subject: [PATCH 6/6] Reverted change to bokeh SpikesPlot
---
holoviews/plotting/bokeh/chart.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/holoviews/plotting/bokeh/chart.py b/holoviews/plotting/bokeh/chart.py
index 5a87056e20..d089df0d3c 100644
--- a/holoviews/plotting/bokeh/chart.py
+++ b/holoviews/plotting/bokeh/chart.py
@@ -520,9 +520,6 @@ def get_extents(self, element, ranges):
# from position and length plot options
for el in self.current_frame.values():
opts = self.lookup_options(el, 'plot').options
- frame = self.current_frame or self.hmap.last
- for el in frame.values():
- opts = self.lookup_options(element, 'plot').options
pos = opts.get('position', self.position)
length = opts.get('spike_length', self.spike_length)
bs.append(pos)