temporal_manager.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. """!
  2. @package animation.temporal_manager
  3. @brief Management of temporal datasets used in animation
  4. Classes:
  5. - temporal_manager::DataMode
  6. - temporal_manager::GranularityMode
  7. - temporal_manager::TemporalManager
  8. (C) 2012 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 Kratochvilova <kratochanna gmail.com>
  12. """
  13. import os
  14. import sys
  15. if __name__ == '__main__':
  16. sys.path.append(os.path.join(os.environ['GISBASE'], "etc", "gui", "wxpython"))
  17. import grass.script as grass
  18. import grass.temporal as tgis
  19. from core.gcmd import GException
  20. from utils import validateTimeseriesName, TemporalType
  21. class DataMode:
  22. SIMPLE = 1
  23. MULTIPLE = 2
  24. class GranularityMode:
  25. ONE_UNIT = 1
  26. ORIGINAL = 2
  27. class TemporalManager(object):
  28. """!Class for temporal data processing."""
  29. def __init__(self):
  30. self.timeseriesList = []
  31. self.timeseriesInfo = {}
  32. self.dataMode = None
  33. self.temporalType = None
  34. self.granularityMode = GranularityMode.ONE_UNIT
  35. # Make sure the temporal database exists
  36. tgis.init()
  37. def GetTemporalType(self):
  38. """!Get temporal type (TemporalType.ABSOLUTE, TemporalType.RELATIVE)"""
  39. return self._temporalType
  40. def SetTemporalType(self, ttype):
  41. self._temporalType = ttype
  42. temporalType = property(fget = GetTemporalType, fset = SetTemporalType)
  43. def AddTimeSeries(self, timeseries, etype):
  44. """!Add space time dataset
  45. and collect basic information about it.
  46. Raises GException (e.g. with invalid topology).
  47. @param timeseries name of timeseries (with or without mapset)
  48. @param etype element type (strds, stvds)
  49. """
  50. self._gatherInformation(timeseries, etype, self.timeseriesList, self.timeseriesInfo)
  51. def EvaluateInputData(self):
  52. """!Checks if all timeseries are compatible (raises GException).
  53. Sets internal variables.
  54. """
  55. timeseriesCount = len(self.timeseriesList)
  56. if timeseriesCount == 1:
  57. self.dataMode = DataMode.SIMPLE
  58. elif timeseriesCount > 1:
  59. self.dataMode = DataMode.MULTIPLE
  60. else:
  61. self.dataMode = None
  62. ret, message = self._setTemporalState()
  63. if not ret:
  64. raise GException(message)
  65. if message: # warning
  66. return message
  67. return None
  68. def _setTemporalState(self):
  69. # check for absolute x relative
  70. absolute, relative = 0, 0
  71. for infoDict in self.timeseriesInfo.values():
  72. if infoDict['temporal_type'] == 'absolute':
  73. absolute += 1
  74. else:
  75. relative += 1
  76. if bool(absolute) == bool(relative):
  77. message = _("It is not allowed to display data with different temporal types (absolute and relative).")
  78. return False, message
  79. if absolute:
  80. self.temporalType = TemporalType.ABSOLUTE
  81. else:
  82. self.temporalType = TemporalType.RELATIVE
  83. # check for units for relative type
  84. if relative:
  85. units = set()
  86. for infoDict in self.timeseriesInfo.values():
  87. units.add(infoDict['unit'])
  88. if len(units) > 1:
  89. message = _("It is not allowed to display data with different units (%s).") % ','.join(units)
  90. return False, message
  91. # check for interval x point
  92. interval, point = 0, 0
  93. for infoDict in self.timeseriesInfo.values():
  94. if infoDict['map_time'] == 'interval':
  95. interval += 1
  96. else:
  97. point += 1
  98. if bool(interval) == bool(point):
  99. message = _("You are going to display data with different temporal types of maps (interval and point)."
  100. " It is recommended to use data of one temporal type to avoid confusion.")
  101. return True, message # warning
  102. return True, None
  103. def GetGranularity(self):
  104. """!Returns temporal granularity of currently loaded timeseries."""
  105. if self.dataMode == DataMode.SIMPLE:
  106. gran = self.timeseriesInfo[self.timeseriesList[0]]['granularity']
  107. if 'unit' in self.timeseriesInfo[self.timeseriesList[0]]: # relative
  108. if self.granularityMode == GranularityMode.ONE_UNIT:
  109. gran = 1
  110. gran = "%(gran)s %(unit)s" % {'gran': gran,
  111. 'unit': self.timeseriesInfo[self.timeseriesList[0]]['unit']}
  112. elif self.granularityMode == GranularityMode.ONE_UNIT: # absolute
  113. number, unit = gran.split()
  114. gran = "1 %s" % unit
  115. return gran
  116. if self.dataMode == DataMode.MULTIPLE:
  117. return self._getCommonGranularity()
  118. def _getCommonGranularity(self):
  119. allMaps = []
  120. for dataset in self.timeseriesList:
  121. maps = self.timeseriesInfo[dataset]['maps']
  122. allMaps.extend(maps)
  123. if self.temporalType == TemporalType.ABSOLUTE:
  124. gran = tgis.compute_absolute_time_granularity(allMaps)
  125. if self.granularityMode == GranularityMode.ONE_UNIT:
  126. number, unit = gran.split()
  127. gran = "1 %s" % unit
  128. return gran
  129. if self.temporalType == TemporalType.RELATIVE:
  130. gran = tgis.compute_relative_time_granularity(allMaps)
  131. if self.granularityMode == GranularityMode.ONE_UNIT:
  132. gran = 1
  133. gran = "%(gran)s %(unit)s" % {'gran': gran,
  134. 'unit': self.timeseriesInfo[self.timeseriesList[0]]['unit']}
  135. return gran
  136. def GetLabelsAndMaps(self):
  137. """!Returns time labels and map names.
  138. """
  139. mapLists = []
  140. labelLists = []
  141. for dataset in self.timeseriesList:
  142. grassLabels, listOfMaps = self._getLabelsAndMaps(dataset)
  143. mapLists.append(listOfMaps)
  144. labelLists.append(grassLabels)
  145. # choose longest time interval and fill missing maps with None
  146. timestamps = max(labelLists, key = len)
  147. newMapLists = []
  148. for mapList, labelList in zip(mapLists, labelLists):
  149. newMapList = [None] * len(timestamps)
  150. i = 0
  151. # compare start time
  152. while timestamps[i][0] != labelList[0][0]: # compare
  153. i += 1
  154. newMapList[i:i + len(mapList)] = mapList
  155. newMapLists.append(newMapList)
  156. mapDict = {}
  157. for i, dataset in enumerate(self.timeseriesList):
  158. mapDict[dataset] = newMapLists[i]
  159. return timestamps, mapDict
  160. def _getLabelsAndMaps(self, timeseries):
  161. """!Returns time labels and map names (done by sampling)
  162. for both interval and point data.
  163. """
  164. sp = tgis.dataset_factory(self.timeseriesInfo[timeseries]['etype'], timeseries)
  165. if sp.is_in_db() == False:
  166. raise GException(_("Space time dataset <%s> not found.") % timeseries)
  167. sp.select()
  168. listOfMaps = []
  169. timeLabels = []
  170. gran = self.GetGranularity()
  171. unit = None
  172. if self.temporalType == TemporalType.ABSOLUTE and \
  173. self.granularityMode == GranularityMode.ONE_UNIT:
  174. gran, unit = gran.split()
  175. gran = '%(one)d %(unit)s' % {'one': 1, 'unit': unit}
  176. elif self.temporalType == TemporalType.RELATIVE:
  177. unit = self.timeseriesInfo[timeseries]['unit']
  178. if self.granularityMode == GranularityMode.ONE_UNIT:
  179. gran = 1
  180. # start sampling - now it can be used for both interval and point data
  181. # after instance, there can be a gap or an interval
  182. # if it is a gap we remove it and put there the previous instance instead
  183. # however the first gap must be removed to avoid duplication
  184. maps = sp.get_registered_maps_as_objects_by_granularity(gran = gran)
  185. if maps and len(maps) > 0:
  186. lastTimeseries = None
  187. followsPoint = False # indicates that we are just after finding a point
  188. afterPoint = False # indicates that we are after finding a point
  189. for mymap in maps:
  190. if isinstance(mymap, list):
  191. if len(mymap) > 0:
  192. map = mymap[0]
  193. else:
  194. map = mymap
  195. series = map.get_id()
  196. start, end = map.get_valid_time()
  197. if end:
  198. end = str(end)
  199. if end is None:
  200. # point data
  201. listOfMaps.append(series)
  202. afterPoint = True
  203. followsPoint = True
  204. lastTimeseries = series
  205. else:
  206. # interval data
  207. if series:
  208. # map exists, stop point mode
  209. listOfMaps.append(series)
  210. afterPoint = False
  211. else:
  212. # check point mode
  213. if afterPoint:
  214. if followsPoint:
  215. # skip this one, already there
  216. followsPoint = False
  217. continue
  218. else:
  219. # append the last one (of point time)
  220. listOfMaps.append(lastTimeseries)
  221. end = None
  222. else:
  223. # append series which is None
  224. listOfMaps.append(series)
  225. timeLabels.append((str(start), end, unit))
  226. if self.temporalType == TemporalType.ABSOLUTE:
  227. timeLabels = self._pretifyTimeLabels(timeLabels)
  228. return timeLabels, listOfMaps
  229. def _pretifyTimeLabels(self, labels):
  230. """!Convert absolute time labels to grass time and
  231. leave only datum when time is 0.
  232. """
  233. grassLabels = []
  234. isTime = False
  235. for start, end, unit in labels:
  236. start = tgis.string_to_datetime(start)
  237. start = tgis.datetime_to_grass_datetime_string(start)
  238. if end is not None:
  239. end = tgis.string_to_datetime(end)
  240. end = tgis.datetime_to_grass_datetime_string(end)
  241. grassLabels.append((start, end, unit))
  242. if '00:00:00' not in start or (end is not None and '00:00:00' not in end):
  243. isTime = True
  244. if not isTime:
  245. for i, (start, end, unit) in enumerate(grassLabels):
  246. start = start.replace('00:00:00', '').strip()
  247. if end is not None:
  248. end = end.replace('00:00:00', '').strip()
  249. grassLabels[i] = (start, end, unit)
  250. return grassLabels
  251. def _gatherInformation(self, timeseries, etype, timeseriesList, infoDict):
  252. """!Get info about timeseries and check topology (raises GException)"""
  253. id = validateTimeseriesName(timeseries, etype)
  254. sp = tgis.dataset_factory(etype, id)
  255. # Insert content from db
  256. sp.select()
  257. # Get ordered map list
  258. maps = sp.get_registered_maps_as_objects()
  259. if not sp.check_temporal_topology(maps):
  260. raise GException(_("Topology of Space time dataset %s is invalid." % id))
  261. timeseriesList.append(id)
  262. infoDict[id] = {}
  263. infoDict[id]['etype'] = etype
  264. infoDict[id]['temporal_type'] = sp.get_temporal_type()
  265. if sp.is_time_relative():
  266. infoDict[id]['unit'] = sp.get_relative_time_unit()
  267. infoDict[id]['granularity'] = sp.get_granularity()
  268. infoDict[id]['map_time'] = sp.get_map_time()
  269. infoDict[id]['maps'] = maps
  270. def test():
  271. import gettext
  272. from pprint import pprint
  273. gettext.install('grasswxpy', os.path.join(os.getenv("GISBASE"), 'locale'), unicode = True)
  274. temp = TemporalManager()
  275. timeseries1 = 'precip_abs0'
  276. timeseries2 = 'precip_abs2'
  277. createAbsoluteInterval(timeseries1, timeseries2)
  278. temp.AddTimeSeries(timeseries1, 'strds')
  279. temp.AddTimeSeries(timeseries2, 'strds')
  280. try:
  281. warn = temp.EvaluateInputData()
  282. print warn
  283. except GException, e:
  284. print e
  285. return
  286. print '///////////////////////////'
  287. gran = temp.GetGranularity()
  288. print "granularity: " + str(gran)
  289. pprint (temp.GetLabelsAndMaps())
  290. def createAbsoluteInterval(name1, name2):
  291. grass.run_command('g.region', s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10, flags = 'p3', quiet = True)
  292. grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite = True)
  293. grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite = True)
  294. grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite = True)
  295. grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite = True)
  296. grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite = True)
  297. grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite = True)
  298. grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite = True)
  299. grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite = True)
  300. grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite = True)
  301. grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite = True)
  302. grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite = True)
  303. grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite = True)
  304. n1 = grass.read_command("g.tempfile", pid = 1, flags = 'd').strip()
  305. fd = open(n1, 'w')
  306. fd.write(
  307. "prec_1|2001-01-01|2001-02-01\n"
  308. "prec_2|2001-04-01|2001-05-01\n"
  309. "prec_3|2001-05-01|2001-09-01\n"
  310. "prec_4|2001-09-01|2002-01-01\n"
  311. "prec_5|2002-01-01|2002-05-01\n"
  312. "prec_6|2002-05-01|2002-07-01\n"
  313. )
  314. fd.close()
  315. n2 = grass.read_command("g.tempfile", pid = 2, flags = 'd').strip()
  316. fd = open(n2, 'w')
  317. fd.write(
  318. "temp_1|2000-10-01|2001-01-01\n"
  319. "temp_2|2001-04-01|2001-05-01\n"
  320. "temp_3|2001-05-01|2001-09-01\n"
  321. "temp_4|2001-09-01|2002-01-01\n"
  322. "temp_5|2002-01-01|2002-05-01\n"
  323. "temp_6|2002-05-01|2002-07-01\n"
  324. )
  325. fd.close()
  326. grass.run_command('t.unregister', type = 'rast',
  327. maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
  328. for name, fname in zip((name1, name2), (n1, n2)):
  329. grass.run_command('t.create', overwrite = True, type='strds',
  330. temporaltype='absolute', output=name,
  331. title="A test with input files", descr="A test with input files")
  332. grass.run_command('t.register', flags = 'i', input=name, file=fname, overwrite = True)
  333. def createRelativeInterval(name1, name2):
  334. grass.run_command('g.region', s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10, flags = 'p3', quiet = True)
  335. grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite = True)
  336. grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite = True)
  337. grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite = True)
  338. grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite = True)
  339. grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite = True)
  340. grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite = True)
  341. grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite = True)
  342. grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite = True)
  343. grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite = True)
  344. grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite = True)
  345. grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite = True)
  346. grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite = True)
  347. n1 = grass.read_command("g.tempfile", pid = 1, flags = 'd').strip()
  348. fd = open(n1, 'w')
  349. fd.write(
  350. "prec_1|1|4\n"
  351. "prec_2|6|7\n"
  352. "prec_3|7|10\n"
  353. "prec_4|10|11\n"
  354. "prec_5|11|14\n"
  355. "prec_6|14|17\n"
  356. )
  357. fd.close()
  358. n2 = grass.read_command("g.tempfile", pid = 2, flags = 'd').strip()
  359. fd = open(n2, 'w')
  360. fd.write(
  361. "temp_1|1|4\n"
  362. "temp_2|4|7\n"
  363. "temp_3|7|10\n"
  364. "temp_4|10|11\n"
  365. "temp_5|11|14\n"
  366. "temp_6|14|17\n"
  367. )
  368. fd.close()
  369. grass.run_command('t.unregister', type = 'rast',
  370. maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
  371. for name, fname in zip((name1, name2), (n1, n2)):
  372. grass.run_command('t.create', overwrite = True, type='strds',
  373. temporaltype='relative', output=name,
  374. title="A test with input files", descr="A test with input files")
  375. grass.run_command('t.register', flags = 'i', input = name, file = fname, unit = "years", overwrite = True)
  376. def createAbsolutePoint(name):
  377. grass.run_command('g.region', s=0, n=80, w=0, e=120, b=0, t=50, res=10, res3=10, flags = 'p3', quiet = True)
  378. grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite = True)
  379. grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite = True)
  380. grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite = True)
  381. grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite = True)
  382. grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite = True)
  383. grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite = True)
  384. n1 = grass.read_command("g.tempfile", pid = 1, flags = 'd').strip()
  385. fd = open(n1, 'w')
  386. fd.write(
  387. "prec_1|2001-01-01\n"
  388. "prec_2|2001-03-01\n"
  389. "prec_3|2001-04-01\n"
  390. "prec_4|2001-05-01\n"
  391. "prec_5|2001-08-01\n"
  392. "prec_6|2001-09-01\n"
  393. )
  394. fd.close()
  395. grass.run_command('t.create', overwrite = True, type='strds',
  396. temporaltype='absolute', output=name,
  397. title="A test with input files", descr="A test with input files")
  398. grass.run_command('t.register', flags = 'i', input=name, file=n1, overwrite = True)
  399. if __name__ == '__main__':
  400. test()