diff --git a/holoviews/operation/element.py b/holoviews/operation/element.py index 373654f862..5384a9c5d5 100644 --- a/holoviews/operation/element.py +++ b/holoviews/operation/element.py @@ -140,6 +140,48 @@ def _process(self, element, key=None): return fn(element, *self.p.args, **self.p.kwargs) +class apply_when(param.ParameterizedFunction): + """ + Applies a selection depending on the current zoom range. If the + supplied predicate function returns a True it will apply the + operation otherwise it will return the raw element after the + selection. For example the following will apply datashading if + the number of points in the current viewport exceed 1000 otherwise + just returning the selected points element: + + apply_when(points, operation=datashade, predicate=lambda x: x > 1000) + """ + + operation = param.Callable(default=lambda x: x) + + predicate = param.Callable(default=None) + + def _apply(self, element, x_range, y_range, invert=False): + selected = element + if x_range is not None and y_range is not None: + selected = element[x_range, y_range] + condition = self.predicate(selected) + if (not invert and condition) or (invert and not condition): + return selected + elif selected.interface.gridded: + return selected.clone([]) + else: + return selected.iloc[:0] + + def __call__(self, obj, **params): + self.set_param(**params) + streams = [RangeXY()] + if not self.predicate: + raise ValueError( + 'Must provide a predicate function to determine when ' + 'to apply the operation and when to return the selected ' + 'data.' + ) + applied = self.operation(obj.apply(self._apply, streams=streams)) + raw = obj.apply(self._apply, streams=streams, invert=True) + return applied * raw + + class chain(Operation): """ Defining an Operation chain is an easy way to define a new