def colorspec(str, data):
    from matplotlib import cm
    from matplotlib.colors import Normalize
    boundaries = []
    cmaps = []
    vmin = None
    vmax = None
    xs = str.split(' ')
    try:
        vmin = float(xs[0])
        xs.pop(0)
    except:
        pass
    while len(xs) > 1:
        cmaps.append(cm.get_cmap(xs.pop(0)))
        boundaries.append(float(xs.pop(0)))

    if xs:
        cmaps.append(cm.get_cmap(xs[0]))
    else:
        vmax = boundaries.pop()

    norm = Normalize(vmin=vmin, vmax=vmax)
    norm.autoscale_None(data)

    return norm, merge_cmaps(map(norm, boundaries), cmaps)
def plot_morphology(morphology, plot_3d=None, show_compartments=False,
                    show_diameter=False, colors=('darkblue', 'darkred'),
                    values=None, value_norm=(None, None), value_colormap='hot',
                    value_colorbar=True, value_unit=None, axes=None):
    '''
    Plot a given `~brian2.spatialneuron.morphology.Morphology` in 2D or 3D.

    Parameters
    ----------
    morphology : `~brian2.spatialneuron.morphology.Morphology`
        The morphology to plot
    plot_3d : bool, optional
        Whether to plot the morphology in 3D or in 2D. If not set (the default)
        a morphology where all z values are 0 is plotted in 2D, otherwise it is
        plot in 3D.
    show_compartments : bool, optional
        Whether to plot a dot at the center of each compartment. Defaults to
        ``False``.
    show_diameter : bool, optional
        Whether to plot the compartments with the diameter given in the
        morphology. Defaults to ``False``.
    colors : sequence of color specifications
        A list of colors that is cycled through for each new section. Can be
        any color specification that matplotlib understands (e.g. a string such
        as ``'darkblue'`` or a tuple such as `(0, 0.7, 0)`.
    values : ~brian2.units.fundamentalunits.Quantity, optional
        Values to fill compartment patches with a color that corresponds to
        their given value.
    value_norm : tuple or callable, optional
        Normalization function to scale the displayed values. Can be a tuple
        of a minimum and a maximum value (where either of them can be ``None``
        to denote taking the minimum/maximum from the data) or a function that
        takes a value and returns the scaled value (e.g. as returned by
        `.matplotlib.colors.PowerNorm`). For a tuple of values, will use
        `.matplotlib.colors.Normalize```(vmin, vmax, clip=True)``` with the
        given ``(vmin, vmax)`` values.
    value_colormap : str or matplotlib.colors.Colormap, optional
        Desired colormap for plots. Either the name of a standard colormap
        or a `.matplotlib.colors.Colormap` instance. Defaults to ``'hot'``.
        Note that this uses ``matplotlib`` color maps even for 3D plots with
        Mayavi.
    value_colorbar : bool or dict, optional
        Whether to add a colorbar for the ``values``. Defaults to ``True``,
        but will be ignored if no ``values`` are provided. Can also be a
        dictionary with the keyword arguments for matplotlib's
        `~.matplotlib.figure.Figure.colorbar` method (2D plot), or for
        Mayavi's `~.mayavi.mlab.scalarbar` method (3D plot).
    value_unit : `Unit`, optional
        A `Unit` to rescale the values for display in the colorbar. Does not
        have any visible effect if no colorbar is used. If not specified, will
        try to determine the "best unit" to itself.
    axes : `~matplotlib.axes.Axes` or `~mayavi.core.api.Scene`, optional
        A matplotlib `~matplotlib.axes.Axes` (for 2D plots) or mayavi
        `~mayavi.core.api.Scene` ( for 3D plots) instance, where the plot will
        be added.

    Returns
    -------
    axes : `~matplotlib.axes.Axes` or `~mayavi.core.api.Scene`
        The `~matplotlib.axes.Axes` or `~mayavi.core.api.Scene` instance that
        was used for plotting. This object allows to modify the plot further,
        e.g. by setting the plotted range, the axis labels, the plot title, etc.
    '''
    # Avoid circular import issues
    from brian2tools.plotting.base import (_setup_axes_matplotlib,
                                           _setup_axes_mayavi)

    if plot_3d is None:
        # Decide whether to use 2d or 3d plotting based on the coordinates
        flat_morphology = FlatMorphology(morphology)
        plot_3d = any(np.abs(flat_morphology.z) > 1e-12)

    if values is not None:
        if hasattr(values, 'name'):
            value_varname = values.name
        else:
            value_varname = 'values'
        if value_unit is not None:
            if not isinstance(value_unit, Unit):
                raise TypeError(f'\'value_unit\' has to be a unit but is'
                                f'\'{type(value_unit)}\'.')
            fail_for_dimension_mismatch(value_unit, values,
                                        'The \'value_unit\' arguments needs '
                                        'to have the same dimensions as '
                                        'the \'values\'.')
        else:
            if have_same_dimensions(values, DIMENSIONLESS):
                value_unit = 1.
            else:
                value_unit = values[:].get_best_unit()
        orig_values = values
        values = values/value_unit
        if isinstance(value_norm, tuple):
            if not len(value_norm) == 2:
                raise TypeError('Need a (vmin, vmax) tuple for the value '
                                'normalization, but got a tuple of length '
                                f'{len(value_norm)}.')
            vmin, vmax = value_norm
            if vmin is not None:
                err_msg = ('The minimum value in \'value_norm\' needs to '
                           'have the same units as \'values\'.')
                fail_for_dimension_mismatch(vmin, orig_values,
                                            error_message=err_msg)
                vmin /= value_unit
            if vmax is not None:
                err_msg = ('The maximum value in \'value_norm\' needs to '
                           'have the same units as \'values\'.')
                fail_for_dimension_mismatch(vmax, orig_values,
                                            error_message=err_msg)
                vmax /= value_unit
            if plot_3d:
                value_norm = (vmin, vmax)
            else:
                value_norm = Normalize(vmin=vmin, vmax=vmax, clip=True)
                value_norm.autoscale_None(values)
        elif plot_3d:
            raise TypeError('3d plots only support normalizations given by '
                            'a (min, max) tuple.')
        value_colormap = plt.get_cmap(value_colormap)

    if plot_3d:
        try:
            import mayavi.mlab as mayavi
        except ImportError:
            raise ImportError('3D plotting needs the mayavi library')
        axes = _setup_axes_mayavi(axes)
        axes.scene.disable_render = True
        surf = _plot_morphology3D(morphology, axes, colors=colors,
                                  values=values, value_norm=value_norm,
                                  value_colormap=value_colormap,
                                  show_diameters=show_diameter,
                                  show_compartments=show_compartments)
        if values is not None and value_colorbar:
            if not isinstance(value_colorbar, Mapping):
                value_colorbar = {}
                if not have_same_dimensions(value_unit, DIMENSIONLESS):
                    unit_str = f' ({value_unit!s})'
                else:
                    unit_str = ''
                if value_varname:
                    value_colorbar['title'] = f'{value_varname}{unit_str}'
            cb = mayavi.scalarbar(surf, **value_colorbar)
            # Make text dark gray
            cb.title_text_property.color = (0.1, 0.1, 0.1)
            cb.label_text_property.color = (0.1, 0.1, 0.1)
        axes.scene.disable_render = False
    else:
        axes = _setup_axes_matplotlib(axes)

        _plot_morphology2D(morphology, axes, colors,
                           values, value_norm, value_colormap,
                           show_compartments=show_compartments,
                           show_diameter=show_diameter)
        axes.set_xlabel('x (um)')
        axes.set_ylabel('y (um)')
        axes.set_aspect('equal')
        if values is not None and value_colorbar:
            divider = make_axes_locatable(axes)
            cax = divider.append_axes("right", size="5%", pad=0.1)
            mappable = ScalarMappable(norm=value_norm, cmap=value_colormap)
            mappable.set_array([])
            fig = axes.get_figure()
            if not isinstance(value_colorbar, Mapping):
                value_colorbar = {}
                if not have_same_dimensions(value_unit, DIMENSIONLESS):
                    unit_str = f' ({value_unit!s})'
                else:
                    unit_str = ''
                if value_varname:
                    value_colorbar['label'] = f'{value_varname}{unit_str}'
            fig.colorbar(mappable, cax=cax, **value_colorbar)
    return axes