From 4ca38fe6feec0e7045b5e64635d2f91d000ab401 Mon Sep 17 00:00:00 2001 From: jlstevens Date: Mon, 8 May 2017 02:40:08 +0100 Subject: [PATCH] Merged Operation and ElementOperation and added alias --- holoviews/core/operation.py | 144 ++++++++++++++++++++++++------------ 1 file changed, 96 insertions(+), 48 deletions(-) diff --git a/holoviews/core/operation.py b/holoviews/core/operation.py index 7fed0f0423..e2fe77f4f3 100644 --- a/holoviews/core/operation.py +++ b/holoviews/core/operation.py @@ -20,7 +20,17 @@ class Operation(param.ParameterizedFunction): """ - Base class for all Operation types. + An ElementOperation process an Element or HoloMap at the level of + individual elements or overlays. If a holomap is passed in as + input, a processed holomap is returned as output where the + individual elements have been transformed accordingly. An + ElementOperation may turn overlays in new elements or vice versa. + + An ElementOperation can be set to be dynamic, which will return a + DynamicMap with a callback that will apply the operation + dynamically. An ElementOperation may also supply a list of Stream + classes on a streams parameter, which can allow dynamic control + over the parameters on the operation. """ group = param.String(default='Operation', doc=""" @@ -28,6 +38,37 @@ class Operation(param.ParameterizedFunction): Operation. By default this should match the operation name.""") + dynamic = param.ObjectSelector(default='default', + objects=['default', True, False], doc=""" + Whether the operation should be applied dynamically when a + specific frame is requested, specified as a Boolean. If set to + 'default' the mode will be determined based on the input type, + i.e. if the data is a DynamicMap it will stay dynamic.""") + + input_ranges = param.ClassSelector(default={}, + class_=(dict, tuple), doc=""" + Ranges to be used for input normalization (if applicable) in a + format appropriate for the Normalization.ranges parameter. + + By default, no normalization is applied. If key-wise + normalization is required, a 2-tuple may be supplied where the + first component is a Normalization.ranges list and the second + component is Normalization.keys. """) + + link_inputs = param.Boolean(default=False, doc=""" + If the operation is dynamic, whether or not linked streams + should be transferred from the operation inputs for backends + that support linked streams. + + For example if an operation is applied to a DynamicMap with an + RangeXY, this switch determines whether the corresponding + visualization should update this stream with range changes + originating from the newly generated axes.""") + + streams = param.List(default=[], doc=""" + List of streams that are applied if dynamic=True, allowing + for dynamic interaction with the plot.""") + @classmethod def search(cls, element, pattern): """ @@ -69,53 +110,6 @@ def get_overlay_bounds(cls, overlay): raise ValueError("Extents across the overlay are inconsistent") - -class ElementOperation(Operation): - """ - An ElementOperation process an Element or HoloMap at the level of - individual elements or overlays. If a holomap is passed in as - input, a processed holomap is returned as output where the - individual elements have been transformed accordingly. An - ElementOperation may turn overlays in new elements or vice versa. - - An ElementOperation can be set to be dynamic, which will return a - DynamicMap with a callback that will apply the operation - dynamically. An ElementOperation may also supply a list of Stream - classes on a streams parameter, which can allow dynamic control - over the parameters on the operation. - """ - - dynamic = param.ObjectSelector(default='default', - objects=['default', True, False], doc=""" - Whether the operation should be applied dynamically when a - specific frame is requested, specified as a Boolean. If set to - 'default' the mode will be determined based on the input type, - i.e. if the data is a DynamicMap it will stay dynamic.""") - - input_ranges = param.ClassSelector(default={}, - class_=(dict, tuple), doc=""" - Ranges to be used for input normalization (if applicable) in a - format appropriate for the Normalization.ranges parameter. - - By default, no normalization is applied. If key-wise - normalization is required, a 2-tuple may be supplied where the - first component is a Normalization.ranges list and the second - component is Normalization.keys. """) - - link_inputs = param.Boolean(default=False, doc=""" - If the operation is dynamic, whether or not linked streams - should be transferred from the operation inputs for backends - that support linked streams. - - For example if an operation is applied to a DynamicMap with an - RangeXY, this switch determines whether the corresponding - visualization should update this stream with range changes - originating from the newly generated axes.""") - - streams = param.List(default=[], doc=""" - List of streams that are applied if dynamic=True, allowing - for dynamic interaction with the plot.""") - def _process(self, view, key=None): """ Process a single input element and outputs new single element @@ -169,6 +163,13 @@ def __call__(self, element, **params): return processed +class ElementOperation(Operation): + + def __init__(self, *args, **kwargs): + self.warning('ElementOperation has been deprecated and renamed to Operation.') + super(ElementOperation, self).__init__(*args, **kwargs) + + class OperationCallable(Callable): """ OperationCallable allows wrapping an ElementOperation and the @@ -185,6 +186,7 @@ def __init__(self, callable, **kwargs): super(OperationCallable, self).__init__(callable, **kwargs) + class TreeOperation(Operation): """ A TreeOperation is the most general Operation type; it accepts any @@ -192,6 +194,52 @@ class TreeOperation(Operation): more elements. """ + group = param.String(default='Operation', doc=""" + The group string used to identify the output of the + Operation. By default this should match the operation name.""") + + + @classmethod + def search(cls, element, pattern): + """ + Helper method that returns a list of elements that match the + given path pattern of form {type}.{group}.{label}. + + The input may be a Layout, an Overlay type or a single + Element. + """ + if isinstance(element, Layout): + return [el for cell in element for el in cls.search(cell, pattern)] + if isinstance(element, (NdOverlay, Overlay)): + return [el for el in element if el.matches(pattern)] + elif isinstance(element, Element): + return [element] if element.matches(pattern) else [] + + + @classmethod + def get_overlay_label(cls, overlay, default_label=''): + """ + Returns a label if all the elements of an overlay agree on a + consistent label, otherwise returns the default label. + """ + if all(el.label==overlay.get(0).label for el in overlay): + return overlay.get(0).label + else: + return default_label + + + @classmethod + def get_overlay_bounds(cls, overlay): + """ + Returns the extents if all the elements of an overlay agree on + a consistent extents, otherwise raises an exception. + """ + if all(el.bounds==overlay.get(0).bounds for el in overlay): + return overlay.get(0).bounds + else: + raise ValueError("Extents across the overlay are inconsistent") + + def process_element(self, element, key, **params): """ The process_element method allows a single element to be