source: grass/trunk/gui/wxpython/animation/temporal_manager.py

Last change on this file was 74307, checked in by neteler, 5 years ago

i18N: cleanup gettext usage for Python code (fixes #3790) (contributed by pmav99)

  • Property svn:eol-style set to native
  • Property svn:mime-type set to text/x-python
File size: 20.8 KB
Line 
1"""
2@package animation.temporal_manager
3
4@brief Management of temporal datasets used in animation
5
6Classes:
7 - temporal_manager::DataMode
8 - temporal_manager::GranularityMode
9 - temporal_manager::TemporalManager
10
11
12(C) 2012 by the GRASS Development Team
13
14This program is free software under the GNU General Public License
15(>=v2). Read the file COPYING that comes with GRASS for details.
16
17@author Anna Kratochvilova <kratochanna gmail.com>
18"""
19
20from __future__ import print_function
21
22import os
23import datetime
24
25import grass.script as grass
26import grass.temporal as tgis
27from core.gcmd import GException
28from core.settings import UserSettings
29from animation.utils import validateTimeseriesName, TemporalType
30
31
32class DataMode:
33 SIMPLE = 1
34 MULTIPLE = 2
35
36
37class GranularityMode:
38 ONE_UNIT = 1
39 ORIGINAL = 2
40
41
42class TemporalManager(object):
43 """Class for temporal data processing."""
44
45 def __init__(self):
46 self.timeseriesList = []
47 self.timeseriesInfo = {}
48
49 self.dataMode = None
50 self.temporalType = None
51
52 self.granularityMode = GranularityMode.ORIGINAL
53
54 def GetTemporalType(self):
55 """Get temporal type (TemporalType.ABSOLUTE,
56 TemporalType.RELATIVE)
57 """
58 return self._temporalType
59
60 def SetTemporalType(self, ttype):
61 self._temporalType = ttype
62
63 temporalType = property(fget=GetTemporalType, fset=SetTemporalType)
64
65 def AddTimeSeries(self, timeseries, etype):
66 """Add space time dataset
67 and collect basic information about it.
68
69 Raises GException (e.g. with invalid topology).
70
71 :param timeseries: name of timeseries (with or without mapset)
72 :param etype: element type (strds, stvds)
73 """
74 self._gatherInformation(
75 timeseries,
76 etype,
77 self.timeseriesList,
78 self.timeseriesInfo)
79
80 def EvaluateInputData(self):
81 """Checks if all timeseries are compatible (raises GException).
82
83 Sets internal variables.
84 """
85 timeseriesCount = len(self.timeseriesList)
86
87 if timeseriesCount == 1:
88 self.dataMode = DataMode.SIMPLE
89 elif timeseriesCount > 1:
90 self.dataMode = DataMode.MULTIPLE
91 else:
92 self.dataMode = None
93
94 ret, message = self._setTemporalState()
95 if not ret:
96 raise GException(message)
97 if message: # warning
98 return message
99
100 return None
101
102 def _setTemporalState(self):
103 # check for absolute x relative
104 absolute, relative = 0, 0
105 for infoDict in self.timeseriesInfo.values():
106 if infoDict['temporal_type'] == 'absolute':
107 absolute += 1
108 else:
109 relative += 1
110 if bool(absolute) == bool(relative):
111 message = _("It is not allowed to display data with different "
112 "temporal types (absolute and relative).")
113 return False, message
114 if absolute:
115 self.temporalType = TemporalType.ABSOLUTE
116 else:
117 self.temporalType = TemporalType.RELATIVE
118
119 # check for units for relative type
120 if relative:
121 units = set()
122 for infoDict in self.timeseriesInfo.values():
123 units.add(infoDict['unit'])
124 if len(units) > 1:
125 message = _(
126 "It is not allowed to display data with different units (%s).") % ','.join(units)
127 return False, message
128
129 # check for interval x point
130 interval, point = 0, 0
131 for infoDict in self.timeseriesInfo.values():
132 if infoDict['map_time'] == 'interval':
133 interval += 1
134 else:
135 point += 1
136 if bool(interval) == bool(point):
137 message = _(
138 "You are going to display data with different "
139 "temporal types of maps (interval and point)."
140 " It is recommended to use data of one temporal type to avoid confusion.")
141 return True, message # warning
142
143 return True, None
144
145 def GetGranularity(self):
146 """Returns temporal granularity of currently loaded timeseries.
147 """
148 if self.dataMode == DataMode.SIMPLE:
149 gran = self.timeseriesInfo[self.timeseriesList[0]]['granularity']
150 if 'unit' in self.timeseriesInfo[
151 self.timeseriesList[0]]: # relative:
152 granNum = gran
153 unit = self.timeseriesInfo[self.timeseriesList[0]]['unit']
154 if self.granularityMode == GranularityMode.ONE_UNIT:
155 granNum = 1
156 else: # absolute
157 granNum, unit = gran.split()
158 if self.granularityMode == GranularityMode.ONE_UNIT:
159 granNum = 1
160
161 return (int(granNum), unit)
162
163 if self.dataMode == DataMode.MULTIPLE:
164 return self._getCommonGranularity()
165
166 def _getCommonGranularity(self):
167 allMaps = []
168 for dataset in self.timeseriesList:
169 maps = self.timeseriesInfo[dataset]['maps']
170 allMaps.extend(maps)
171
172 if self.temporalType == TemporalType.ABSOLUTE:
173 gran = tgis.compute_absolute_time_granularity(allMaps)
174 granNum, unit = gran.split()
175 if self.granularityMode == GranularityMode.ONE_UNIT:
176 granNum = 1
177 return int(granNum), unit
178 if self.temporalType == TemporalType.RELATIVE:
179 unit = self.timeseriesInfo[self.timeseriesList[0]]['unit']
180 granNum = tgis.compute_relative_time_granularity(allMaps)
181 if self.granularityMode == GranularityMode.ONE_UNIT:
182 granNum = 1
183 return (granNum, unit)
184
185 def GetLabelsAndMaps(self):
186 """Returns time labels and map names.
187 """
188 mapLists = []
189 labelLists = []
190 labelListSet = set()
191 for dataset in self.timeseriesList:
192 grassLabels, listOfMaps = self._getLabelsAndMaps(dataset)
193 mapLists.append(listOfMaps)
194 labelLists.append(tuple(grassLabels))
195 labelListSet.update(grassLabels)
196 # combine all timeLabels and fill missing maps with None
197 # BUT this does not work properly if the datasets have
198 # no temporal overlap! We would need to sample all datasets
199 # by a temporary dataset, I don't know how it would work with point
200 # data
201 if self.temporalType == TemporalType.ABSOLUTE:
202 timestamps = sorted(list(labelListSet), key=lambda x: x[0])
203 else:
204 timestamps = sorted(list(labelListSet), key=lambda x: x[0])
205
206 newMapLists = []
207 for mapList, labelList in zip(mapLists, labelLists):
208 newMapList = [None] * len(timestamps)
209 i = 0
210 # compare start time
211 while timestamps[i][0] != labelList[0][0]: # compare
212 i += 1
213 newMapList[i:i + len(mapList)] = mapList
214 newMapLists.append(newMapList)
215
216 mapDict = {}
217 for i, dataset in enumerate(self.timeseriesList):
218 mapDict[dataset] = newMapLists[i]
219
220 if self.temporalType == TemporalType.ABSOLUTE:
221 # ('1996-01-01 00:00:00', '1997-01-01 00:00:00', 'year'),
222 formatString = UserSettings.Get(
223 group='animation', key='temporal', subkey='format')
224 timestamps = [
225 (datetime.datetime.strftime(
226 st, formatString), datetime.datetime.strftime(
227 end, formatString) if end is not None else None, unit) for (
228 st, end, unit) in timestamps]
229 else:
230 # ('15', '16', u'years'),
231 timestamps = [(str(st), end if end is None else str(end), unit)
232 for st, end, unit in timestamps]
233 return timestamps, mapDict
234
235 def _getLabelsAndMaps(self, timeseries):
236 """Returns time labels and map names (done by sampling)
237 for both interval and point data.
238 """
239 sp = tgis.dataset_factory(
240 self.timeseriesInfo[timeseries]['etype'], timeseries)
241 if sp.is_in_db() is False:
242 raise GException(
243 _("Space time dataset <%s> not found.") %
244 timeseries)
245 sp.select()
246
247 listOfMaps = []
248 timeLabels = []
249 granNum, unit = self.GetGranularity()
250 if self.temporalType == TemporalType.ABSOLUTE:
251 if self.granularityMode == GranularityMode.ONE_UNIT:
252 gran = '%(one)d %(unit)s' % {'one': 1, 'unit': unit}
253 else:
254 gran = '%(num)d %(unit)s' % {'num': granNum, 'unit': unit}
255
256 elif self.temporalType == TemporalType.RELATIVE:
257 unit = self.timeseriesInfo[timeseries]['unit']
258 if self.granularityMode == GranularityMode.ONE_UNIT:
259 gran = 1
260 else:
261 gran = granNum
262 # start sampling - now it can be used for both interval and point data
263 # after instance, there can be a gap or an interval
264 # if it is a gap we remove it and put there the previous instance instead
265 # however the first gap must be removed to avoid duplication
266 maps = sp.get_registered_maps_as_objects_by_granularity(gran=gran)
267 if maps and len(maps) > 0:
268 lastTimeseries = None
269 followsPoint = False # indicates that we are just after finding a point
270 afterPoint = False # indicates that we are after finding a point
271 for mymap in maps:
272 if isinstance(mymap, list):
273 if len(mymap) > 0:
274 map = mymap[0]
275 else:
276 map = mymap
277
278 series = map.get_id()
279
280 start, end = map.get_temporal_extent_as_tuple()
281 if self.timeseriesInfo[timeseries]['map_time'] == 'point':
282 # point data
283 listOfMaps.append(series)
284 afterPoint = True
285 followsPoint = True
286 lastTimeseries = series
287 end = None
288 else:
289 end = end
290 # interval data
291 if series:
292 # map exists, stop point mode
293 listOfMaps.append(series)
294 afterPoint = False
295 else:
296 # check point mode
297 if afterPoint:
298 if followsPoint:
299 # skip this one, already there
300 followsPoint = False
301 continue
302 else:
303 # append the last one (of point time)
304 listOfMaps.append(lastTimeseries)
305 end = None
306 else:
307 # append series which is None
308 listOfMaps.append(series)
309 timeLabels.append((start, end, unit))
310
311 return timeLabels, listOfMaps
312
313 def _pretifyTimeLabels(self, labels):
314 """Convert absolute time labels to grass time and
315 leave only datum when time is 0.
316 """
317 grassLabels = []
318 isTime = False
319 for start, end, unit in labels:
320 start = tgis.string_to_datetime(start)
321 start = tgis.datetime_to_grass_datetime_string(start)
322 if end is not None:
323 end = tgis.string_to_datetime(end)
324 end = tgis.datetime_to_grass_datetime_string(end)
325 grassLabels.append((start, end, unit))
326 if '00:00:00' not in start or (
327 end is not None and '00:00:00' not in end):
328 isTime = True
329 if not isTime:
330 for i, (start, end, unit) in enumerate(grassLabels):
331 start = start.replace('00:00:00', '').strip()
332 if end is not None:
333 end = end.replace('00:00:00', '').strip()
334 grassLabels[i] = (start, end, unit)
335 return grassLabels
336
337 def _gatherInformation(self, timeseries, etype, timeseriesList, infoDict):
338 """Get info about timeseries and check topology (raises GException)"""
339 id = validateTimeseriesName(timeseries, etype)
340 sp = tgis.dataset_factory(etype, id)
341 # Insert content from db
342 sp.select()
343 # Get ordered map list
344 maps = sp.get_registered_maps_as_objects()
345
346 if not sp.check_temporal_topology(maps):
347 raise GException(
348 _("Topology of Space time dataset %s is invalid." % id))
349
350 timeseriesList.append(id)
351 infoDict[id] = {}
352 infoDict[id]['etype'] = etype
353 infoDict[id]['temporal_type'] = sp.get_temporal_type()
354 if sp.is_time_relative():
355 infoDict[id]['unit'] = sp.get_relative_time_unit()
356 infoDict[id]['granularity'] = sp.get_granularity()
357 infoDict[id]['map_time'] = sp.get_map_time()
358 infoDict[id]['maps'] = maps
359
360
361def test():
362 from pprint import pprint
363 # Make sure the temporal database exists
364 tgis.init()
365
366 temp = TemporalManager()
367# timeseries = createAbsolutePoint()
368# timeseries = createRelativePoint()
369# timeseries1, timeseries2 = createAbsoluteInterval()
370 timeseries1, timeseries2 = createRelativeInterval()
371
372 temp.AddTimeSeries(timeseries1, 'strds')
373 temp.AddTimeSeries(timeseries2, 'strds')
374
375 try:
376 warn = temp.EvaluateInputData()
377 print(warn)
378 except GException as e:
379 print(e)
380 return
381
382 print('///////////////////////////')
383 gran = temp.GetGranularity()
384 print("granularity: " + str(gran))
385 pprint(temp.GetLabelsAndMaps())
386
387
388def createAbsoluteInterval():
389 grass.run_command(
390 'g.region',
391 s=0,
392 n=80,
393 w=0,
394 e=120,
395 b=0,
396 t=50,
397 res=10,
398 res3=10,
399 flags='p3',
400 quiet=True)
401
402 grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
403 grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
404 grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
405 grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
406 grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
407 grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
408
409 grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite=True)
410 grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite=True)
411 grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite=True)
412 grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite=True)
413 grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite=True)
414 grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True)
415
416 n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
417 fd = open(n1, 'w')
418 fd.write(
419 "prec_1|2001-01-01|2001-02-01\n"
420 "prec_2|2001-04-01|2001-05-01\n"
421 "prec_3|2001-05-01|2001-09-01\n"
422 "prec_4|2001-09-01|2002-01-01\n"
423 "prec_5|2002-01-01|2002-05-01\n"
424 "prec_6|2002-05-01|2002-07-01\n"
425 )
426 fd.close()
427
428 n2 = grass.read_command("g.tempfile", pid=2, flags='d').strip()
429 fd = open(n2, 'w')
430 fd.write(
431 "temp_1|2000-10-01|2001-01-01\n"
432 "temp_2|2001-04-01|2001-05-01\n"
433 "temp_3|2001-05-01|2001-09-01\n"
434 "temp_4|2001-09-01|2002-01-01\n"
435 "temp_5|2002-01-01|2002-05-01\n"
436 "temp_6|2002-05-01|2002-07-01\n"
437 )
438 fd.close()
439 name1 = 'absinterval1'
440 name2 = 'absinterval2'
441 grass.run_command('t.unregister', type='raster',
442 maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,'
443 'temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
444 for name, fname in zip((name1, name2), (n1, n2)):
445 grass.run_command(
446 't.create',
447 overwrite=True,
448 type='strds',
449 temporaltype='absolute',
450 output=name,
451 title="A test with input files",
452 descr="A test with input files")
453 grass.run_command(
454 't.register',
455 flags='i',
456 input=name,
457 file=fname,
458 overwrite=True)
459
460 return name1, name2
461
462
463def createRelativeInterval():
464 grass.run_command(
465 'g.region',
466 s=0,
467 n=80,
468 w=0,
469 e=120,
470 b=0,
471 t=50,
472 res=10,
473 res3=10,
474 flags='p3',
475 quiet=True)
476
477 grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
478 grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
479 grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
480 grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
481 grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
482 grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
483
484 grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite=True)
485 grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite=True)
486 grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite=True)
487 grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite=True)
488 grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite=True)
489 grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True)
490
491 n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
492 fd = open(n1, 'w')
493 fd.write(
494 "prec_1|1|4\n"
495 "prec_2|6|7\n"
496 "prec_3|7|10\n"
497 "prec_4|10|11\n"
498 "prec_5|11|14\n"
499 "prec_6|14|17\n"
500 )
501 fd.close()
502
503 n2 = grass.read_command("g.tempfile", pid=2, flags='d').strip()
504 fd = open(n2, 'w')
505 fd.write(
506 "temp_1|5|6\n"
507 "temp_2|6|7\n"
508 "temp_3|7|10\n"
509 "temp_4|10|11\n"
510 "temp_5|11|18\n"
511 "temp_6|19|22\n"
512 )
513 fd.close()
514 name1 = 'relinterval1'
515 name2 = 'relinterval2'
516 grass.run_command('t.unregister', type='raster',
517 maps='prec_1,prec_2,prec_3,prec_4,prec_5,prec_6,'
518 'temp_1,temp_2,temp_3,temp_4,temp_5,temp_6')
519 for name, fname in zip((name1, name2), (n1, n2)):
520 grass.run_command(
521 't.create',
522 overwrite=True,
523 type='strds',
524 temporaltype='relative',
525 output=name,
526 title="A test with input files",
527 descr="A test with input files")
528 grass.run_command(
529 't.register',
530 flags='i',
531 input=name,
532 file=fname,
533 unit="years",
534 overwrite=True)
535 return name1, name2
536
537
538def createAbsolutePoint():
539 grass.run_command(
540 'g.region',
541 s=0,
542 n=80,
543 w=0,
544 e=120,
545 b=0,
546 t=50,
547 res=10,
548 res3=10,
549 flags='p3',
550 quiet=True)
551
552 grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
553 grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
554 grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
555 grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
556 grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
557 grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
558
559 n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
560 fd = open(n1, 'w')
561 fd.write(
562 "prec_1|2001-01-01\n"
563 "prec_2|2001-03-01\n"
564 "prec_3|2001-04-01\n"
565 "prec_4|2001-05-01\n"
566 "prec_5|2001-08-01\n"
567 "prec_6|2001-09-01\n"
568 )
569 fd.close()
570 name = 'abspoint'
571 grass.run_command(
572 't.create',
573 overwrite=True,
574 type='strds',
575 temporaltype='absolute',
576 output=name,
577 title="A test with input files",
578 descr="A test with input files")
579
580 grass.run_command(
581 't.register',
582 flags='i',
583 input=name,
584 file=n1,
585 overwrite=True)
586 return name
587
588
589def createRelativePoint():
590 grass.run_command(
591 'g.region',
592 s=0,
593 n=80,
594 w=0,
595 e=120,
596 b=0,
597 t=50,
598 res=10,
599 res3=10,
600 flags='p3',
601 quiet=True)
602
603 grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
604 grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
605 grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
606 grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
607 grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
608 grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
609
610 n1 = grass.read_command("g.tempfile", pid=1, flags='d').strip()
611 fd = open(n1, 'w')
612 fd.write(
613 "prec_1|1\n"
614 "prec_2|3\n"
615 "prec_3|5\n"
616 "prec_4|7\n"
617 "prec_5|11\n"
618 "prec_6|13\n"
619 )
620 fd.close()
621 name = 'relpoint'
622 grass.run_command(
623 't.create',
624 overwrite=True,
625 type='strds',
626 temporaltype='relative',
627 output=name,
628 title="A test with input files",
629 descr="A test with input files")
630
631 grass.run_command(
632 't.register',
633 unit="day",
634 input=name,
635 file=n1,
636 overwrite=True)
637 return name
638
639if __name__ == '__main__':
640
641 test()
Note: See TracBrowser for help on using the repository browser.