1
""" Defines the LogMapper and InvalidDataRangeException classes.
3
# Major library imports
4
from numpy import array, isnan, log, log10, exp, zeros, sometrue,\
7
# Enthought library imports
8
from enthought.traits.api import Bool, Float
10
#Local relative imports
11
from base_1d_mapper import Base1DMapper
16
class InvalidDataRangeException(Exception):
19
class LogMapper(Base1DMapper):
20
""" Defines a 1-D logarithmic scale mapping from a 1-D region in input
21
space to a 1-D region in output space.
24
# The value to map when asked to map values <= LOG_MINIMUM to screen space.
25
fill_value = Float(1.0)
27
#------------------------------------------------------------------------
29
#------------------------------------------------------------------------
31
_inter_scale = Float(0.0)
32
_inter_offset = Float(0.0)
33
_screen_scale = Float(0.0)
34
_screen_offset = Float(0.0)
35
_null_screen_range = Bool(False)
36
_null_data_range = Bool(False)
38
#------------------------------------------------------------------------
40
#------------------------------------------------------------------------
42
def map_screen(self, data_array):
43
""" map_screen(data_array) -> screen_array
45
Overrides AbstractMapper. Maps values from data space to screen space.
47
#First convert to a [0,1] space, then to the screen space
48
if not self._cache_valid:
50
if self._inter_scale == 0.0:
51
intermediate = data_array*0.0
54
mask = (data_array <= LOG_MINIMUM) | isnan(data_array)
56
data_array = array(data_array, copy=True, ndmin=1)
57
data_array[mask] = self.fill_value
58
intermediate = (log(data_array) - self._inter_offset)/self._inter_scale
60
intermediate = zeros(len(data_array))
62
return intermediate * self._screen_scale + self._screen_offset
64
def map_data(self, screen_val):
65
""" map_data(screen_val) -> data_val
67
Overrides Abstract Mapper. Maps values from screen space into data space.
69
if not self._cache_valid:
71
if self._null_screen_range or self._null_data_range:
72
return array([self.range.low])
73
#First convert to a [0,1] space, then to the data space
74
intermediate = (screen_val-self._screen_offset)/self._screen_scale
75
return exp(self._inter_scale*intermediate + self._inter_offset)
77
def map_data_array(self, screen_vals):
78
return self.map_data(screen_vals)
80
#------------------------------------------------------------------------
82
#------------------------------------------------------------------------
84
def _get_safe_scale(self, range):
86
orig_high = range.high
87
if orig_low < LOG_MINIMUM:
92
if orig_high < LOG_MINIMUM:
98
if low == LOG_MINIMUM:
103
low = pow(10, floor(log_val))
104
if ceil(log_val) != floor(log_val):
105
high = pow(10, ceil(log_val))
107
high = pow(10, ceil(log_val) + 1)
111
def _compute_scale(self):
112
if self._cache_valid:
115
if self.range is None:
116
self._cache_valid = False
119
screen_range = self.high_pos - self.low_pos
120
if screen_range == 0.0:
121
self._null_screen_range = True
123
# Get dataspace low and high from the range that are "safe" for a
124
# logarithmic mapper, i.e. constrained to be between LOG_MINIMUM and inf.
125
low, high = self._get_safe_scale(self.range)
127
self._null_data_range = True
129
if low == LOG_MINIMUM:
130
self._inter_scale = log(high)
131
self._inter_offset = 0.0
133
self._inter_scale = log(high)-log(low)
134
self._inter_offset = log(low)
135
self._screen_scale = screen_range
136
self._screen_offset = self.low_pos
138
self._cache_valid = True