data.py 9.7 KB

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