Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Single, unified colorbar for layout #4237

Open
ahuang11 opened this issue Feb 25, 2020 · 12 comments
Open

Single, unified colorbar for layout #4237

ahuang11 opened this issue Feb 25, 2020 · 12 comments

Comments

@ahuang11
Copy link
Collaborator

Is there a way to set a unified colorbar for a layout?

import xarray as xr
import hvplot.xarray
ds = xr.tutorial.open_dataset('air_temperature').isel(time=[0, 4, 7, 8])
ds.hvplot('lon', 'lat').layout().cols(2)
@jbednar
Copy link
Member

jbednar commented Feb 28, 2020

That code doesn't run for me on master with xarray 0.13 or 0.15; I get AttributeError: 'DataFrame' object has no attribute 'data_vars'.

@ahuang11
Copy link
Collaborator Author

I think it works for me with latest master and xarray==0.15
image

@poplarShift
Copy link
Collaborator

Does this not "just" amount to the fact that bokeh does not permit creating legends/colorbars on its own, i.e. not attached to a particular axis?

@jbednar
Copy link
Member

jbednar commented Mar 3, 2020

Even so, you can still disable the colorbar on all but one plot, which works but is clunky:

import xarray as xr
import hvplot.xarray
ds = xr.tutorial.open_dataset('air_temperature').isel(time=[0, 4, 7, 8])
l = ds.hvplot('lon', 'lat', colorbar=False).layout()
l.values()[-1].opts(colorbar=True)
l.cols(2)

image

(I got the code to run locally by deleting my ~/.xarray_tutorial_data/air_temperature.nc file, which must have been cached from a long-ago xarray version; the current version xarray downloads works fine.)

It would be nice to have a less-clunky way to do this, and also to ensure that all plots actually do respect that single shared colorbar.

@poplarShift
Copy link
Collaborator

Yes, though it only gets really clunky if you want a colorbar that stretches over, let's say, the entire height of the subplot grid, instead of being attached to only one of the subplots. And I'm not sure it's even doable in current bokeh without creating a fifth panel and then hiding the axes.

@ahuang11
Copy link
Collaborator Author

ahuang11 commented Jul 1, 2024

To answer this question https://discourse.holoviz.org/t/shared-colorbar-for-all-heatmap-subplots/448/2

With Panel, it's possible with a lot of opts :)

import xarray as xr
import hvplot.xarray
import panel as pn
ds = xr.tutorial.open_dataset('air_temperature').isel(time=[0, 4, 7, 8])
l = ds.hvplot('lon', 'lat', colorbar=False).layout()
plots = l.cols(2)

shared_colorbar = l.values()[0].clone().opts(
    colorbar=True,
    frame_width=0,
    frame_height=500,
    show_frame=False,
    shared_axes=False,
    xaxis=None,
    yaxis=None,
    toolbar=None,
    colorbar_opts={"width": 50, "height": 400, "title": "Temperature (°C)"},
)
# colorbar_opts: background_fill_alpha, background_fill_color, bar_line_alpha, bar_line_cap, bar_line_color, bar_line_dash, bar_line_dash_offset, bar_line_join, bar_line_width, border_line_alpha, border_line_cap, border_line_color, border_line_dash, border_line_dash_offset, border_line_join, border_line_width, color_mapper, context_menu, coordinates, display_high, display_low, elements, formatter, group, height, js_event_callbacks, js_property_callbacks, label_standoff, level, location, major_label_overrides, major_label_policy, major_label_text_align, major_label_text_alpha, major_label_text_baseline, major_label_text_color, major_label_text_font, major_label_text_font_size, major_label_text_font_style, major_label_text_line_height, major_label_text_outline_color, major_tick_in, major_tick_line_alpha, major_tick_line_cap, major_tick_line_color, major_tick_line_dash, major_tick_line_dash_offset, major_tick_line_join, major_tick_line_width, major_tick_out, margin, minor_tick_in, minor_tick_line_alpha, minor_tick_line_cap, minor_tick_line_color, minor_tick_line_dash, minor_tick_line_dash_offset, minor_tick_line_join, minor_tick_line_width, minor_tick_out, name, orientation, padding, propagate_hover, renderers, scale_alpha, subscribed_events, syncable, tags, ticker, title, title_standoff, title_text_align, title_text_alpha, title_text_baseline, title_text_color, title_text_font, title_text_font_size, title_text_font_style, title_text_line_height, title_text_outline_color, visible, width, x_range_name or y_range_name

