units.py 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. """!
  2. @package core.units
  3. @brief Units management
  4. @todo Probably will be replaced by Python ctypes fns in the near
  5. future(?)
  6. Usage:
  7. @code
  8. from core.units import Units
  9. @endcode
  10. Classes:
  11. - units::BaseUnits
  12. (C) 2009, 2011 by the GRASS Development Team
  13. This program is free software under the GNU General Public License
  14. (>=v2). Read the file COPYING that comes with GRASS for details.
  15. @author Martin Landa <landa.martin gmail.com>
  16. """
  17. import math
  18. if __name__ == '__main__':
  19. import os
  20. import sys
  21. gui_wx_path = os.path.join(os.getenv('GISBASE'), 'etc', 'gui', 'wxpython')
  22. if gui_wx_path not in sys.path:
  23. sys.path.append(gui_wx_path)
  24. from core.utils import _
  25. class BaseUnits:
  26. def __init__(self):
  27. self._units = dict()
  28. self._units['length'] = { 0 : { 'key' : 'mu', 'label' : _('map units') },
  29. 1 : { 'key' : 'me', 'label' : _('meters') },
  30. 2 : { 'key' : 'km', 'label' : _('kilometers') },
  31. 3 : { 'key' : 'mi', 'label' : _('miles') },
  32. 4 : { 'key' : 'ft', 'label' : _('feet') } }
  33. self._units['area'] = { 0 : { 'key' : 'mu', 'label' : _('sq map units') },
  34. 1 : { 'key' : 'me', 'label' : _('sq meters') },
  35. 2 : { 'key' : 'km', 'label' : _('sq kilometers') },
  36. 3 : { 'key' : 'ar', 'label' : _('acres') },
  37. 4 : { 'key' : 'ht', 'label' : _('hectares') } }
  38. def GetUnitsList(self, type):
  39. """!Get list of units (their labels)
  40. @param type units type ('length' or 'area')
  41. @return list of units labels
  42. """
  43. result = list()
  44. try:
  45. keys = self._units[type].keys()
  46. keys.sort()
  47. for idx in keys:
  48. result.append(self._units[type][idx]['label'])
  49. except KeyError:
  50. pass
  51. return result
  52. def GetUnitsKey(self, type, index):
  53. """!Get units key based on index
  54. @param type units type ('length' or 'area')
  55. @param index units index
  56. """
  57. return self._units[type][index]['key']
  58. def GetUnitsIndex(self, type, key):
  59. """!Get units index based on key
  60. @param type units type ('length' or 'area')
  61. @param key units key, e.g. 'me' for meters
  62. @return index
  63. """
  64. for k, u in self._units[type].iteritems():
  65. if u['key'] == key:
  66. return k
  67. return 0
  68. Units = BaseUnits()
  69. def ConvertValue(value, type, units):
  70. """!Convert value from map units to given units
  71. Inspired by vector/v.to.db/units.c
  72. @param value value to be converted
  73. @param type units type ('length', 'area')
  74. @param unit destination units
  75. """
  76. # get map units
  77. # TODO
  78. f = 1
  79. if type == 'length':
  80. if units == 'me':
  81. f = 1.0
  82. elif units == 'km':
  83. f = 1.0e-3
  84. elif units == 'mi':
  85. f = 6.21371192237334e-4
  86. elif units == 'ft':
  87. f = 3.28083989501312
  88. else: # -> area
  89. if units == 'me':
  90. f = 1.0
  91. elif units == 'km':
  92. f = 1.0e-6
  93. elif units == 'mi':
  94. f = 3.86102158542446e-7
  95. elif units == 'ft':
  96. f = 10.7639104167097
  97. elif units == 'ar':
  98. f = 2.47105381467165e-4
  99. elif units == 'ht':
  100. f = 1.0e-4
  101. return f * value
  102. def formatDist(distance, mapunits):
  103. """!Formats length numbers and units in a nice way.
  104. Formats length numbers and units as a function of length.
  105. @code
  106. >>> formatDist(20.56915, 'metres')
  107. (20.57, 'm')
  108. >>> formatDist(6983.4591, 'metres')
  109. (6.983, 'km')
  110. >>> formatDist(0.59, 'feet')
  111. (0.59, 'ft')
  112. >>> formatDist(8562, 'feet')
  113. (1.622, 'miles')
  114. >>> formatDist(0.48963, 'degrees')
  115. (29.38, 'min')
  116. >>> formatDist(20.2546, 'degrees')
  117. (20.25, 'deg')
  118. >>> formatDist(82.146, 'unknown')
  119. (82.15, 'meters')
  120. @endcode
  121. Accepted map units are 'meters', 'metres', 'feet', 'degree'. Any
  122. other units will be considered as meters (output 'meters').
  123. @param distance map units
  124. @param mapunits map units
  125. From code by Hamish Bowman Grass Development Team 2006.
  126. """
  127. if mapunits == 'metres':
  128. mapunits = 'meters'
  129. outunits = mapunits
  130. distance = float(distance)
  131. divisor = 1.0
  132. # figure out which units to use
  133. if mapunits == 'meters':
  134. if distance > 2500.0:
  135. outunits = 'km'
  136. divisor = 1000.0
  137. else:
  138. outunits = 'm'
  139. elif mapunits == 'feet':
  140. # nano-bug: we match any "feet", but US Survey feet is really
  141. # 5279.9894 per statute mile, or 10.6' per 1000 miles. As >1000
  142. # miles the tick markers are rounded to the nearest 10th of a
  143. # mile (528'), the difference in foot flavours is ignored.
  144. if distance > 5280.0:
  145. outunits = 'miles'
  146. divisor = 5280.0
  147. else:
  148. outunits = 'ft'
  149. elif 'degree' in mapunits:
  150. # was: 'degree' in mapunits and not haveCtypes (for unknown reason)
  151. if distance < 1:
  152. outunits = 'min'
  153. divisor = (1/60.0)
  154. else:
  155. outunits = 'deg'
  156. else:
  157. outunits = 'meters'
  158. # format numbers in a nice way
  159. if (distance / divisor) >= 2500.0:
  160. outdistance = round(distance / divisor)
  161. elif (distance / divisor) >= 1000.0:
  162. outdistance = round(distance / divisor, 1)
  163. elif (distance / divisor) > 0.0:
  164. outdistance = round(distance / divisor,
  165. int(math.ceil(3 - math.log10(distance / divisor))))
  166. else:
  167. outdistance = float(distance / divisor)
  168. return (outdistance, outunits)
  169. def doc_test():
  170. """Tests the module using doctest
  171. @return a number of failed tests
  172. """
  173. import doctest
  174. from core.utils import do_doctest_gettext_workaround
  175. do_doctest_gettext_workaround()
  176. return doctest.testmod().failed
  177. if __name__ == '__main__':
  178. sys.exit(doc_test())