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''' Models for controlling the text and visual formatting of tick 8labels on Bokeh plot axes. 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 LatLon, NumeralLanguage, RoundingFunction 24from ..core.has_props import abstract 25from ..core.properties import ( 26 AnyRef, 27 Auto, 28 Bool, 29 Dict, 30 Either, 31 Enum, 32 Instance, 33 Int, 34 List, 35 Nullable, 36 String, 37) 38from ..core.validation import error 39from ..core.validation.errors import MISSING_MERCATOR_DIMENSION 40from ..model import Model 41from ..util.string import format_docstring 42from .tickers import Ticker 43 44#----------------------------------------------------------------------------- 45# Globals and constants 46#----------------------------------------------------------------------------- 47 48__all__ = ( 49 'TickFormatter', 50 'BasicTickFormatter', 51 'MercatorTickFormatter', 52 'NumeralTickFormatter', 53 'PrintfTickFormatter', 54 'LogTickFormatter', 55 'CategoricalTickFormatter', 56 'FuncTickFormatter', 57 'DatetimeTickFormatter', 58) 59 60#----------------------------------------------------------------------------- 61# Private API 62#----------------------------------------------------------------------------- 63 64def _DATETIME_TICK_FORMATTER_HELP(field): 65 return """ 66 Formats for displaying datetime values in the %s range. 67 68 See the :class:`~bokeh.models.formatters.DatetimeTickFormatter` help for a list of all supported formats. 69 """ % field 70 71#----------------------------------------------------------------------------- 72# General API 73#----------------------------------------------------------------------------- 74 75@abstract 76class TickFormatter(Model): 77 ''' A base class for all tick formatter types. 78 79 ''' 80 pass 81 82class BasicTickFormatter(TickFormatter): 83 ''' Display tick values from continuous ranges as "basic numbers", 84 using scientific notation when appropriate by default. 85 86 ''' 87 precision = Either(Auto, Int, help=""" 88 How many digits of precision to display in tick labels. 89 """) 90 91 use_scientific = Bool(True, help=""" 92 Whether to ever display scientific notation. If ``True``, then 93 when to use scientific notation is controlled by ``power_limit_low`` 94 and ``power_limit_high``. 95 """) 96 97 power_limit_high = Int(5, help=""" 98 Limit the use of scientific notation to when:: 99 100 log(x) >= power_limit_high 101 102 """) 103 104 power_limit_low = Int(-3, help=""" 105 Limit the use of scientific notation to when:: 106 107 log(x) <= power_limit_low 108 109 """) 110 111class MercatorTickFormatter(BasicTickFormatter): 112 ''' A ``TickFormatter`` for values in WebMercator units. 113 114 Some map plot types internally use WebMercator to describe coordinates, 115 plot bounds, etc. These units are not very human-friendly. This tick 116 formatter will convert WebMercator units into Latitude and Longitude 117 for display on axes. 118 119 ''' 120 121 dimension = Nullable(Enum(LatLon), help=""" 122 Specify whether to format ticks for Latitude or Longitude. 123 124 Projected coordinates are not separable, computing Latitude and Longitude 125 tick labels from Web Mercator requires considering coordinates from both 126 dimensions together. Use this property to specify which result should be 127 used for display. 128 129 Typically, if the formatter is for an x-axis, then dimension should be 130 ``"lon"`` and if the formatter is for a y-axis, then the dimension 131 should be `"lat"``. 132 133 In order to prevent hard to debug errors, there is no default value for 134 dimension. Using an un-configured ``MercatorTickFormatter`` will result in 135 a validation error and a JavaScript console error. 136 """) 137 138 @error(MISSING_MERCATOR_DIMENSION) 139 def _check_missing_dimension(self): 140 if self.dimension is None: 141 return str(self) 142 143class NumeralTickFormatter(TickFormatter): 144 ''' Tick formatter based on a human-readable format string. ''' 145 146 format = String("0,0", help=""" 147 The number format, as defined in the following tables: 148 149 **NUMBERS**: 150 151 ============ ============== =============== 152 Number Format String 153 ============ ============== =============== 154 10000 '0,0.0000' 10,000.0000 155 10000.23 '0,0' 10,000 156 10000.23 '+0,0' +10,000 157 -10000 '0,0.0' -10,000.0 158 10000.1234 '0.000' 10000.123 159 10000.1234 '0[.]00000' 10000.12340 160 -10000 '(0,0.0000)' (10,000.0000) 161 -0.23 '.00' -.23 162 -0.23 '(.00)' (.23) 163 0.23 '0.00000' 0.23000 164 0.23 '0.0[0000]' 0.23 165 1230974 '0.0a' 1.2m 166 1460 '0 a' 1 k 167 -104000 '0a' -104k 168 1 '0o' 1st 169 52 '0o' 52nd 170 23 '0o' 23rd 171 100 '0o' 100th 172 ============ ============== =============== 173 174 **CURRENCY**: 175 176 =========== =============== ============= 177 Number Format String 178 =========== =============== ============= 179 1000.234 '$0,0.00' $1,000.23 180 1000.2 '0,0[.]00 $' 1,000.20 $ 181 1001 '$ 0,0[.]00' $ 1,001 182 -1000.234 '($0,0)' ($1,000) 183 -1000.234 '$0.00' -$1000.23 184 1230974 '($ 0.00 a)' $ 1.23 m 185 =========== =============== ============= 186 187 **BYTES**: 188 189 =============== =========== ============ 190 Number Format String 191 =============== =========== ============ 192 100 '0b' 100B 193 2048 '0 b' 2 KB 194 7884486213 '0.0b' 7.3GB 195 3467479682787 '0.000 b' 3.154 TB 196 =============== =========== ============ 197 198 **PERCENTAGES**: 199 200 ============= ============= =========== 201 Number Format String 202 ============= ============= =========== 203 1 '0%' 100% 204 0.974878234 '0.000%' 97.488% 205 -0.43 '0 %' -43 % 206 0.43 '(0.000 %)' 43.000 % 207 ============= ============= =========== 208 209 **TIME**: 210 211 ============ ============== ============ 212 Number Format String 213 ============ ============== ============ 214 25 '00:00:00' 0:00:25 215 238 '00:00:00' 0:03:58 216 63846 '00:00:00' 17:44:06 217 ============ ============== ============ 218 219 For the complete specification, see http://numbrojs.com/format.html 220 """) 221 222 language = Enum(NumeralLanguage, default="en", help=""" 223 The language to use for formatting language-specific features (e.g. thousands separator). 224 """) 225 226 rounding = Enum(RoundingFunction, help=""" 227 Rounding functions (round, floor, ceil) and their synonyms (nearest, rounddown, roundup). 228 """) 229 230class PrintfTickFormatter(TickFormatter): 231 ''' Tick formatter based on a printf-style format string. ''' 232 233 format = String("%s", help=""" 234 The number format, as defined as follows: the placeholder in the format 235 string is marked by % and is followed by one or more of these elements, 236 in this order: 237 238 * An optional ``+`` sign 239 Causes the result to be preceded with a plus or minus sign on numeric 240 values. By default, only the ``-`` sign is used on negative numbers. 241 242 * An optional padding specifier 243 Specifies what (if any) character to use for padding. Possible values 244 are 0 or any other character preceded by a ``'`` (single quote). The 245 default is to pad with spaces. 246 247 * An optional ``-`` sign 248 Causes sprintf to left-align the result of this placeholder. The default 249 is to right-align the result. 250 251 * An optional number 252 Specifies how many characters the result should have. If the value to be 253 returned is shorter than this number, the result will be padded. 254 255 * An optional precision modifier 256 Consists of a ``.`` (dot) followed by a number, specifies how many digits 257 should be displayed for floating point numbers. When used on a string, it 258 causes the result to be truncated. 259 260 * A type specifier 261 Can be any of: 262 263 - ``%`` --- yields a literal ``%`` character 264 - ``b`` --- yields an integer as a binary number 265 - ``c`` --- yields an integer as the character with that ASCII value 266 - ``d`` or ``i`` --- yields an integer as a signed decimal number 267 - ``e`` --- yields a float using scientific notation 268 - ``u`` --- yields an integer as an unsigned decimal number 269 - ``f`` --- yields a float as is 270 - ``o`` --- yields an integer as an octal number 271 - ``s`` --- yields a string as is 272 - ``x`` --- yields an integer as a hexadecimal number (lower-case) 273 - ``X`` --- yields an integer as a hexadecimal number (upper-case) 274 275 """) 276 277class LogTickFormatter(TickFormatter): 278 ''' Display tick values from continuous ranges as powers 279 of some base. 280 281 Most often useful in conjunction with a ``LogTicker``. 282 283 ''' 284 ticker = Nullable(Instance(Ticker), help=""" 285 The corresponding ``LogTicker``, used to determine the correct 286 base to use. If unset, the formatter will use base 10 as a default. 287 """) 288 289class CategoricalTickFormatter(TickFormatter): 290 ''' Display tick values from categorical ranges as string 291 values. 292 293 ''' 294 pass 295 296class FuncTickFormatter(TickFormatter): 297 ''' Display tick values that are formatted by a user-defined function. 298 299 .. warning:: 300 The explicit purpose of this Bokeh Model is to embed *raw JavaScript 301 code* for a browser to execute. If any part of the code is derived 302 from untrusted user inputs, then you must take appropriate care to 303 sanitize the user input prior to passing to Bokeh. 304 305 ''' 306 307 args = Dict(String, AnyRef, help=""" 308 A mapping of names to Python objects. In particular those can be bokeh's models. 309 These objects are made available to the formatter's code snippet as the values of 310 named parameters to the callback. 311 """) 312 313 code = String(default="", help=""" 314 A snippet of JavaScript code that reformats a single tick to the desired 315 format. The variable ``tick`` will contain the unformatted tick value and 316 can be expected to be present in the code snippet namespace at render time. 317 318 Additionally available variables are: 319 320 * ``ticks``, an array of all axis ticks as positioned by the ticker, 321 * ``index``, the position of ``tick`` within ``ticks``, and 322 * the keys of ``args`` mapping, if any. 323 324 Finding yourself needing to cache an expensive ``ticks``-dependent 325 computation, you can store it on the ``this`` variable. 326 327 Example: 328 329 .. code-block:: javascript 330 331 code = ''' 332 this.precision = this.precision || (ticks.length > 5 ? 1 : 2); 333 return Math.floor(tick) + " + " + (tick % 1).toFixed(this.precision); 334 ''' 335 """) 336 337class DatetimeTickFormatter(TickFormatter): 338 ''' A ``TickFormatter`` for displaying datetime values nicely across a 339 range of scales. 340 341 ``DatetimeTickFormatter`` has the following properties (listed together 342 with their default values) that can be used to control the formatting 343 of axis ticks at different scales scales: 344 345 .. code-block:: python 346 347 {defaults} 348 349 Each scale property can be set to format or list of formats to use for 350 formatting datetime tick values that fall in in that "time scale". 351 By default, only the first format string passed for each time scale 352 will be used. By default, all leading zeros are stripped away from 353 the formatted labels. 354 355 This list of supported `strftime`_ formats is reproduced below. 356 357 %a 358 The abbreviated name of the day of the week according to the 359 current locale. 360 361 %A 362 The full name of the day of the week according to the current 363 locale. 364 365 %b 366 The abbreviated month name according to the current locale. 367 368 %B 369 The full month name according to the current locale. 370 371 %c 372 The preferred date and time representation for the current 373 locale. 374 375 %C 376 The century number (year/100) as a 2-digit integer. 377 378 %d 379 The day of the month as a decimal number (range 01 to 31). 380 381 %D 382 Equivalent to %m/%d/%y. (Americans should note that in many 383 other countries %d/%m/%y is rather common. This means that in 384 international context this format is ambiguous and should not 385 be used.) 386 387 %e 388 Like %d, the day of the month as a decimal number, but a 389 leading zero is replaced by a space. 390 391 %f 392 Microsecond as a decimal number, zero-padded on the left (range 393 000000-999999). This is an extension to the set of directives 394 available to `timezone`_. 395 396 %F 397 Equivalent to %Y-%m-%d (the ISO 8601 date format). 398 399 %G 400 The ISO 8601 week-based year with century as a decimal number. 401 The 4-digit year corresponding to the ISO week number (see %V). 402 This has the same format and value as %Y, except that if the 403 ISO week number belongs to the previous or next year, that year 404 is used instead. 405 406 %g 407 Like %G, but without century, that is, with a 2-digit year (00-99). 408 409 %h 410 Equivalent to %b. 411 412 %H 413 The hour as a decimal number using a 24-hour clock (range 00 414 to 23). 415 416 %I 417 The hour as a decimal number using a 12-hour clock (range 01 418 to 12). 419 420 %j 421 The day of the year as a decimal number (range 001 to 366). 422 423 %k 424 The hour (24-hour clock) as a decimal number (range 0 to 23). 425 Single digits are preceded by a blank. (See also %H.) 426 427 %l 428 The hour (12-hour clock) as a decimal number (range 1 to 12). 429 Single digits are preceded by a blank. (See also %I.) (TZ) 430 431 %m 432 The month as a decimal number (range 01 to 12). 433 434 %M 435 The minute as a decimal number (range 00 to 59). 436 437 %n 438 A newline character. Bokeh text does not currently support 439 newline characters. 440 441 %N 442 Nanosecond as a decimal number, zero-padded on the left (range 443 000000000-999999999). Supports a padding width specifier, i.e. 444 %3N displays 3 leftmost digits. However, this is only accurate 445 to the millisecond level of precision due to limitations of 446 `timezone`_. 447 448 %p 449 Either "AM" or "PM" according to the given time value, or the 450 corresponding strings for the current locale. Noon is treated 451 as "PM" and midnight as "AM". 452 453 %P 454 Like %p but in lowercase: "am" or "pm" or a corresponding 455 string for the current locale. 456 457 %r 458 The time in a.m. or p.m. notation. In the POSIX locale this 459 is equivalent to %I:%M:%S %p. 460 461 %R 462 The time in 24-hour notation (%H:%M). For a version including 463 the seconds, see %T below. 464 465 %s 466 The number of seconds since the Epoch, 1970-01-01 00:00:00 467 +0000 (UTC). 468 469 %S 470 The second as a decimal number (range 00 to 60). (The range 471 is up to 60 to allow for occasional leap seconds.) 472 473 %t 474 A tab character. Bokeh text does not currently support tab 475 characters. 476 477 %T 478 The time in 24-hour notation (%H:%M:%S). 479 480 %u 481 The day of the week as a decimal, range 1 to 7, Monday being 1. 482 See also %w. 483 484 %U 485 The week number of the current year as a decimal number, range 486 00 to 53, starting with the first Sunday as the first day of 487 week 01. See also %V and %W. 488 489 %V 490 The ISO 8601 week number (see NOTES) of the current year as a 491 decimal number, range 01 to 53, where week 1 is the first week 492 that has at least 4 days in the new year. See also %U and %W. 493 494 %w 495 The day of the week as a decimal, range 0 to 6, Sunday being 0. 496 See also %u. 497 498 %W 499 The week number of the current year as a decimal number, range 500 00 to 53, starting with the first Monday as the first day of 501 week 01. 502 503 %x 504 The preferred date representation for the current locale 505 without the time. 506 507 %X 508 The preferred time representation for the current locale 509 without the date. 510 511 %y 512 The year as a decimal number without a century (range 00 to 99). 513 514 %Y 515 The year as a decimal number including the century. 516 517 %z 518 The +hhmm or -hhmm numeric timezone (that is, the hour and 519 minute offset from UTC). 520 521 %Z 522 The timezone name or abbreviation. 523 524 %% 525 A literal '%' character. 526 527 .. warning:: 528 The client library BokehJS uses the `timezone`_ library to 529 format datetimes. The inclusion of the list below is based on the 530 claim that `timezone`_ makes to support "the full compliment 531 of GNU date format specifiers." However, this claim has not 532 been tested exhaustively against this list. If you find formats 533 that do not function as expected, please submit a `github issue`_, 534 so that the documentation can be updated appropriately. 535 536 .. _strftime: http://man7.org/linux/man-pages/man3/strftime.3.html 537 .. _timezone: http://bigeasy.github.io/timezone/ 538 .. _github issue: https://github.com/bokeh/bokeh/issues 539 540 ''' 541 microseconds = List(String, 542 help=_DATETIME_TICK_FORMATTER_HELP("``microseconds``"), 543 default=['%fus']).accepts(String, lambda fmt: [fmt]) 544 545 milliseconds = List(String, 546 help=_DATETIME_TICK_FORMATTER_HELP("``milliseconds``"), 547 default=['%3Nms', '%S.%3Ns']).accepts(String, lambda fmt: [fmt]) 548 549 seconds = List(String, 550 help=_DATETIME_TICK_FORMATTER_HELP("``seconds``"), 551 default=['%Ss']).accepts(String, lambda fmt: [fmt]) 552 553 minsec = List(String, 554 help=_DATETIME_TICK_FORMATTER_HELP("``minsec`` (for combined minutes and seconds)"), 555 default=[':%M:%S']).accepts(String, lambda fmt: [fmt]) 556 557 minutes = List(String, 558 help=_DATETIME_TICK_FORMATTER_HELP("``minutes``"), 559 default=[':%M', '%Mm']).accepts(String, lambda fmt: [fmt]) 560 561 hourmin = List(String, 562 help=_DATETIME_TICK_FORMATTER_HELP("``hourmin`` (for combined hours and minutes)"), 563 default=['%H:%M']).accepts(String, lambda fmt: [fmt]) 564 565 hours = List(String, 566 help=_DATETIME_TICK_FORMATTER_HELP("``hours``"), 567 default=['%Hh', '%H:%M']).accepts(String, lambda fmt: [fmt]) 568 569 days = List(String, 570 help=_DATETIME_TICK_FORMATTER_HELP("``days``"), 571 default=['%m/%d', '%a%d']).accepts(String, lambda fmt: [fmt]) 572 573 months = List(String, 574 help=_DATETIME_TICK_FORMATTER_HELP("``months``"), 575 default=['%m/%Y', '%b %Y']).accepts(String, lambda fmt: [fmt]) 576 577 years = List(String, 578 help=_DATETIME_TICK_FORMATTER_HELP("``years``"), 579 default=['%Y']).accepts(String, lambda fmt: [fmt]) 580 581#----------------------------------------------------------------------------- 582# Dev API 583#----------------------------------------------------------------------------- 584 585#----------------------------------------------------------------------------- 586# Code 587#----------------------------------------------------------------------------- 588 589# This is to automate documentation of DatetimeTickFormatter formats and their defaults 590_df = DatetimeTickFormatter() 591_df_fields = ['microseconds', 'milliseconds', 'seconds', 'minsec', 'minutes', 'hourmin', 'hours', 'days', 'months', 'years'] 592_df_defaults = _df.properties_with_values() 593_df_defaults_string = "\n\n ".join("%s = %s" % (name, _df_defaults[name]) for name in _df_fields) 594 595DatetimeTickFormatter.__doc__ = format_docstring(DatetimeTickFormatter.__doc__, defaults=_df_defaults_string) 596del _df, _df_fields, _df_defaults, _df_defaults_string 597