pn.Row(plots, shared_colorbar, align="center")
image

@iuryt
Copy link

iuryt commented Jul 1, 2024

To answer this question https://discourse.holoviz.org/t/shared-colorbar-for-all-heatmap-subplots/448/2

With Panel, it's possible with a lot of opts :)

import xarray as xr
import hvplot.xarray
import panel as pn
ds = xr.tutorial.open_dataset('air_temperature').isel(time=[0, 4, 7, 8])
l = ds.hvplot('lon', 'lat', colorbar=False).layout()
plots = l.cols(2)

shared_colorbar = l.values()[0].clone().opts(
    colorbar=True,
    frame_width=0,
    frame_height=500,
    show_frame=False,
    shared_axes=False,
    xaxis=None,
    yaxis=None,
    toolbar=None,
    colorbar_opts={"width": 50, "height": 400, "title": "Temperature (°C)"},
)
# colorbar_opts: background_fill_alpha, background_fill_color, bar_line_alpha, bar_line_cap, bar_line_color, bar_line_dash, bar_line_dash_offset, bar_line_join, bar_line_width, border_line_alpha, border_line_cap, border_line_color, border_line_dash, border_line_dash_offset, border_line_join, border_line_width, color_mapper, context_menu, coordinates, display_high, display_low, elements, formatter, group, height, js_event_callbacks, js_property_callbacks, label_standoff, level, location, major_label_overrides, major_label_policy, major_label_text_align, major_label_text_alpha, major_label_text_baseline, major_label_text_color, major_label_text_font, major_label_text_font_size, major_label_text_font_style, major_label_text_line_height, major_label_text_outline_color, major_tick_in, major_tick_line_alpha, major_tick_line_cap, major_tick_line_color, major_tick_line_dash, major_tick_line_dash_offset, major_tick_line_join, major_tick_line_width, major_tick_out, margin, minor_tick_in, minor_tick_line_alpha, minor_tick_line_cap, minor_tick_line_color, minor_tick_line_dash, minor_tick_line_dash_offset, minor_tick_line_join, minor_tick_line_width, minor_tick_out, name, orientation, padding, propagate_hover, renderers, scale_alpha, subscribed_events, syncable, tags, ticker, title, title_standoff, title_text_align, title_text_alpha, title_text_baseline, title_text_color, title_text_font, title_text_font_size, title_text_font_style, title_text_line_height, title_text_outline_color, visible, width, x_range_name or y_range_name

pn.Row(plots, shared_colorbar, align="center")
image

What if we have tiles?
Is there a way to create a panel for the colorbar instead of copying from the layout?

@ahuang11
Copy link
Collaborator Author

ahuang11 commented Jul 1, 2024

What tiles? Can you share an example?

@iuryt
Copy link

iuryt commented Jul 1, 2024

Sorry..

I meant

import xarray as xr
import hvplot.xarray
import panel as pn

ds = xr.tutorial.open_dataset('air_temperature').isel(time=[0, 4, 7, 8])
l = ds.hvplot('lon', 'lat', geo=True, tiles="EsriImagery", colorbar=False).layout()
plots = l.cols(2)
plots

creates

image

but when I do

