You can build this pretty easily by using a RangeXY stream. A basic example would look something like this:
points = hv.Points(np.random.multivariate_normal((0,0), [[0.1, 0.1], [0.1, 1.0]], (500000,)))
def filter_points(points, x_range, y_range):
if x_range is None or y_range is None:
return points
return points[x_range, y_range]
def hover_points(points, threshold=5000):
if len(points) > threshold:
return points.iloc[:0]
return points
range_stream = hv.streams.RangeXY(source=points)
streams=[range_stream]
filtered = points.apply(filter_points, streams=streams)
shaded = datashade(filtered, width=400, height=400, streams=streams)
hover = filtered.apply(hover_points)
dynamic_hover = (shaded * hover).opts(
hv.opts.Points(tools=['hover'], alpha=0.1, hover_alpha=0.2, size=10))
Here we built a little pipeline starting with the set of points, apply the filter_points
function which selects only the points in the current view port by taking the x_range
and y_range
from the RangeXY
stream and then finally datashade the result and apply the hover_points
function which returns an empty set of points if there are more points than the defined threshold. You can even add a panel widget to control the threshold:
range_stream = hv.streams.RangeXY(source=points)
streams=[range_stream]
threshold = pn.widgets.IntSlider(name='Threshold', start=1000, end=10000, step=100)
filtered = points.apply(filter_points, streams=streams)
hover = filtered.apply(hover_points, threshold=threshold)
shaded = datashade(filtered, width=400, height=400, streams=streams)
dynamic_hover = (shaded * hover).opts(
hv.opts.Points(tools=['hover'], alpha=0.1, hover_alpha=0.2, size=10))
pn.Column(threshold, dynamic_hover)