Custom Discrete Colormaps

Hello,

I am looking to create custom colormap steps. eg what .opts(color_levels=levels) does pretty much but rather than splitting on the levels I want each level to be equal.

For example I might have a colorbar that is equally spaced every 200m between 0 and 1000 then spaced every 1000 between 1000 and 6000.

I found this website that explains how to create a custom map which I have been able to apply with satisfactory results HOWEVER my colorbar is still normally scaled is there a way to “fix” this? Also I fancy something could be passed to holoviews.plotting.util.color_intervals to allow this sort of thing to be done automatically.

Basic result using color_levels:

Current Result using code from link above:

Desired Result (notice colorbar):

I don’t think this is possible right now. I’d suggest you chime in here.

I may have a workaround but I don’t know if it will work with your plot.

    import pandas as pd
    import numpy as np
    import holoviews as hv
    from holoviews import opts
    from bokeh.palettes import all_palettes
    from bokeh.models import FuncTickFormatter
    from bokeh.plotting import show

    df = pd.DataFrame(columns=['x','y','val','bin'])
    df['x'] = list(range(10))
    df['y'] = list(range(10))
    df['val'] = np.random.randint(110,size=10)
    df['bin'] = (df.val/10).astype(int)
    plot = hv.Scatter(df,kdims=['x'],vdims=['y','bin']).opts(color='bin',colorbar=True,tools=['hover'],color_levels=11)
    plot

    def custom_colorbar(plot,palette,labels=None):
        plot = plot.opts(color_levels=10)
        min_ = 0
        nb_color = len(palette)
        max_=2*nb_color
        rend = hv.render(plot)
        cmapper = rend.right[0].color_mapper
        ticker = rend.right[0].ticker
        cmapper.high = nb_color
        cmapper.palette = palette
        ticker.desired_num_ticks = nb_color+1
        ticker.num_minor_ticks = 0
        step = int((max_-min_)/nb_color)
        ticker.base=2
        ticker.mantissas = list(range(min_,max_+step,step))
        if labels is not None and len(labels) == nb_color+1:
            Xlabel = dict(zip(list(range(nb_color+1)),labels))
            formatter = FuncTickFormatter(code="""
                        var labels = %s;
                        return labels[tick] || tick;
                        """ % Xlabel)
            rend.right[0].formatter = formatter
        return rend

    labels = ['0', '200', '400', '600','800', '1000', '2000','3000','4000','5000','6000']
    new_rend = custom_colorbar(plot,all_palettes['BrBG'][10],labels)
    show(new_rend)

Before
bokeh_plot

After
bokeh_plot2

The plot generated by the function is no longer a Holoview plot, now it’s a bokeh figure.
And before using this function you need to add a column in your datasource in which you will map your value with the color levels (0 to 10 in your case).

You could use hooks to keep it as holoviews object.