utils.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. """!
  2. @package animation.utils
  3. @brief Miscellaneous functions and enum classes
  4. Classes:
  5. - utils::TemporalMode
  6. - utils::TemporalType
  7. - utils::Orientation
  8. - utils::ReplayMode
  9. (C) 2013 by the GRASS Development Team
  10. This program is free software under the GNU General Public License
  11. (>=v2). Read the file COPYING that comes with GRASS for details.
  12. @author Anna Perasova <kratochanna gmail.com>
  13. """
  14. import os
  15. import wx
  16. import hashlib
  17. try:
  18. from PIL import Image
  19. hasPIL = True
  20. except ImportError:
  21. hasPIL = False
  22. import grass.temporal as tgis
  23. import grass.script as grass
  24. from core.gcmd import GException
  25. from core.utils import _
  26. class TemporalMode:
  27. TEMPORAL = 1
  28. NONTEMPORAL = 2
  29. class TemporalType:
  30. ABSOLUTE = 1
  31. RELATIVE = 2
  32. class Orientation:
  33. FORWARD = 1
  34. BACKWARD = 2
  35. class ReplayMode:
  36. ONESHOT = 1
  37. REVERSE = 2
  38. REPEAT = 3
  39. def validateTimeseriesName(timeseries, etype='strds'):
  40. """!Checks if space time dataset exists and completes missing mapset.
  41. Raises GException if dataset doesn't exist.
  42. """
  43. trastDict = tgis.tlist_grouped(etype)
  44. if timeseries.find("@") >= 0:
  45. nameShort, mapset = timeseries.split('@', 1)
  46. if nameShort in trastDict[mapset]:
  47. return timeseries
  48. else:
  49. raise GException(_("Space time dataset <%s> not found.") % timeseries)
  50. for mapset, names in trastDict.iteritems():
  51. if timeseries in names:
  52. return timeseries + "@" + mapset
  53. raise GException(_("Space time dataset <%s> not found.") % timeseries)
  54. def validateMapNames(names, etype):
  55. """!Checks if maps exist and completes missing mapset.
  56. Input is list of map names.
  57. Raises GException if map doesn't exist.
  58. """
  59. mapDict = grass.list_grouped(etype)
  60. newNames = []
  61. for name in names:
  62. if name.find("@") >= 0:
  63. nameShort, mapset = name.split('@', 1)
  64. if nameShort in mapDict[mapset]:
  65. newNames.append(name)
  66. else:
  67. raise GException(_("Map <%s> not found.") % name)
  68. else:
  69. found = False
  70. for mapset, mapNames in mapDict.iteritems():
  71. if name in mapNames:
  72. found = True
  73. newNames.append(name + "@" + mapset)
  74. if not found:
  75. raise GException(_("Map <%s> not found.") % name)
  76. return newNames
  77. def getRegisteredMaps(timeseries, etype):
  78. """!Returns list of maps registered in dataset.
  79. Can throw ScriptError if the dataset doesn't exist.
  80. """
  81. timeseriesMaps = []
  82. sp = tgis.open_old_space_time_dataset(timeseries, etype)
  83. rows = sp.get_registered_maps(columns="id", where=None, order="start_time")
  84. timeseriesMaps = []
  85. if rows:
  86. for row in rows:
  87. timeseriesMaps.append(row["id"])
  88. return timeseriesMaps
  89. def checkSeriesCompatibility(mapSeriesList=None, timeseriesList=None):
  90. """!Checks whether time series (map series and stds) are compatible,
  91. which means they have equal number of maps ad times (in case of stds).
  92. This is needed within layer list, not within the entire animation tool.
  93. Throws GException if these are incompatible.
  94. @return number of maps for animation
  95. """
  96. timeseriesInfo = {'count': set(), 'temporalType': set(), 'mapType': set(),
  97. 'mapTimes': set()}
  98. if timeseriesList:
  99. for stds, etype in timeseriesList:
  100. sp = tgis.open_old_space_time_dataset(stds, etype)
  101. mapType = sp.get_map_time() # interval, ...
  102. tempType = sp.get_initial_values()[0] # absolute
  103. timeseriesInfo['mapType'].add(mapType)
  104. timeseriesInfo['temporalType'].add(tempType)
  105. rows = sp.get_registered_maps_as_objects(where=None,
  106. order="start_time")
  107. if rows:
  108. times = []
  109. timeseriesInfo['count'].add(len(rows))
  110. for row in rows:
  111. if tempType == 'absolute':
  112. time = row.get_absolute_time()
  113. else:
  114. time = row.get_relative_time()
  115. times.append(time)
  116. timeseriesInfo['mapTimes'].add(tuple(times))
  117. else:
  118. timeseriesInfo['mapTimes'].add(None)
  119. timeseriesInfo['count'].add(None)
  120. if len(timeseriesInfo['count']) > 1:
  121. raise GException(_("The number of maps in space-time datasets "
  122. "has to be the same."))
  123. if len(timeseriesInfo['temporalType']) > 1:
  124. raise GException(_("The temporal type (absolute/relative) of space-time datasets "
  125. "has to be the same."))
  126. if len(timeseriesInfo['mapType']) > 1:
  127. raise GException(_("The map type (point/interval) of space-time datasets "
  128. "has to be the same."))
  129. if len(timeseriesInfo['mapTimes']) > 1:
  130. raise GException(_("The temporal extents of maps in space-time datasets "
  131. "have to be the same."))
  132. if mapSeriesList:
  133. count = set()
  134. for mapSeries in mapSeriesList:
  135. count.add(len(mapSeries))
  136. if len(count) > 1:
  137. raise GException(_("The number of maps to animate has to be "
  138. "the same for each map series."))
  139. if timeseriesList and list(count)[0] != list(timeseriesInfo['count'])[0]:
  140. raise GException(_("The number of maps to animate has to be "
  141. "the same as the number of maps in temporal dataset."))
  142. if mapSeriesList:
  143. return list(count)[0]
  144. if timeseriesList:
  145. return list(timeseriesInfo['count'])[0]
  146. def ComputeScaledRect(sourceSize, destSize):
  147. """!Fits source rectangle into destination rectangle
  148. by scaling and centering.
  149. @code
  150. >>> ComputeScaledRect(sourceSize = (10, 40), destSize = (100, 50))
  151. {'height': 50, 'scale': 1.25, 'width': 13, 'x': 44, 'y': 0}
  152. @endcode
  153. @param sourceSize size of source rectangle
  154. @param destSize size of destination rectangle
  155. """
  156. ratio1 = destSize[0] / float(sourceSize[0])
  157. ratio2 = destSize[1] / float(sourceSize[1])
  158. if ratio1 < ratio2:
  159. scale = ratio1
  160. width = int(sourceSize[0] * scale + 0.5)
  161. height = int(sourceSize[1] * scale + 0.5)
  162. x = 0
  163. y = int((destSize[1] - height) / 2. + 0.5)
  164. else:
  165. scale = ratio2
  166. width = int(sourceSize[0] * scale + 0.5)
  167. height = int(sourceSize[1] * scale + 0.5)
  168. y = 0
  169. x = int((destSize[0] - width) / 2. + 0.5)
  170. return {'width': width, 'height': height, 'x': x, 'y': y, 'scale': scale}
  171. def RenderText(text, font):
  172. """!Renderes text with given font to bitmap."""
  173. dc = wx.MemoryDC()
  174. dc.SetFont(font)
  175. w, h = dc.GetTextExtent(text)
  176. bmp = wx.EmptyBitmap(w + 2, h + 2)
  177. dc.SelectObject(bmp)
  178. dc.SetBrush(wx.TRANSPARENT_BRUSH)
  179. dc.SetBackgroundMode(wx.TRANSPARENT)
  180. dc.Clear()
  181. dc.DrawText(text, 1, 1)
  182. dc.SelectObject(wx.NullBitmap)
  183. return bmp
  184. def WxImageToPil(image):
  185. """!Converts wx.Image to PIL image"""
  186. pilImage = Image.new('RGB', (image.GetWidth(), image.GetHeight()))
  187. pilImage.fromstring(image.GetData())
  188. return pilImage
  189. def HashCmd(cmd):
  190. """!Returns a hash from command given as a list."""
  191. name = '_'.join(cmd)
  192. return hashlib.sha1(name).hexdigest()
  193. def HashCmds(cmds):
  194. """!Returns a hash from list of commands."""
  195. name = ';'.join([item for sublist in cmds for item in sublist])
  196. return hashlib.sha1(name).hexdigest()
  197. def GetFileFromCmd(dirname, cmd, extension='ppm'):
  198. """!Returns file path created as a hash from command."""
  199. return os.path.join(dirname, HashCmd(cmd) + '.' + extension)
  200. def GetFileFromCmds(dirname, cmds, extension='ppm'):
  201. """!Returns file path created as a hash from list of commands."""
  202. return os.path.join(dirname, HashCmds(cmds) + '.' + extension)
  203. def layerListToCmdsMatrix(layerList):
  204. """!Goes thru layerList and create matrix of commands
  205. for the composition of map series.:
  206. @returns matrix of cmds for composition
  207. """
  208. count = 0
  209. for layer in layerList:
  210. if layer.active and hasattr(layer, 'maps'):
  211. # assuming the consistency of map number is checked already
  212. count = len(layer.maps)
  213. break
  214. cmdsForComposition = []
  215. for layer in layerList:
  216. if not layer.active:
  217. continue
  218. if hasattr(layer, 'maps'):
  219. for i, part in enumerate(layer.cmd):
  220. if part.startswith('map='):
  221. cmd = layer.cmd[:]
  222. cmds = []
  223. for map_ in layer.maps:
  224. cmd[i] = 'map={}'.format(map_)
  225. cmds.append(cmd[:])
  226. cmdsForComposition.append(cmds)
  227. else:
  228. cmdsForComposition.append([layer.cmd] * count)
  229. return zip(*cmdsForComposition)
  230. def sampleCmdMatrixAndCreateNames(cmdMatrix, sampledSeries):
  231. """!Applies information from temporal sampling
  232. to the command matrix."""
  233. namesList = []
  234. j = -1
  235. lastName = ''
  236. for name in sampledSeries:
  237. if name is not None:
  238. if lastName != name:
  239. lastName = name
  240. j += 1
  241. namesList.append(HashCmds(cmdMatrix[j]))
  242. else:
  243. namesList.append(None)
  244. assert(j == len(cmdMatrix) - 1)
  245. return namesList