64 lines
1.9 KiB
Python
64 lines
1.9 KiB
Python
|
#!/usr/bin/env python
|
||
|
|
||
|
from decimal import Decimal
|
||
|
|
||
|
from leather.scales.base import Scale
|
||
|
|
||
|
|
||
|
class Ordinal(Scale):
|
||
|
"""
|
||
|
A scale that maps individual values (e.g. strings) to a range.
|
||
|
"""
|
||
|
def __init__(self, domain):
|
||
|
self._domain = domain
|
||
|
|
||
|
def contains(self, v):
|
||
|
"""
|
||
|
Return :code:`True` if a given value is contained within this scale's
|
||
|
displayed domain.
|
||
|
"""
|
||
|
return v in self._domain
|
||
|
|
||
|
def project(self, value, range_min, range_max):
|
||
|
"""
|
||
|
Project a value in this scale's domain to a target range.
|
||
|
"""
|
||
|
range_min = Decimal(range_min)
|
||
|
range_max = Decimal(range_max)
|
||
|
|
||
|
segments = len(self._domain)
|
||
|
segment_size = (range_max - range_min) / segments
|
||
|
|
||
|
try:
|
||
|
pos = range_min + (self._domain.index(value) * segment_size) + (segment_size / 2)
|
||
|
except ValueError:
|
||
|
raise ValueError('Value "%s" is not present in Ordinal scale domain' % value)
|
||
|
|
||
|
return pos
|
||
|
|
||
|
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`.
|
||
|
"""
|
||
|
range_min = Decimal(range_min)
|
||
|
range_max = Decimal(range_max)
|
||
|
|
||
|
segments = len(self._domain)
|
||
|
segment_size = (range_max - range_min) / segments
|
||
|
gap = segment_size / Decimal(20)
|
||
|
|
||
|
try:
|
||
|
a = range_min + (self._domain.index(value) * segment_size) + gap
|
||
|
b = range_min + ((self._domain.index(value) + 1) * segment_size) - gap
|
||
|
except ValueError:
|
||
|
raise ValueError('Value "%s" is not present in Ordinal scale domain' % value)
|
||
|
|
||
|
return (a, b)
|
||
|
|
||
|
def ticks(self):
|
||
|
"""
|
||
|
Generate a series of ticks for this scale.
|
||
|
"""
|
||
|
return self._domain
|