shared_colorbar = l.values()[0].clone().opts(
    colorbar=True,
    frame_width=0,
    frame_height=500,
    show_frame=False,
    shared_axes=False,
    xaxis=None,
    yaxis=None,
    toolbar=None,
    colorbar_opts={"width": 50, "height": 400, "title": "Temperature (°C)"},
)

pn.Row(plots, shared_colorbar, align="center")

it returns

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[4], line 1
----> 1 shared_colorbar = l.values()[0].clone().opts(
      2     colorbar=True,
      3     frame_width=0,
      4     frame_height=500,
      5     show_frame=False,
      6     shared_axes=False,
      7     xaxis=None,
      8     yaxis=None,
      9     toolbar=None,
     10     colorbar_opts={"width": 50, "height": 400, "title": "Temperature (°C)"},
     11 )
     12 # colorbar_opts: background_fill_alpha, background_fill_color, bar_line_alpha, bar_line_cap, bar_line_color, bar_line_dash, bar_line_dash_offset, bar_line_join, bar_line_width, border_line_alpha, border_line_cap, border_line_color, border_line_dash, border_line_dash_offset, border_line_join, border_line_width, color_mapper, context_menu, coordinates, display_high, display_low, elements, formatter, group, height, js_event_callbacks, js_property_callbacks, label_standoff, level, location, major_label_overrides, major_label_policy, major_label_text_align, major_label_text_alpha, major_label_text_baseline, major_label_text_color, major_label_text_font, major_label_text_font_size, major_label_text_font_style, major_label_text_line_height, major_label_text_outline_color, major_tick_in, major_tick_line_alpha, major_tick_line_cap, major_tick_line_color, major_tick_line_dash, major_tick_line_dash_offset, major_tick_line_join, major_tick_line_width, major_tick_out, margin, minor_tick_in, minor_tick_line_alpha, minor_tick_line_cap, minor_tick_line_color, minor_tick_line_dash, minor_tick_line_dash_offset, minor_tick_line_join, minor_tick_line_width, minor_tick_out, name, orientation, padding, propagate_hover, renderers, scale_alpha, subscribed_events, syncable, tags, ticker, title, title_standoff, title_text_align, title_text_alpha, title_text_baseline, title_text_color, title_text_font, title_text_font_size, title_text_font_style, title_text_line_height, title_text_outline_color, visible, width, x_range_name or y_range_name
     14 pn.Row(plots, shared_colorbar, align="center")

File ~/miniforge3/envs/coringa/lib/python3.9/site-packages/holoviews/core/accessors.py:35, in AccessorPipelineMeta.pipelined.<locals>.pipelined_call(*args, **kwargs)
     31 inst = args[0]
     33 if not hasattr(inst._obj, '_pipeline'):
     34     # Wrapped object doesn't support the pipeline property
---> 35     return __call__(*args, **kwargs)
     37 inst_pipeline = copy.copy(inst._obj. _pipeline)
     38 in_method = inst._obj._in_method

File ~/miniforge3/envs/coringa/lib/python3.9/site-packages/holoviews/core/accessors.py:568, in Opts.__call__(self, *args, **kwargs)
    562         msg = ("Calling the .opts method with options broken down by options "
    563                "group (i.e. separate plot, style and norm groups) is deprecated. "
    564                "Use the .options method converting to the simplified format "
    565                "instead or use hv.opts.apply_groups for backward compatibility.")
    566         param.main.param.warning(msg)
--> 568 return self._dispatch_opts( *args, **kwargs)

File ~/miniforge3/envs/coringa/lib/python3.9/site-packages/holoviews/core/accessors.py:572, in Opts._dispatch_opts(self, *args, **kwargs)
    570 def _dispatch_opts(self, *args, **kwargs):
    571     if self._mode is None:
--> 572         return self._base_opts(*args, **kwargs)
    573     elif self._mode == 'holomap':
    574         return self._holomap_opts(*args, **kwargs)

