1#-----------------------------------------------------------------------------
2# Copyright (c) 2012 - 2021, Anaconda, Inc., and Bokeh Contributors.
3# All rights reserved.
4#
5# The full license is in the file LICENSE.txt, distributed with this software.
6#-----------------------------------------------------------------------------
7''' Guide renderers for various kinds of axes that can be added to
8Bokeh plots
9
10'''
11
12#-----------------------------------------------------------------------------
13# Boilerplate
14#-----------------------------------------------------------------------------
15import logging # isort:skip
16log = logging.getLogger(__name__)
17
18#-----------------------------------------------------------------------------
19# Imports
20#-----------------------------------------------------------------------------
21
22# Bokeh imports
23from ..core.enums import TickLabelOrientation
24from ..core.has_props import abstract
25from ..core.properties import (
26    Auto,
27    Datetime,
28    Dict,
29    Either,
30    Enum,
31    Factor,
32    Float,
33    Include,
34    Instance,
35    Int,
36    Null,
37    Nullable,
38    Override,
39    Seq,
40    String,
41    Tuple,
42)
43from ..core.property_mixins import ScalarLineProps, ScalarTextProps
44from .formatters import (
45    BasicTickFormatter,
46    CategoricalTickFormatter,
47    DatetimeTickFormatter,
48    LogTickFormatter,
49    MercatorTickFormatter,
50    TickFormatter,
51)
52from .labeling import AllLabels, LabelingPolicy
53from .renderers import GuideRenderer
54from .tickers import (
55    BasicTicker,
56    CategoricalTicker,
57    DatetimeTicker,
58    FixedTicker,
59    LogTicker,
60    MercatorTicker,
61    Ticker,
62)
63
64#-----------------------------------------------------------------------------
65# Globals and constants
66#-----------------------------------------------------------------------------
67
68__all__ = (
69    'Axis',
70    'CategoricalAxis',
71    'ContinuousAxis',
72    'DatetimeAxis',
73    'LinearAxis',
74    'LogAxis',
75    'MercatorAxis',
76)
77
78#-----------------------------------------------------------------------------
79# General API
80#-----------------------------------------------------------------------------
81
82@abstract
83class Axis(GuideRenderer):
84    ''' A base class that defines common properties for all axis types.
85
86    '''
87
88    bounds = Either(Auto, Tuple(Float, Float), Tuple(Datetime, Datetime), help="""
89    Bounds for the rendered axis. If unset, the axis will span the
90    entire plot in the given dimension.
91    """)
92
93    ticker = Instance(Ticker, help="""
94    A Ticker to use for computing locations of axis components.
95
96    The property may also be passed a sequence of floating point numbers as
97    a shorthand for creating and configuring a ``FixedTicker``, e.g. the
98    following code
99
100    .. code-block:: python
101
102        from bokeh.plotting import figure
103
104        p = figure()
105        p.xaxis.ticker = [10, 20, 37.4]
106
107    is equivalent to:
108
109    .. code-block:: python
110
111        from bokeh.plotting import figure
112        from bokeh.models import FixedTicker
113
114        p = figure()
115        p.xaxis.ticker = FixedTicker(ticks=[10, 20, 37.4])
116
117    """).accepts(Seq(Float), lambda ticks: FixedTicker(ticks=ticks))
118
119    formatter = Instance(TickFormatter, help="""
120    A ``TickFormatter`` to use for formatting the visual appearance
121    of ticks.
122    """)
123
124    axis_label = Nullable(String, default="", help="""
125    A text label for the axis, displayed parallel to the axis rule.
126
127    .. note::
128        LaTeX notation is not currently supported; please see
129        :bokeh-issue:`647` to track progress or contribute.
130
131    """)
132
133    axis_label_standoff = Int(default=5, help="""
134    The distance in pixels that the axis labels should be offset
135    from the tick labels.
136    """)
137
138    axis_label_props = Include(ScalarTextProps, help="""
139    The %s of the axis label.
140    """)
141
142    axis_label_text_font_size = Override(default="13px")
143
144    axis_label_text_font_style = Override(default="italic")
145
146    major_label_standoff = Int(default=5, help="""
147    The distance in pixels that the major tick labels should be
148    offset from the associated ticks.
149    """)
150
151    major_label_orientation = Either(Enum("horizontal", "vertical"), Float, help="""
152    What direction the major label text should be oriented. If a
153    number is supplied, the angle of the text is measured from horizontal.
154    """)
155
156    major_label_overrides = Dict(Either(Float, String), String, default={}, help="""
157    Provide explicit tick label values for specific tick locations that
158    override normal formatting.
159    """)
160
161    major_label_policy = Instance(LabelingPolicy, default=lambda: AllLabels(), help="""
162    Allows to filter out labels, e.g. declutter labels to avoid overlap.
163    """)
164
165    major_label_props = Include(ScalarTextProps, help="""
166    The %s of the major tick labels.
167    """)
168
169    major_label_text_align = Override(default="center")
170
171    major_label_text_baseline = Override(default="alphabetic")
172
173    major_label_text_font_size = Override(default="11px")
174
175    axis_props = Include(ScalarLineProps, help="""
176    The %s of the axis line.
177    """)
178
179    major_tick_props = Include(ScalarLineProps, help="""
180    The %s of the major ticks.
181    """)
182
183    major_tick_in = Int(default=2, help="""
184    The distance in pixels that major ticks should extend into the
185    main plot area.
186    """)
187
188    major_tick_out = Int(default=6, help="""
189    The distance in pixels that major ticks should extend out of the
190    main plot area.
191    """)
192
193    minor_tick_props = Include(ScalarLineProps, help="""
194    The %s of the minor ticks.
195    """)
196
197    minor_tick_in = Int(default=0, help="""
198    The distance in pixels that minor ticks should extend into the
199    main plot area.
200    """)
201
202    minor_tick_out = Int(default=4, help="""
203    The distance in pixels that major ticks should extend out of the
204    main plot area.
205    """)
206
207    fixed_location = Either(Null, Float, Factor, help="""
208    Set to specify a fixed coordinate location to draw the axis. The direction
209    of ticks and major labels is determined by the side panel that the axis
210    belongs to.
211
212    .. note::
213        Axes labels are suppressed when axes are positioned at fixed locations
214        inside the central plot area.
215    """)
216
217@abstract
218class ContinuousAxis(Axis):
219    ''' A base class for all numeric, non-categorical axes types.
220
221    '''
222    pass
223
224class LinearAxis(ContinuousAxis):
225    ''' An axis that picks nice numbers for tick locations on a
226    linear scale. Configured with a ``BasicTickFormatter`` by default.
227
228    '''
229    ticker = Override(default=lambda: BasicTicker())
230
231    formatter = Override(default=lambda: BasicTickFormatter())
232
233class LogAxis(ContinuousAxis):
234    ''' An axis that picks nice numbers for tick locations on a
235    log scale. Configured with a ``LogTickFormatter`` by default.
236
237    '''
238    ticker = Override(default=lambda: LogTicker())
239
240    formatter = Override(default=lambda: LogTickFormatter())
241
242class CategoricalAxis(Axis):
243    ''' An axis that displays ticks and labels for categorical ranges.
244
245    The ``CategoricalAxis`` can handle factor ranges with up to two levels of
246    nesting, including drawing a separator line between top-level groups of
247    factors.
248
249    '''
250    ticker = Override(default=lambda: CategoricalTicker())
251
252    formatter = Override(default=lambda: CategoricalTickFormatter())
253
254    separator_props = Include(ScalarLineProps, help="""
255    The %s of the separator line between top-level categorical groups.
256
257    This property always applies to factors in the outermost level of nesting.
258    """)
259
260    separator_line_color = Override(default="lightgrey")
261    separator_line_width = Override(default=2)
262
263    group_props = Include(ScalarTextProps, help="""
264    The %s of the group categorical labels.
265
266    This property always applies to factors in the outermost level of nesting.
267    If the list of categorical factors is flat (i.e. no nesting) then this
268    property has no effect.
269    """)
270
271    group_label_orientation = Either(Enum(TickLabelOrientation), Float, default="parallel", help="""
272    What direction the group label text should be oriented.
273
274    If a number is supplied, the angle of the text is measured from horizontal.
275
276    This property always applies to factors in the outermost level of nesting.
277    If the list of categorical factors is flat (i.e. no nesting) then this
278    property has no effect.
279    """)
280
281    group_text_font_size = Override(default="11px")
282    group_text_font_style = Override(default="bold")
283    group_text_color = Override(default="grey")
284
285    subgroup_props = Include(ScalarTextProps, help="""
286    The %s of the subgroup categorical labels.
287
288    This property always applies to factors in the middle level of nesting.
289    If the list of categorical factors is has only zero or one levels of nesting,
290    then this property has no effect.
291    """)
292
293    subgroup_label_orientation = Either(Enum(TickLabelOrientation), Float, default="parallel", help="""
294    What direction the subgroup label text should be oriented.
295
296    If a number is supplied, the angle of the text is measured from horizontal.
297
298    This property always applies to factors in the middle level of nesting.
299    If the list of categorical factors is has only zero or one levels of nesting,
300    then this property has no effect.
301    """)
302
303    subgroup_text_font_size = Override(default="11px")
304    subgroup_text_font_style = Override(default="bold")
305
306class DatetimeAxis(LinearAxis):
307    ''' A ``LinearAxis`` that picks nice numbers for tick locations on
308    a datetime scale. Configured with a ``DatetimeTickFormatter`` by
309    default.
310
311    '''
312
313    ticker = Override(default=lambda: DatetimeTicker())
314
315    formatter = Override(default=lambda: DatetimeTickFormatter())
316
317class MercatorAxis(LinearAxis):
318    ''' An axis that picks nice numbers for tick locations on a
319    Mercator scale. Configured with a ``MercatorTickFormatter`` by default.
320
321    Args:
322        dimension ('lat' or 'lon', optional) :
323            Whether this axis will display latitude or longitude values.
324            (default: 'lat')
325
326    '''
327    def __init__(self, dimension='lat', **kw):
328        super().__init__(**kw)
329
330        # Just being careful. It would be defeat the purpose for anyone to actually
331        # configure this axis with different kinds of tickers or formatters.
332        if isinstance(self.ticker, MercatorTicker):
333            self.ticker.dimension = dimension
334        if isinstance(self.formatter, MercatorTickFormatter):
335            self.formatter.dimension = dimension
336
337    ticker = Override(default=lambda: MercatorTicker())
338
339    formatter = Override(default=lambda: MercatorTickFormatter())
340
341#-----------------------------------------------------------------------------
342# Dev API
343#-----------------------------------------------------------------------------
344
345#-----------------------------------------------------------------------------
346# Private API
347#-----------------------------------------------------------------------------
348
349#-----------------------------------------------------------------------------
350# Code
351#-----------------------------------------------------------------------------
352