holoviz / holoviews

With Holoviews, your data visualizes itself.
https://holoviews.org
BSD 3-Clause "New" or "Revised" License
2.69k stars 402 forks source link

Quickly show/hide overlay layers #3882

Open asmith26 opened 5 years ago

asmith26 commented 5 years ago

I work a lot with Geospatial data, and having the ability to quickly show/hide different HoloViews objects (e.g. a satelite tif image and another hv object of e.g. air temperature) would be very useful.

A similiar example is: https://rstudio.github.io/leaflet/showhide.html image

Does this functionality already exist? I'm not that familiar with HoloViews internals, but I feel this could work something like:

Many thanks for any help (and many awesome viz libraries!)

jbednar commented 5 years ago

Bokeh legends are already clickable to mute/unmute specific traces in some cases, but for that to work in general for map tiles, etc. would currently require creating a corresponding Panel widget that sets the alpha property of each item and selects which map tile provider to use. But it does seem like there could be a custom Bokeh tool that handles this common use case, which would be nice to have...

ahuang11 commented 5 years ago

This would be really awesome to have!

asmith26 commented 4 years ago

I'm thinking of having a go at this.

I feel this could be implemented as a bokeh tool:

(df.hvplot.points(x="x", y="y") * df.hvplot.line(x="x", y="y")).opts(tools=["layers_control"])

potentially creating a button (e.g. p1, p2 could denote "plot1", "plot2") for each overlay element (I think this is called a glyph is bokeh terminology): image

Or as a holoviews option:

(df.hvplot.points(x="x", y="y") * df.hvplot.line(x="x", y="y")).opts(show_layers_control=True)

This could potentially be added to a plot like a legend is (also similar to the image above)

Any help appreciated (mentioning @bryevdv and @philippjfr in case they kindly have any advice), many thanks!

asmith26 commented 4 years ago

I'm more familiar with Holoview/HoloViz, so thought I try some code:

import pandas as pd
import panel as pn
import param as pm
import hvplot.pandas

df = pd.DataFrame({
    "x": [1,2,3],
    "y": [1,2,3],
})
show_plot = pn.widgets.Checkbox()
plot = df.hvplot.points(x="x", y="y")

@pn.depends(show_plot.param.value)
def get_plot(value):
    return plot.opts(alpha=value)

pn.Column(show_plot, get_plot)

This works, but I think it is generating a new plot (thus may be slow for big or datashaded data). To get around this, following the approach of the NYC taxi demo:

plot = df.hvplot.points(x="x", y="y")

class LayersControl(pm.Parameterized):
    # show_plot = pm.Boolean(True)  # "couldn't get working"
    show_plot = pm.Magnitude(default=1.0)

    def viewable(self, **kwargs):
        return plot.apply.opts(alpha=self.param.show_plot)

lc = LayersControl()
pn.Column(lc.param, lc.viewable())

Not sure if this is the correct approach, or how it would integrate with the library code.

asmith26 commented 3 years ago

I've recently established this seems to work quite well (allows you to turn layers on and off using the legend):

# gdf := GeoPandas GeoDataFrame

gdf.hvplot(.. label='Some Layer', muted_alpha=0.001) * gdf.hvplot(.. label='Another Layer', muted_alpha=0.001)
jlstevens commented 3 years ago

I like this suggestion and I particularly like the bokeh tools idea...

lalligagger commented 1 year ago

Bumping this as a definite nice-to-have standard feature. 😀

LayerGroups/ Selector API is really one of the only things still pulling me back towards ipyleaflet from holoviz in my project.

ahuang11 commented 1 year ago

I think this is related: https://github.com/holoviz/panel/issues/5115

ahuang11 commented 1 year ago
import panel as pn
import geoviews as gv

gv.extension("bokeh")
pn.extension()

sliders = pn.Column()
overlay = gv.Overlay().opts(width=500, height=500)
for tile_source in gv.tile_sources.tile_sources:
    slider = pn.widgets.IntSlider(
        name=tile_source,
        value=0,
        start=0,
        stop=10,
        step=1,
    )
    if tile_source == "OSM":
        slider.value = 1
    sliders.append(slider)
    overlay *= (
        gv.tile_sources.tile_sources[tile_source]
        .apply.opts(alpha=slider.param.value)
        .opts(global_extent=True)
    )

pn.Row(sliders, overlay).servable()

This kind of works, but it's clunky. I initially tried using toggle, but alpha doesn't accept booleans and I'm not sure if I can use hv.dim or something to convert it to a float value.

https://github.com/holoviz/holoviews/assets/15331990/f819fb1a-dbb1-4bc1-a262-9a5f01de7a07

ahuang11 commented 1 year ago

Got the slider to work with jslink too

import panel as pn
import geoviews as gv
import xarray as xr
gv.extension("bokeh")
pn.extension()

ds = xr.tutorial.open_dataset('air_temperature')
slider = pn.widgets.FloatSlider(name="alpha", start=0, end=1, value=0.5)
plot = gv.tile_sources.CartoLight()
slider.jslink(plot, value="glyph_renderer.alpha")
pn.Column(slider, plot).servable()