data.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. """
  2. @package animation.data
  3. @brief animation data structures
  4. Classes:
  5. - data::AnimationData
  6. - data::AnimationLayer
  7. (C) 2013 by the GRASS Development Team
  8. This program is free software under the GNU General Public License
  9. (>=v2). Read the file COPYING that comes with GRASS for details.
  10. @author Anna Petrasova <kratochanna gmail.com>
  11. """
  12. import os
  13. import copy
  14. from grass.script.utils import parse_key_val
  15. from grass.script import core as gcore
  16. from core.gcmd import GException
  17. from animation.nviztask import NvizTask
  18. from animation.utils import validateMapNames, getRegisteredMaps, \
  19. checkSeriesCompatibility, validateTimeseriesName, interpolate
  20. from core.layerlist import LayerList, Layer
  21. class AnimationData(object):
  22. def __init__(self):
  23. self._name = None
  24. self._windowIndex = 0
  25. self._layerList = None
  26. # only this stds is taken into account for time computations
  27. # if there are any stds at all
  28. self._firstStdsNameType = None
  29. self._mapCount = None
  30. self._cmdMatrix = None
  31. self._viewModes = [('2d', _("2D view")),
  32. ('3d', _("3D view"))]
  33. self.viewMode = '2d'
  34. self.nvizTask = NvizTask()
  35. self._nvizParameters = self.nvizTask.ListMapParameters()
  36. self.nvizParameter = self._nvizParameters[0]
  37. self.workspaceFile = None
  38. self.legendCmd = None
  39. self._startRegion = None
  40. self._endRegion = None
  41. self._zoomRegionValue = None
  42. self._regions = None
  43. def GetName(self):
  44. return self._name
  45. def SetName(self, name):
  46. if name == '':
  47. raise ValueError(_("No animation name selected."))
  48. self._name = name
  49. name = property(fget=GetName, fset=SetName)
  50. def GetWindowIndex(self):
  51. return self._windowIndex
  52. def SetWindowIndex(self, windowIndex):
  53. self._windowIndex = windowIndex
  54. windowIndex = property(fget=GetWindowIndex, fset=SetWindowIndex)
  55. def SetLayerList(self, layerList):
  56. """
  57. Throws GException if layer list's combination of stds is not valid.
  58. """
  59. mapSeriesList = []
  60. timeseriesList = []
  61. for layer in layerList:
  62. if layer.active and hasattr(layer, 'maps'):
  63. if layer.mapType in ('strds', 'stvds', 'str3ds'):
  64. timeseriesList.append((layer.name, layer.mapType))
  65. self._firstStdsNameType = layer.name, layer.mapType
  66. else:
  67. mapSeriesList.append((layer.maps))
  68. if not timeseriesList:
  69. self._firstStdsNameType = None, None
  70. # this throws GException
  71. count = checkSeriesCompatibility(mapSeriesList=mapSeriesList,
  72. timeseriesList=timeseriesList)
  73. self._mapCount = count
  74. self._layerList = layerList
  75. def GetLayerList(self):
  76. return self._layerList
  77. layerList = property(fget=GetLayerList, fset=SetLayerList)
  78. def GetFirstStdsNameType(self):
  79. return self._firstStdsNameType
  80. firstStdsNameType = property(fget=GetFirstStdsNameType)
  81. def GetMapCount(self):
  82. return self._mapCount
  83. mapCount = property(fget=GetMapCount)
  84. def GetCmdMatrix(self):
  85. return self._cmdMatrix
  86. def SetCmdMatrix(self, cmdMatrix):
  87. self._cmdMatrix = cmdMatrix
  88. cmdMatrix = property(fget=GetCmdMatrix, fset=SetCmdMatrix)
  89. def GetWorkspaceFile(self):
  90. return self._workspaceFile
  91. def SetWorkspaceFile(self, fileName):
  92. if fileName is None:
  93. self._workspaceFile = None
  94. return
  95. if fileName == '':
  96. raise ValueError(_("No workspace file selected."))
  97. if not os.path.exists(fileName):
  98. raise IOError(_("File %s not found") % fileName)
  99. self._workspaceFile = fileName
  100. self.nvizTask.Load(self.workspaceFile)
  101. workspaceFile = property(fget=GetWorkspaceFile, fset=SetWorkspaceFile)
  102. def SetDefaultValues(self, windowIndex, animationIndex):
  103. self.windowIndex = windowIndex
  104. self.name = _("Animation %d") % (animationIndex + 1)
  105. self.layerList = LayerList()
  106. def GetNvizParameters(self):
  107. return self._nvizParameters
  108. nvizParameters = property(fget=GetNvizParameters)
  109. def GetNvizParameter(self):
  110. return self._nvizParameter
  111. def SetNvizParameter(self, param):
  112. self._nvizParameter = param
  113. nvizParameter = property(fget=GetNvizParameter, fset=SetNvizParameter)
  114. def GetViewMode(self):
  115. return self._viewMode
  116. def SetViewMode(self, mode):
  117. self._viewMode = mode
  118. viewMode = property(fget=GetViewMode, fset=SetViewMode)
  119. def GetViewModes(self):
  120. return self._viewModes
  121. viewModes = property(fget=GetViewModes)
  122. def SetLegendCmd(self, cmd):
  123. self._legendCmd = cmd
  124. def GetLegendCmd(self):
  125. return self._legendCmd
  126. legendCmd = property(fget=GetLegendCmd, fset=SetLegendCmd)
  127. def GetNvizCommands(self):
  128. if not self.workspaceFile or not self._layerList:
  129. return []
  130. cmds = self.nvizTask.GetCommandSeries(layerList=self._layerList,
  131. paramName=self.nvizParameter)
  132. region = self.nvizTask.GetRegion()
  133. return {'commands': cmds, 'region': region}
  134. def SetStartRegion(self, region):
  135. self._startRegion = region
  136. def GetStartRegion(self):
  137. return self._startRegion
  138. startRegion = property(fset=SetStartRegion, fget=GetStartRegion)
  139. def SetEndRegion(self, region):
  140. self._endRegion = region
  141. def GetEndRegion(self):
  142. return self._endRegion
  143. endRegion = property(fset=SetEndRegion, fget=GetEndRegion)
  144. def SetZoomRegionValue(self, value):
  145. self._zoomRegionValue = value
  146. def GetZoomRegionValue(self):
  147. return self._zoomRegionValue
  148. zoomRegionValue = property(
  149. fset=SetZoomRegionValue,
  150. fget=GetZoomRegionValue)
  151. def GetRegions(self):
  152. self._computeRegions(self._mapCount, self._startRegion,
  153. self._endRegion, self._zoomRegionValue)
  154. return self._regions
  155. def _computeRegions(
  156. self, count, startRegion, endRegion=None,
  157. zoomValue=None):
  158. """Computes regions based on start region and end region or zoom value
  159. for each of the animation frames."""
  160. region = dict(gcore.region()) # cast to dict, otherwise deepcopy error
  161. if startRegion:
  162. region = dict(parse_key_val(gcore.read_command('g.region',
  163. flags='gu',
  164. region=startRegion),
  165. val_type=float))
  166. del region['cells']
  167. del region['cols']
  168. del region['rows']
  169. if 'projection' in region:
  170. del region['projection']
  171. if 'zone' in region:
  172. del region['zone']
  173. regions = []
  174. for i in range(self._mapCount):
  175. regions.append(copy.copy(region))
  176. self._regions = regions
  177. if not (endRegion or zoomValue):
  178. return
  179. startRegionDict = parse_key_val(
  180. gcore.read_command(
  181. 'g.region',
  182. flags='gu',
  183. region=startRegion),
  184. val_type=float)
  185. if endRegion:
  186. endRegionDict = parse_key_val(
  187. gcore.read_command(
  188. 'g.region',
  189. flags='gu',
  190. region=endRegion),
  191. val_type=float)
  192. for key in ('n', 's', 'e', 'w', 'nsres', 'ewres'):
  193. values = interpolate(
  194. startRegionDict[key],
  195. endRegionDict[key],
  196. self._mapCount)
  197. for value, region in zip(values, regions):
  198. region[key] = value
  199. elif zoomValue:
  200. for i in range(self._mapCount):
  201. regions[i]['n'] -= zoomValue[0] * i
  202. regions[i]['e'] -= zoomValue[1] * i
  203. regions[i]['s'] += zoomValue[0] * i
  204. regions[i]['w'] += zoomValue[1] * i
  205. # handle cases when north < south and similarly EW
  206. if regions[i]['n'] < regions[i]['s'] or \
  207. regions[i]['e'] < regions[i]['w']:
  208. regions[i] = regions[i - 1]
  209. self._regions = regions
  210. def __repr__(self):
  211. return "%s(%r)" % (self.__class__, self.__dict__)
  212. class AnimLayer(Layer):
  213. """Animation layer allows adding either space-time dataset
  214. or series of maps."""
  215. def __init__(self):
  216. Layer.__init__(self)
  217. self._mapTypes.extend(['strds', 'stvds', 'str3ds'])
  218. self._maps = []
  219. def SetName(self, name):
  220. if not self.hidden:
  221. if self._mapType is None:
  222. raise ValueError(
  223. "To set layer name, the type of layer must be specified.")
  224. if self._mapType in ('strds', 'stvds', 'str3ds'):
  225. try:
  226. name = validateTimeseriesName(name, self._mapType)
  227. self._maps = getRegisteredMaps(name, self._mapType)
  228. except (GException, gcore.ScriptError) as e:
  229. raise ValueError(str(e))
  230. else:
  231. self._maps = validateMapNames(name.split(','), self._mapType)
  232. self._name = name
  233. self.label = name
  234. def GetName(self):
  235. return self._name
  236. name = property(fget=GetName, fset=SetName)
  237. def GetMaps(self):
  238. return self._maps
  239. maps = property(fget=GetMaps)