What is the intended behavior of clicking on the legend for GeoViews.Points displayed on a map

Hi,

We’ve just finished up a beta version of our dashboard tool built with Holoviz. I personally am pretty stoked! It has been a lot of fun to work with and we’re quite pleased with the results. Thanks for a great tool. But, of course, I have a lot of questions.

To start, when you click the legend on the map the entire legend dims and all of the points on the map dim (not just the category you intended to select). I thought one could click a particular category and see it highlighted.

The category and legend are created by the color=‘platform_type’ where platform_type is a column of string values in the dataframe.

surface_points = gv.Points(surface_locs, [‘longitude’, ‘latitude’])
surface_points.opts(… color=‘platform_type’ … show_legend=True, legend_position=‘right’,)

The bokeh documentation (https://docs.bokeh.org/en/latest/docs/user_guide/interaction/legends.html) makes me think maybe this doesn’t work yet, but I’m not sure I understand enough about bokeh to say for sure. If that’s the case I like for the legend to just ignore the click instead of dimming everything which is of no particular use.

You can see what I’m talking about at: https://ferret.pmel.noaa.gov/osmc/dashboard (be patient, it takes a long time to load which is a separate topic).

Thanks
Roland

Nope, you should be able to highlight individual categories of the legend as you suspect. Can you boil down your code to an MWE?

I don’t know if it’s minimal, but this shows what I’m talking about:

import pandas as pd
import geoviews as gv
import geoviews.feature as gf
import holoviews as hv
hv.extension('bokeh')
import hvplot.pandas
from holoviews import streams
from holoviews import opts
from cartopy import crs
import panel as pn
pn.extension()
import numpy as np
import colorcet as cc

df = pd.read_csv('https://ferret.pmel.noaa.gov/pmel/erddap/tabledap/saildrone_west_coast_survey_2018.csv?trajectory%2Clatitude%2Clongitude%2CSOG%2CTEMP_AIR_MEAN%2CTEMP_CTD_MEAN%2CTEMP_CTD_STDDEV%2CSAL_MEAN%2Ctime&time%3E=2018-08-26&time%3C=2018-08-27', skiprows=[1])

df['trajectory'] = df['trajectory'].astype(int).astype(str)

surface_points = gv.Points(df, ['longitude', 'latitude'])
surface_points.opts(global_extent=False, 
                    size=6,
                    aspect='equal',
                    projection=crs.PlateCarree(), 
                    tools=['hover', 'tap'],
                    active_tools=['pan','wheel_zoom'],
                    color='trajectory',
                    cmap='glasbey',
                    show_legend=True,
                    frame_width=450,
                    nonselection_alpha=0.4,
                    legend_position='right',
                    title='One day of West Coast Survey Saildrones')

gf.land*gf.ocean*gf.coastline.opts(line_color='black')*surface_points

So that’s a subtlety that’s maybe not sufficiently explained in the docs. If you plot a single set of points they will all share the same bokeh GlyphRenderer and therefore all be controlled by the same legend entry. This is something I’d love to improve at the Bokeh level eventually for now there is an alternative however. Instead of simply setting the color you can group your data by your color variable. This can be done by the groupby method or using the .to method and then calling the overlay method:

ds = hv.Dataset(df, ['latitude', 'longitude', 'trajectory'])

surface_points = ds.to(gv.Points, ['longitude', 'latitude']).opts(
    global_extent=False, 
    size=6,
    aspect='equal',
    projection=crs.PlateCarree(), 
    tools=['hover', 'tap'],
    active_tools=['pan','wheel_zoom'],
    color=hv.Cycle(cc.glasbey),
    frame_width=450,
    alpha=0.01,
    muted_alpha=0.001,
    legend_position='right',
    title='One day of West Coast Survey Saildrones'
).overlay()

You will now have an NdOverlay of points grouped by the ‘trajectory’ variable which cycles through the glasbey colors. The other major problem that you have is that you have ton of overlapping points so even a low alpha will still be at full opacity. You will therefore have to reduce your muted_alpha value to something very low.

Thank you very much. I will admit not yet knowing the details of GylphRenders, NdOverlays, and whatnot and floundering between the HoloViz docs, the Bokeh docs and the help(), but I’m happy to get the desired effect without yet understanding the details. For those following along at home, here is a complete working example with a different data query which shows the weekly position so the data are less dense and so we don’t have resort to the extreme alpha values.

import pandas as pd
import geoviews as gv
import geoviews.feature as gf
import holoviews as hv
import hvplot.pandas
from cartopy import crs
import colorcet as cc
df = pd.read_csv('https://ferret.pmel.noaa.gov/pmel/erddap/tabledap/saildrone_west_coast_survey_2018.csv?trajectory%2Clatitude%2Clongitude%2Ctime&time%3E=2018-08-01&orderByClosest(%22trajectory%2Ctime%2C7%20day%22)', skiprows=[1])
df['trajectory'] = df['trajectory'].astype(int).astype(str)
ds = hv.Dataset(df, ['latitude', 'longitude', 'trajectory'])

surface_points = ds.to(gv.Points, ['longitude', 'latitude']).opts(
    global_extent=False, 
    size=6,
    aspect='equal',
    projection=crs.PlateCarree(), 
    tools=['hover', 'tap'],
    active_tools=['pan','wheel_zoom'],
    color=hv.Cycle(cc.glasbey),
    frame_width=450,
    legend_position='right',
    title='Weekly Position West Coast Survey Saildrones, August onward').overlay()
    
gf.land*gf.ocean*gf.coastline.opts(line_color='black')*surface_points

Thanks again for all the help.

In my application, in addition to dimming some categories by selecting them from the legend, I would like to attach a stream to all of the points. When I attach it to the overlay, I get callbacks with the index into the points of the selected category for the last category plotted, but no call back for any of the other categories. Is there a different thing I can attach to the stream as the source or a different source or stream where I get callback from any point?

import pandas as pd
import geoviews as gv
import geoviews.feature as gf
import holoviews as hv
import hvplot.pandas
from cartopy import crs
import colorcet as cc
from holoviews import streams
import panel as pn


df = pd.read_csv('https://ferret.pmel.noaa.gov/pmel/erddap/tabledap/saildrone_west_coast_survey_2018.csv?trajectory%2Clatitude%2Clongitude%2Ctime&time%3E=2018-08-01&orderByClosest(%22trajectory%2Ctime%2C7%20day%22)', skiprows=[1])
df['trajectory'] = df['trajectory'].astype(int).astype(str)
ds = hv.Dataset(df, ['latitude', 'longitude', 'trajectory'])

surface_points = ds.to(gv.Points, ['longitude', 'latitude']).opts(
    global_extent=False, 
    size=6,
    aspect='equal',
    projection=crs.PlateCarree(), 
    tools=['hover', 'tap'],
    active_tools=['pan','wheel_zoom'],
    color=hv.Cycle(cc.glasbey),
    frame_width=450,
    legend_position='right',
    title='Weekly Position West Coast Survey Saildrones, August onward').overlay()

surface_stream = streams.Selection1D(source=surface_points)

selection_title = hv.DynamicMap(make_title, streams=[surface_stream])

def make_title(index):
    if len(index) > 0:
        return hv.Div('Your selection has index ' + str(index)).opts(height=40)
    else:
        return hv.Div("Nothing selected." + str(index)).opts(height=40)

pn.Column(selection_title, 
gf.land*gf.ocean*gf.coastline.opts(line_color='black')*surface_points)