File ~/miniforge3/envs/coringa/lib/python3.9/site-packages/holoviews/core/accessors.py:651, in Opts._base_opts(self, *args, **kwargs)
    648     return opts.apply_groups(self._obj, **dict(kwargs, **new_kwargs))
    650 kwargs['clone'] = False if clone is None else clone
--> 651 return self._obj.options(*new_args, **kwargs)

File ~/miniforge3/envs/coringa/lib/python3.9/site-packages/holoviews/core/dimension.py:1276, in Dimensioned.options(self, clone, *args, **kwargs)
   1274     expanded_backends = opts._expand_by_backend(options, backend)
   1275 else:
-> 1276     expanded_backends = [(backend, opts._expand_options(options, backend))]
   1278 obj = self
   1279 for backend, expanded in expanded_backends:

File ~/miniforge3/envs/coringa/lib/python3.9/site-packages/holoviews/util/__init__.py:361, in opts._expand_options(cls, options, backend)
    355         else:
    356             valid_options = sorted({
    357                 keyword
    358                 for group_opts in obj_options.groups.values()
    359                 for keyword in group_opts.allowed_keywords
    360             })
--> 361             cls._options_error(opt, objtype, backend, valid_options)
    362 return expanded

File ~/miniforge3/envs/coringa/lib/python3.9/site-packages/holoviews/util/__init__.py:404, in opts._options_error(cls, opt, objtype, backend, valid_options)
    401     return
    403 if matches:
--> 404     raise ValueError('Unexpected option %r for %s type '
    405                      'across all extensions. Similar options '
    406                      'for current extension (%r) are: %s.' %
    407                      (opt, objtype, current_backend, matches))
    408 else:
    409     raise ValueError('Unexpected option %r for %s type '
    410                      'across all extensions. No similar options '
    411                      'found.' % (opt, objtype))

ValueError: Unexpected option 'colorbar' for Overlay type across all extensions. Similar options for current extension ('bokeh') are: ['bgcolor', 'toolbar'].

@ahuang11
Copy link
Collaborator Author

ahuang11 commented Jul 1, 2024

Maybe

shared_colorbar = l.values()[0].clone().opts(
    "Image",
    colorbar=True,
    frame_width=0,
    frame_height=500,
    show_frame=False,
    shared_axes=False,
    xaxis=None,
    yaxis=None,
    toolbar=None,
    colorbar_opts={"width": 50, "height": 400, "title": "Temperature (°C)"},
)

pn.Row(plots, shared_colorbar, align="center")

@iuryt
Copy link

iuryt commented Jul 2, 2024

shared_colorbar = l.values()[0].clone().opts(
"Image",
colorbar=True,
frame_width=0,
frame_height=500,
show_frame=False,
shared_axes=False,
xaxis=None,
yaxis=None,
toolbar=None,
colorbar_opts={"width": 50, "height": 400, "title": "Temperature (°C)"},
)

pn.Row(plots, shared_colorbar, align="center")

It returned something like this

image

😓

@ahuang11
Copy link
Collaborator Author

ahuang11 commented Jul 2, 2024

import xarray as xr
import hvplot.xarray
import panel as pn

ds = xr.tutorial.open_dataset('air_temperature').isel(time=[0, 4, 7, 8])
l = ds.hvplot('lon', 'lat', geo=True, tiles="EsriImagery", colorbar=False).layout()
plots = l.cols(2)
shared_colorbar = plots.values()[0].get(1).clone().opts(
    "Image",
    colorbar=True,
    frame_width=1,
    frame_height=550,
    show_frame=False,
    shared_axes=False,
    xaxis=None,
    yaxis=None,
    toolbar=None,
    colorbar_opts={"width": 50, "height": 400, "title": "Temperature (°C)"},
)

pn.Row(plots, shared_colorbar, align="center", styles={"background-color": "white"})
image

How I approached it:

Print out the plots
image

Get the image
image

Tweak the opts, specifically width

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants