UnitDbl.py 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. """UnitDbl module."""
  2. import operator
  3. from matplotlib import cbook
  4. class UnitDbl:
  5. """Class UnitDbl in development.
  6. """
  7. # Unit conversion table. Small subset of the full one but enough
  8. # to test the required functions. First field is a scale factor to
  9. # convert the input units to the units of the second field. Only
  10. # units in this table are allowed.
  11. allowed = {
  12. "m": (0.001, "km"),
  13. "km": (1, "km"),
  14. "mile": (1.609344, "km"),
  15. "rad": (1, "rad"),
  16. "deg": (1.745329251994330e-02, "rad"),
  17. "sec": (1, "sec"),
  18. "min": (60.0, "sec"),
  19. "hour": (3600, "sec"),
  20. }
  21. _types = {
  22. "km": "distance",
  23. "rad": "angle",
  24. "sec": "time",
  25. }
  26. def __init__(self, value, units):
  27. """Create a new UnitDbl object.
  28. Units are internally converted to km, rad, and sec. The only
  29. valid inputs for units are [m, km, mile, rad, deg, sec, min, hour].
  30. The field UnitDbl.value will contain the converted value. Use
  31. the convert() method to get a specific type of units back.
  32. = ERROR CONDITIONS
  33. - If the input units are not in the allowed list, an error is thrown.
  34. = INPUT VARIABLES
  35. - value The numeric value of the UnitDbl.
  36. - units The string name of the units the value is in.
  37. """
  38. data = cbook._check_getitem(self.allowed, units=units)
  39. self._value = float(value * data[0])
  40. self._units = data[1]
  41. def convert(self, units):
  42. """Convert the UnitDbl to a specific set of units.
  43. = ERROR CONDITIONS
  44. - If the input units are not in the allowed list, an error is thrown.
  45. = INPUT VARIABLES
  46. - units The string name of the units to convert to.
  47. = RETURN VALUE
  48. - Returns the value of the UnitDbl in the requested units as a floating
  49. point number.
  50. """
  51. if self._units == units:
  52. return self._value
  53. data = cbook._check_getitem(self.allowed, units=units)
  54. if self._units != data[1]:
  55. raise ValueError(f"Error trying to convert to different units.\n"
  56. f" Invalid conversion requested.\n"
  57. f" UnitDbl: {self}\n"
  58. f" Units: {units}\n")
  59. return self._value / data[0]
  60. def __abs__(self):
  61. """Return the absolute value of this UnitDbl."""
  62. return UnitDbl(abs(self._value), self._units)
  63. def __neg__(self):
  64. """Return the negative value of this UnitDbl."""
  65. return UnitDbl(-self._value, self._units)
  66. def __bool__(self):
  67. """Return the truth value of a UnitDbl."""
  68. return bool(self._value)
  69. def __eq__(self, rhs):
  70. return self._cmp(rhs, operator.eq)
  71. def __ne__(self, rhs):
  72. return self._cmp(rhs, operator.ne)
  73. def __lt__(self, rhs):
  74. return self._cmp(rhs, operator.lt)
  75. def __le__(self, rhs):
  76. return self._cmp(rhs, operator.le)
  77. def __gt__(self, rhs):
  78. return self._cmp(rhs, operator.gt)
  79. def __ge__(self, rhs):
  80. return self._cmp(rhs, operator.ge)
  81. def _cmp(self, rhs, op):
  82. """Compare two UnitDbl's.
  83. = ERROR CONDITIONS
  84. - If the input rhs units are not the same as our units,
  85. an error is thrown.
  86. = INPUT VARIABLES
  87. - rhs The UnitDbl to compare against.
  88. - op The function to do the comparison
  89. = RETURN VALUE
  90. - Returns op(self, rhs)
  91. """
  92. self.checkSameUnits(rhs, "compare")
  93. return op(self._value, rhs._value)
  94. def __add__(self, rhs):
  95. """Add two UnitDbl's.
  96. = ERROR CONDITIONS
  97. - If the input rhs units are not the same as our units,
  98. an error is thrown.
  99. = INPUT VARIABLES
  100. - rhs The UnitDbl to add.
  101. = RETURN VALUE
  102. - Returns the sum of ourselves and the input UnitDbl.
  103. """
  104. self.checkSameUnits(rhs, "add")
  105. return UnitDbl(self._value + rhs._value, self._units)
  106. def __sub__(self, rhs):
  107. """Subtract two UnitDbl's.
  108. = ERROR CONDITIONS
  109. - If the input rhs units are not the same as our units,
  110. an error is thrown.
  111. = INPUT VARIABLES
  112. - rhs The UnitDbl to subtract.
  113. = RETURN VALUE
  114. - Returns the difference of ourselves and the input UnitDbl.
  115. """
  116. self.checkSameUnits(rhs, "subtract")
  117. return UnitDbl(self._value - rhs._value, self._units)
  118. def __mul__(self, rhs):
  119. """Scale a UnitDbl by a value.
  120. = INPUT VARIABLES
  121. - rhs The scalar to multiply by.
  122. = RETURN VALUE
  123. - Returns the scaled UnitDbl.
  124. """
  125. return UnitDbl(self._value * rhs, self._units)
  126. def __rmul__(self, lhs):
  127. """Scale a UnitDbl by a value.
  128. = INPUT VARIABLES
  129. - lhs The scalar to multiply by.
  130. = RETURN VALUE
  131. - Returns the scaled UnitDbl.
  132. """
  133. return UnitDbl(self._value * lhs, self._units)
  134. def __div__(self, rhs):
  135. """Divide a UnitDbl by a value.
  136. = INPUT VARIABLES
  137. - rhs The scalar to divide by.
  138. = RETURN VALUE
  139. - Returns the scaled UnitDbl.
  140. """
  141. return UnitDbl(self._value / rhs, self._units)
  142. def __str__(self):
  143. """Print the UnitDbl."""
  144. return "%g *%s" % (self._value, self._units)
  145. def __repr__(self):
  146. """Print the UnitDbl."""
  147. return "UnitDbl(%g, '%s')" % (self._value, self._units)
  148. def type(self):
  149. """Return the type of UnitDbl data."""
  150. return self._types[self._units]
  151. @staticmethod
  152. def range(start, stop, step=None):
  153. """Generate a range of UnitDbl objects.
  154. Similar to the Python range() method. Returns the range [
  155. start, stop) at the requested step. Each element will be a
  156. UnitDbl object.
  157. = INPUT VARIABLES
  158. - start The starting value of the range.
  159. - stop The stop value of the range.
  160. - step Optional step to use. If set to None, then a UnitDbl of
  161. value 1 w/ the units of the start is used.
  162. = RETURN VALUE
  163. - Returns a list containing the requested UnitDbl values.
  164. """
  165. if step is None:
  166. step = UnitDbl(1, start._units)
  167. elems = []
  168. i = 0
  169. while True:
  170. d = start + i * step
  171. if d >= stop:
  172. break
  173. elems.append(d)
  174. i += 1
  175. return elems
  176. @cbook.deprecated("3.2")
  177. def checkUnits(self, units):
  178. """Check to see if some units are valid.
  179. = ERROR CONDITIONS
  180. - If the input units are not in the allowed list, an error is thrown.
  181. = INPUT VARIABLES
  182. - units The string name of the units to check.
  183. """
  184. if units not in self.allowed:
  185. raise ValueError("Input units '%s' are not one of the supported "
  186. "types of %s" % (
  187. units, list(self.allowed.keys())))
  188. def checkSameUnits(self, rhs, func):
  189. """Check to see if units are the same.
  190. = ERROR CONDITIONS
  191. - If the units of the rhs UnitDbl are not the same as our units,
  192. an error is thrown.
  193. = INPUT VARIABLES
  194. - rhs The UnitDbl to check for the same units
  195. - func The name of the function doing the check.
  196. """
  197. if self._units != rhs._units:
  198. raise ValueError(f"Cannot {func} units of different types.\n"
  199. f"LHS: {self._units}\n"
  200. f"RHS: {rhs._units}")