77 lines
2.3 KiB
Python
77 lines
2.3 KiB
Python
|
#!/usr/bin/env python
|
||
|
|
||
|
from datetime import datetime
|
||
|
|
||
|
import six
|
||
|
|
||
|
from leather.scales.base import Scale
|
||
|
from leather.ticks.score_time import ScoreTimeTicker
|
||
|
|
||
|
|
||
|
class Temporal(Scale):
|
||
|
"""
|
||
|
A scale that linearly maps date/datetime values from a domain to a range.
|
||
|
|
||
|
:param domain_min:
|
||
|
The minimum date/datetime of the input domain.
|
||
|
:param domain_max:
|
||
|
The maximum date/datetime of the input domain.
|
||
|
"""
|
||
|
def __init__(self, domain_min, domain_max):
|
||
|
if domain_min >= domain_max:
|
||
|
raise ValueError('Domain minimum must be less than domain maximum. Inverted domains are not currently supported.')
|
||
|
|
||
|
self._data_min = domain_min
|
||
|
self._data_max = domain_max
|
||
|
|
||
|
self._ticker = ScoreTimeTicker(self._data_min, self._data_max)
|
||
|
|
||
|
def contains(self, v):
|
||
|
"""
|
||
|
Return :code:`True` if a given value is contained within this scale's
|
||
|
domain.
|
||
|
"""
|
||
|
return self._data_min <= v <= self._data_max
|
||
|
|
||
|
def project(self, value, range_min, range_max):
|
||
|
"""
|
||
|
Project a value in this scale's domain to a target range.
|
||
|
"""
|
||
|
numerator = value - self._ticker.min
|
||
|
denominator = self._ticker.max - self._ticker.min
|
||
|
|
||
|
# Python 2 does not support timedelta division
|
||
|
if six.PY2:
|
||
|
if isinstance(self._ticker.min, datetime):
|
||
|
numerator = numerator.total_seconds()
|
||
|
denominator = denominator.total_seconds()
|
||
|
else:
|
||
|
numerator = float(numerator.days)
|
||
|
denominator = float(denominator.days)
|
||
|
|
||
|
pos = numerator / denominator
|
||
|
|
||
|
return ((range_max - range_min) * pos) + range_min
|
||
|
|
||
|
def project_interval(self, value, range_min, range_max):
|
||
|
"""
|
||
|
Project a value in this scale's domain to an interval in the target
|
||
|
range. This is used for places :class:`.Bars` and :class:`.Columns`.
|
||
|
"""
|
||
|
raise NotImplementedError
|
||
|
|
||
|
def ticks(self):
|
||
|
"""
|
||
|
Generate a series of ticks for this scale.
|
||
|
"""
|
||
|
return self._ticker.ticks
|
||
|
|
||
|
def format_tick(self, value, i, count):
|
||
|
"""
|
||
|
Format ticks for display.
|
||
|
|
||
|
This method is used as a default which will be ignored if the user
|
||
|
provides a custom tick formatter to the axis.
|
||
|
"""
|
||
|
return self._ticker.format_tick(value)
|