Repository: holoviz/holoviews

Issue:
Matplotlib Scatter3D & Path3D cannot plot with colorbar
#### ALL software version info
Holoviews: 1.14.1
Matplotlib: 3.3.3
Python: 3.6
(Plotly: 4.14.3)

#### Description of expected behavior and the observed behavior

The code below (a minor variation on the [reference](http://holoviews.org/reference/elements/matplotlib/Scatter3D.html) [examples](http://holoviews.org/reference/elements/matplotlib/Path3D.html)) raises the exception

> TypeError: Dimension lookup supports int, string, and Dimension instances, cannot lookup Dimensions using dim type.

This seems related to the following line in `Plot3D`: https://github.com/holoviz/holoviews/blob/8e137c05db93844439144321e27ce8ab737cc9ba/holoviews/plotting/mpl/chart3d.py#L104
My guess is that 3D plots use transformed dimensions, which aren't instances of  `Dimension`.

Since the same code works with Plotly, this seems Matplotlib specific.

#### Complete, minimal, self-contained example code that reproduces the issue

Scatter3D:
```
import numpy as np
import holoviews as hv
hv.extension('matplotlib')

y,x = np.mgrid[-5:5, -5:5] * 0.1
heights = np.sin(x**2+y**2)
hv.Scatter3D((x.flat, y.flat, heights.flat)).opts(
    hv.opts.Scatter3D(color='z', cmap='fire', colorbar=True)
)
```

Path3D:
```
line = np.random.randn(500, 3).cumsum(axis=0)
hv.Path3D(line).opts(color='z', colorbar=True)
```

#### Stack traceback
```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
~/usr/venv/comp/lib64/python3.6/site-packages/IPython/core/formatters.py in __call__(self, obj, include, exclude)
    968 
    969             if method is not None:
--> 970                 return method(include=include, exclude=exclude)
    971             return None
    972         else:

[…]

~/usr/venv/comp/lib64/python3.6/site-packages/holoviews/plotting/mpl/chart3d.py in _finalize_axis(self, key, **kwargs)
     85         else:
     86             axis.set_facecolor(self.bgcolor)
---> 87         return super(Plot3D, self)._finalize_axis(key, **kwargs)
     88 
     89 

~/usr/venv/comp/lib64/python3.6/site-packages/holoviews/plotting/mpl/element.py in _finalize_axis(self, key, element, title, dimensions, ranges, xticks, yticks, zticks, xlabel, ylabel, zlabel)
    190 
    191         if not subplots and not self.drawn:
--> 192             self._finalize_artist(element)
    193 
    194         self._execute_hooks(element)

~/usr/venv/comp/lib64/python3.6/site-packages/holoviews/plotting/mpl/element.py in _finalize_artist(self, element)
    771             dims = [h for k, h in self.handles.items() if k.endswith('color_dim')]
    772             for d in dims:
--> 773                 self._draw_colorbar(element, d)
    774 
    775 

~/usr/venv/comp/lib64/python3.6/site-packages/holoviews/plotting/mpl/chart3d.py in _draw_colorbar(self, element, dim, redraw)
    102                 dim = element.get_dimension(2)
    103         elif not isinstance(dim, Dimension):
--> 104             dim = element.get_dimension(dim)
    105         label = dim.pprint_label
    106         cbar = fig.colorbar(artist, shrink=0.7, ax=ax)

~/usr/venv/comp/lib64/python3.6/site-packages/holoviews/core/dimension.py in get_dimension(self, dimension, default, strict)
    961             raise TypeError('Dimension lookup supports int, string, '
    962                             'and Dimension instances, cannot lookup '
--> 963                             'Dimensions using %s type.' % type(dimension).__name__)
    964         all_dims = self.dimensions()
    965         if isinstance(dimension, int):

TypeError: Dimension lookup supports int, string, and Dimension instances, cannot lookup Dimensions using dim type.
```



Discussion:

Resolving patch:
diff --git a/holoviews/plotting/mpl/chart3d.py b/holoviews/plotting/mpl/chart3d.py
--- a/holoviews/plotting/mpl/chart3d.py
+++ b/holoviews/plotting/mpl/chart3d.py
@@ -5,6 +5,7 @@
 
 from ...core import Dimension
 from ...core.options import abbreviated_exception
+from ...util.transform import dim as dim_expr
 from ..util import map_colors
 from .element import ColorbarPlot
 from .chart import PointPlot
@@ -91,6 +92,8 @@ def _draw_colorbar(self, element=None, dim=None, redraw=True):
         fig = self.handles['fig']
         ax = self.handles['axis']
         # Get colorbar label
+        if isinstance(dim, dim_expr):
+            dim = dim.dimension
         if dim is None:
             if hasattr(self, 'color_index'):
                 dim = element.get_dimension(self.color_index)
@@ -100,6 +103,7 @@ def _draw_colorbar(self, element=None, dim=None, redraw=True):
             dim = element.get_dimension(dim)
         label = dim.pprint_label
         cbar = fig.colorbar(artist, shrink=0.7, ax=ax)
+        self.handles['cbar'] = cbar
         self.handles['cax'] = cbar.ax
         self._adjust_cbar(cbar, label, dim)
 


Test patch:
diff --git a/holoviews/tests/plotting/matplotlib/test_scatter3d.py b/holoviews/tests/plotting/matplotlib/test_scatter3d.py
--- a/holoviews/tests/plotting/matplotlib/test_scatter3d.py
+++ b/holoviews/tests/plotting/matplotlib/test_scatter3d.py
@@ -5,6 +5,11 @@
 
 class TestPointPlot(TestMPLPlot):
 
+    def test_scatter3d_colorbar_label(self):
+        scatter3d = Scatter3D([(0, 1, 2), (1, 2, 3), (2, 3, 4)]).options(color='z', colorbar=True)
+        plot = mpl_renderer.get_plot(scatter3d)
+        assert plot.handles['cax'].get_ylabel() == 'z'
+
     def test_scatter3d_padding_square(self):
         scatter3d = Scatter3D([(0, 1, 2), (1, 2, 3), (2, 3, 4)]).options(padding=0.1)
         plot = mpl_renderer.get_plot(scatter3d)