Source code for xrview.glyphs

""" xrview.glyphs """
from bokeh.models import Band as _Band
from bokeh.models import Whisker as _Whisker

from xrview.utils import MappingProxyType

__all__ = [
    "Line",
    "Circle",
    "Diamond",
    "Square",
    "Triangle",
    "Ray",
    "HBar",
    "VBar",
    "Rect",
    "Whisker",
    "Band",
    "VLine",
    "ErrorLine",
    "ErrorCircle",
    "BoxWhisker",
    "get_glyph",
    "get_glyph_list",
]


# -- Glyphs -- #
class BaseGlyph(object):
    """ Base class for glyphs. """

    method = None
    default_kwargs = MappingProxyType({})

    def __init__(self, x_arg="x", y_arg="y", **kwargs):
        """ Constructor.

        Parameters
        ----------
        x_arg: str, default 'x'
            The glyph argument associated with x-axis values in the data.

        y_arg: str, default 'y'
            The glyph argument associated with y-axis values in the data.

        kwargs:
            Additional keyword arguments to be passed to the underlying
            bokeh glyph(s).
        """
        self.x_arg = x_arg
        self.y_arg = y_arg
        self.glyph_kwargs = dict(self.default_kwargs)
        self.glyph_kwargs.update(kwargs)


[docs]class Line(BaseGlyph): """ A line glyph. """ method = "line"
[docs]class Circle(BaseGlyph): """ A circle glyph. """ method = "circle"
[docs]class Diamond(BaseGlyph): """ A diamond glyph. """ method = "diamond"
[docs]class Square(BaseGlyph): """ A square glyph. """ method = "square"
[docs]class Triangle(BaseGlyph): """ A triangle glyph. """ method = "triangle"
[docs]class Ray(BaseGlyph): """ A ray glyph. """ method = "ray" default_kwargs = MappingProxyType({"length": 0, "angle": 0})
[docs]class HBar(BaseGlyph): """ A horizontal bar glyph. """ method = "hbar"
[docs] def __init__(self, height, x_arg="right", y_arg="y", other=0.0, **kwargs): """ Constructor. Parameters ---------- height : str or float The name of a coordinate or a fixed value that will represent the height of the bar. x_arg: str, default 'right' The glyph argument associated with x-axis values in the data. y_arg: str, default 'y' The glyph argument associated with y-axis values in the data. other: str or float, default 0. The name of a coordinate or a fixed value that will represent the other side of the bar (i.e. the left side when x_arg='right'). kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ if x_arg == "left": if "right" not in kwargs: kwargs.update({"right": other}) elif x_arg == "right": if "left" not in kwargs: kwargs.update({"left": other}) else: raise ValueError("Unrecognized x_arg") super(HBar, self).__init__(x_arg, y_arg, height=height, **kwargs)
[docs]class VBar(BaseGlyph): """ A vertical bar glyph. """ method = "vbar"
[docs] def __init__(self, width, x_arg="x", y_arg="top", other=0.0, **kwargs): """ Constructor. Parameters ---------- width : str or float The name of a coordinate or a fixed value that will represent the width of the bar. x_arg: str, default 'x' The glyph argument associated with x-axis values in the data. y_arg: str, default 'top' The glyph argument associated with y-axis values in the data. other: str or float, default 0. The name of a coordinate or a fixed value that will represent the other side of the bar (i.e. the bottom side when y_arg='top'). kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ if y_arg == "top": if "bottom" not in kwargs: kwargs.update({"bottom": other}) elif y_arg == "bottom": if "top" not in kwargs: kwargs.update({"top": other}) else: raise ValueError("Unrecognized x_arg") super(VBar, self).__init__(x_arg, y_arg, width=width, **kwargs)
[docs]class Rect(BaseGlyph): """ A rectangle glyph. """ method = "rect"
[docs] def __init__(self, width, height, x_arg="x", y_arg="y", **kwargs): """ Constructor. Parameters ---------- width : str or float The name of a coordinate or a fixed value that will represent the width of the rectangle. height : str or float The name of a coordinate or a fixed value that will represent the height of the rectangle. x_arg: str, default 'x' The glyph argument associated with x-axis values in the data. y_arg: str, default 'y' The glyph argument associated with y-axis values in the data. kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ super(Rect, self).__init__( x_arg, y_arg, width=width, height=height, **kwargs )
# -- Compat Glyphs -- # class BaseGlyphCompat(BaseGlyph): """ Base class for annotations that are currently treated like glyphs. """
[docs]class Whisker(BaseGlyphCompat): """ A whisker annotation. """ method = _Whisker
[docs] def __init__(self, x_arg="base", y_arg="upper", other=0.0, **kwargs): """ Constructor. Parameters ---------- x_arg: str, default 'base' The glyph argument associated with x-axis values in the data. y_arg: str, default 'upper' The glyph argument associated with y-axis values in the data. other: str or float, default 0. The name of a coordinate or a fixed value that will represent the other end of the whisker (i.e. the lower end when y_arg='upper'). kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ if y_arg == "upper": if "lower" not in kwargs: kwargs.update({"lower": other}) elif y_arg == "lower": if "upper" not in kwargs: kwargs.update({"upper": other}) else: raise ValueError("Unrecognized y_arg") super(Whisker, self).__init__(x_arg, y_arg, **kwargs)
[docs]class Band(BaseGlyphCompat): """ A band annotation. """ method = _Band
[docs] def __init__(self, x_arg="base", y_arg="upper", other=0.0, **kwargs): """ Constructor. Parameters ---------- x_arg: str, default 'base' The glyph argument associated with x-axis values in the data. y_arg: str, default 'upper' The glyph argument associated with y-axis values in the data. other: str or float, default 0. The name of a coordinate or a fixed value that will represent the other end of the whisker (i.e. the lower end when y_arg='upper'). kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ if y_arg == "upper": if "lower" not in kwargs: kwargs.update({"lower": other}) elif y_arg == "lower": if "upper" not in kwargs: kwargs.update({"upper": other}) else: raise ValueError("Unrecognized y_arg") super(Band, self).__init__(x_arg, y_arg, **kwargs)
# -- Composite Glyphs -- # class CompositeGlyph(object): """ A glyph composed of multiple glyphs. """ default_kwargs = MappingProxyType({}) def __init__(self, glyphs, x_arg="x", y_arg="y", **kwargs): """ Constructor. Parameters ---------- x_arg: str, default 'x' The glyph argument associated with x-axis values in the data. y_arg: str, default 'y' The glyph argument associated with y-axis values in the data. kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ glyph_kwargs = dict(self.default_kwargs) glyph_kwargs.update(kwargs) self.glyphs = [ g(x_arg=x_arg, y_arg=y_arg, **glyph_kwargs) for g in glyphs ] def __iter__(self): return iter(self.glyphs) def __len__(self): return len(self.glyphs)
[docs]class VLine(CompositeGlyph): """ A collection of vertical lines. """ default_kwargs = MappingProxyType( { "length": 0, "line_width": 1, "angle_units": "deg", "color": "grey", "alpha": 0.5, } )
[docs] def __init__(self, x_arg="x", y_arg="y", **kwargs): super(VLine, self).__init__( [Ray, Ray], x_arg=x_arg, y_arg=y_arg, **kwargs ) self.glyphs[0].glyph_kwargs["angle"] = 90 self.glyphs[1].glyph_kwargs["angle"] = 270
[docs]class ErrorLine(CompositeGlyph): """ A line with an error bar. """
[docs] def __init__(self, lower, upper, **kwargs): """ Constructor. Parameters ---------- lower: str or float The name of a coordinate or a fixed value that will represent the lower end of the error bar. upper: str or float The name of a coordinate or a fixed value that will represent the upper end of the error bar. kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ self.glyphs = [Line(**kwargs)] kwargs.pop("color", None) kwargs.pop("legend", None) self.glyphs.append(Whisker(lower=lower, upper=upper, **kwargs))
[docs]class ErrorCircle(CompositeGlyph): """ A circle with an error bar. """
[docs] def __init__(self, lower, upper, **kwargs): """ Constructor. Parameters ---------- lower: str or float The name of a coordinate or a fixed value that will represent the lower end of the error bar. upper: str or float The name of a coordinate or a fixed value that will represent the upper end of the error bar. kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ self.glyphs = [Circle(**kwargs)] kwargs.pop("color", None) kwargs.pop("legend", None) self.glyphs.append(Whisker(lower=lower, upper=upper, **kwargs))
[docs]class BoxWhisker(CompositeGlyph): """ A box-whisker glyph. """ default_kwargs = MappingProxyType({"line_color": "black"})
[docs] def __init__(self, width, q_lower, w_lower, q_upper, w_upper, **kwargs): """ Constructor. Parameters ---------- width : str or float The name of a coordinate or a fixed value that will represent the width of the box. q_lower: str or float The name of a coordinate or a fixed value that will represent the lower end of the box. w_lower: str or float The name of a coordinate or a fixed value that will represent the lower end of the error bar. q_upper: str or float The name of a coordinate or a fixed value that will represent the upper end of the box. w_upper: str or float The name of a coordinate or a fixed value that will represent the upper end of the error bar. kwargs: Additional keyword arguments to be passed to the underlying bokeh glyph(s). """ glyph_kwargs = dict(self.default_kwargs) glyph_kwargs.update(kwargs) self.glyphs = [ VBar(width, bottom=q_lower, **glyph_kwargs), VBar(width, y_arg="bottom", top=q_upper, **glyph_kwargs), ] glyph_kwargs.pop("color", None) glyph_kwargs.pop("legend", None) self.glyphs.append( Whisker(lower=w_lower, upper=w_upper, **glyph_kwargs) )
def get_glyph(name, *args, **kwargs): """ Get a glyph instance by name. Parameters ---------- name : str The name of the glyph class. Returns ------- glyph : BaseGlyph An instance of the corresponding glyph class. """ glyphs = { "line": Line, "circle": Circle, "diamond": Diamond, "square": Square, "triangle": Triangle, "ray": Ray, "hbar": HBar, "vbar": VBar, "rect": Rect, "vline": VLine, "whisker": Whisker, "band": Band, "errorline": ErrorLine, "errorcircle": ErrorCircle, "boxwhisker": BoxWhisker, } try: return glyphs[name.lower()](*args, **kwargs) except KeyError: raise ValueError("Unrecognized or unsupported glyph: " + name) def get_glyph_list(glyphs): """ Get a list of glyphs from str, single glyph or iterable. """ if isinstance(glyphs, str): glyphs = [get_glyph(glyphs)] else: try: iter(glyphs) except TypeError: glyphs = [glyphs] else: glyphs = [ get_glyph(g) if isinstance(g, str) else g for g in glyphs ] return glyphs