123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- """
- @package animation.temporal_manager
- @brief Management of temporal datasets used in animation
- Classes:
- - temporal_manager::DataMode
- - temporal_manager::GranularityMode
- - temporal_manager::TemporalManager
- (C) 2012 by the GRASS Development Team
- This program is free software under the GNU General Public License
- (>=v2). Read the file COPYING that comes with GRASS for details.
- @author Anna Kratochvilova <kratochanna gmail.com>
- """
- from __future__ import print_function
- import datetime
- import grass.script as grass
- import grass.temporal as tgis
- from core.gcmd import GException
- from core.settings import UserSettings
- from animation.utils import validateTimeseriesName, TemporalType
- class DataMode:
- SIMPLE = 1
- MULTIPLE = 2
- class GranularityMode:
- ONE_UNIT = 1
- ORIGINAL = 2
- class TemporalManager(object):
- """Class for temporal data processing."""
- def __init__(self):
- self.timeseriesList = []
- self.timeseriesInfo = {}
- self.dataMode = None
- self.temporalType = None
- self.granularityMode = GranularityMode.ORIGINAL
- def GetTemporalType(self):
- """Get temporal type (TemporalType.ABSOLUTE,
- TemporalType.RELATIVE)
- """
- return self._temporalType
- def SetTemporalType(self, ttype):
- self._temporalType = ttype
- temporalType = property(fget=GetTemporalType, fset=SetTemporalType)
- def AddTimeSeries(self, timeseries, etype):
- """Add space time dataset
- and collect basic information about it.
- Raises GException (e.g. with invalid topology).
- :param timeseries: name of timeseries (with or without mapset)
- :param etype: element type (strds, stvds)
- """
- self._gatherInformation(
- timeseries, etype, self.timeseriesList, self.timeseriesInfo
- )
- def EvaluateInputData(self):
- """Checks if all timeseries are compatible (raises GException).
- Sets internal variables.
- """
- timeseriesCount = len(self.timeseriesList)
- if timeseriesCount == 1:
- self.dataMode = DataMode.SIMPLE
- elif timeseriesCount > 1:
- self.dataMode = DataMode.MULTIPLE
- else:
- self.dataMode = None
- ret, message = self._setTemporalState()
- if not ret:
- raise GException(message)
- if message: # warning
- return message
- return None
- def _setTemporalState(self):
- # check for absolute x relative
- absolute, relative = 0, 0
- for infoDict in self.timeseriesInfo.values():
- if infoDict["temporal_type"] == "absolute":
- absolute += 1
- else:
- relative += 1
- if bool(absolute) == bool(relative):
- message = _(
- "It is not allowed to display data with different "
- "temporal types (absolute and relative)."
- )
- return False, message
- if absolute:
- self.temporalType = TemporalType.ABSOLUTE
- else:
- self.temporalType = TemporalType.RELATIVE
- # check for units for relative type
- if relative:
- units = set()
- for infoDict in self.timeseriesInfo.values():
- units.add(infoDict["unit"])
- if len(units) > 1:
- message = _(
- "It is not allowed to display data with different units (%s)."
- ) % ",".join(units)
- return False, message
- # check for interval x point
- interval, point = 0, 0
- for infoDict in self.timeseriesInfo.values():
- if infoDict["map_time"] == "interval":
- interval += 1
- else:
- point += 1
- if bool(interval) == bool(point):
- message = _(
- "You are going to display data with different "
- "temporal types of maps (interval and point)."
- " It is recommended to use data of one temporal type to avoid confusion."
- )
- return True, message # warning
- return True, None
- def GetGranularity(self):
- """Returns temporal granularity of currently loaded timeseries."""
- if self.dataMode == DataMode.SIMPLE:
- gran = self.timeseriesInfo[self.timeseriesList[0]]["granularity"]
- if "unit" in self.timeseriesInfo[self.timeseriesList[0]]: # relative:
- granNum = gran
- unit = self.timeseriesInfo[self.timeseriesList[0]]["unit"]
- if self.granularityMode == GranularityMode.ONE_UNIT:
- granNum = 1
- else: # absolute
- granNum, unit = gran.split()
- if self.granularityMode == GranularityMode.ONE_UNIT:
- granNum = 1
- return (int(granNum), unit)
- if self.dataMode == DataMode.MULTIPLE:
- return self._getCommonGranularity()
- def _getCommonGranularity(self):
- allMaps = []
- for dataset in self.timeseriesList:
- maps = self.timeseriesInfo[dataset]["maps"]
- allMaps.extend(maps)
- if self.temporalType == TemporalType.ABSOLUTE:
- gran = tgis.compute_absolute_time_granularity(allMaps)
- granNum, unit = gran.split()
- if self.granularityMode == GranularityMode.ONE_UNIT:
- granNum = 1
- return int(granNum), unit
- if self.temporalType == TemporalType.RELATIVE:
- unit = self.timeseriesInfo[self.timeseriesList[0]]["unit"]
- granNum = tgis.compute_relative_time_granularity(allMaps)
- if self.granularityMode == GranularityMode.ONE_UNIT:
- granNum = 1
- return (granNum, unit)
- def GetLabelsAndMaps(self):
- """Returns time labels and map names."""
- mapLists = []
- labelLists = []
- labelListSet = set()
- for dataset in self.timeseriesList:
- grassLabels, listOfMaps = self._getLabelsAndMaps(dataset)
- mapLists.append(listOfMaps)
- labelLists.append(tuple(grassLabels))
- labelListSet.update(grassLabels)
- # combine all timeLabels and fill missing maps with None
- # BUT this does not work properly if the datasets have
- # no temporal overlap! We would need to sample all datasets
- # by a temporary dataset, I don't know how it would work with point
- # data
- if self.temporalType == TemporalType.ABSOLUTE:
- timestamps = sorted(list(labelListSet), key=lambda x: x[0])
- else:
- timestamps = sorted(list(labelListSet), key=lambda x: x[0])
- newMapLists = []
- for mapList, labelList in zip(mapLists, labelLists):
- newMapList = [None] * len(timestamps)
- i = 0
- # compare start time
- while timestamps[i][0] != labelList[0][0]: # compare
- i += 1
- newMapList[i : i + len(mapList)] = mapList
- newMapLists.append(newMapList)
- mapDict = {}
- for i, dataset in enumerate(self.timeseriesList):
- mapDict[dataset] = newMapLists[i]
- if self.temporalType == TemporalType.ABSOLUTE:
- # ('1996-01-01 00:00:00', '1997-01-01 00:00:00', 'year'),
- formatString = UserSettings.Get(
- group="animation", key="temporal", subkey="format"
- )
- timestamps = [
- (
- datetime.datetime.strftime(st, formatString),
- datetime.datetime.strftime(end, formatString)
- if end is not None
- else None,
- unit,
- )
- for (st, end, unit) in timestamps
- ]
- else:
- # ('15', '16', u'years'),
- timestamps = [
- (str(st), end if end is None else str(end), unit)
- for st, end, unit in timestamps
- ]
- return timestamps, mapDict
- def _getLabelsAndMaps(self, timeseries):
- """Returns time labels and map names (done by sampling)
- for both interval and point data.
- """
- sp = tgis.dataset_factory(self.timeseriesInfo[timeseries]["etype"], timeseries)
- if sp.is_in_db() is False:
- raise GException(_("Space time dataset <%s> not found.") % timeseries)
- sp.select()
- listOfMaps = []
- timeLabels = []
- granNum, unit = self.GetGranularity()
- if self.temporalType == TemporalType.ABSOLUTE:
- if self.granularityMode == GranularityMode.ONE_UNIT:
- gran = "%(one)d %(unit)s" % {"one": 1, "unit": unit}
- else:
- gran = "%(num)d %(unit)s" % {"num": granNum, "unit": unit}
- elif self.temporalType == TemporalType.RELATIVE:
- unit = self.timeseriesInfo[timeseries]["unit"]
- if self.granularityMode == GranularityMode.ONE_UNIT:
- gran = 1
- else:
- gran = granNum
- # start sampling - now it can be used for both interval and point data
- # after instance, there can be a gap or an interval
- # if it is a gap we remove it and put there the previous instance instead
- # however the first gap must be removed to avoid duplication
- maps = sp.get_registered_maps_as_objects_by_granularity(gran=gran)
- if maps and len(maps) > 0:
- lastTimeseries = None
- followsPoint = False # indicates that we are just after finding a point
- afterPoint = False # indicates that we are after finding a point
- for mymap in maps:
- if isinstance(mymap, list):
- if len(mymap) > 0:
- map = mymap[0]
- else:
- map = mymap
- series = map.get_id()
- start, end = map.get_temporal_extent_as_tuple()
- if self.timeseriesInfo[timeseries]["map_time"] == "point":
- # point data
- listOfMaps.append(series)
- afterPoint = True
- followsPoint = True
- lastTimeseries = series
- end = None
- else:
- end = end
- # interval data
- if series:
- # map exists, stop point mode
- listOfMaps.append(series)
- afterPoint = False
- else:
- # check point mode
- if afterPoint:
- if followsPoint:
- # skip this one, already there
- followsPoint = False
- continue
- else:
- # append the last one (of point time)
- listOfMaps.append(lastTimeseries)
- end = None
- else:
- # append series which is None
- listOfMaps.append(series)
- timeLabels.append((start, end, unit))
- return timeLabels, listOfMaps
- def _pretifyTimeLabels(self, labels):
- """Convert absolute time labels to grass time and
- leave only datum when time is 0.
- """
- grassLabels = []
- isTime = False
- for start, end, unit in labels:
- start = tgis.string_to_datetime(start)
- start = tgis.datetime_to_grass_datetime_string(start)
- if end is not None:
- end = tgis.string_to_datetime(end)
- end = tgis.datetime_to_grass_datetime_string(end)
- grassLabels.append((start, end, unit))
- if "00:00:00" not in start or (end is not None and "00:00:00" not in end):
- isTime = True
- if not isTime:
- for i, (start, end, unit) in enumerate(grassLabels):
- start = start.replace("00:00:00", "").strip()
- if end is not None:
- end = end.replace("00:00:00", "").strip()
- grassLabels[i] = (start, end, unit)
- return grassLabels
- def _gatherInformation(self, timeseries, etype, timeseriesList, infoDict):
- """Get info about timeseries and check topology (raises GException)"""
- id = validateTimeseriesName(timeseries, etype)
- sp = tgis.dataset_factory(etype, id)
- # Insert content from db
- sp.select()
- # Get ordered map list
- maps = sp.get_registered_maps_as_objects()
- if not sp.check_temporal_topology(maps):
- raise GException(_("Topology of Space time dataset %s is invalid." % id))
- timeseriesList.append(id)
- infoDict[id] = {}
- infoDict[id]["etype"] = etype
- infoDict[id]["temporal_type"] = sp.get_temporal_type()
- if sp.is_time_relative():
- infoDict[id]["unit"] = sp.get_relative_time_unit()
- infoDict[id]["granularity"] = sp.get_granularity()
- infoDict[id]["map_time"] = sp.get_map_time()
- infoDict[id]["maps"] = maps
- def test():
- from pprint import pprint
- # Make sure the temporal database exists
- tgis.init()
- temp = TemporalManager()
- # timeseries = createAbsolutePoint()
- # timeseries = createRelativePoint()
- # timeseries1, timeseries2 = createAbsoluteInterval()
- timeseries1, timeseries2 = createRelativeInterval()
- temp.AddTimeSeries(timeseries1, "strds")
- temp.AddTimeSeries(timeseries2, "strds")
- try:
- warn = temp.EvaluateInputData()
- print(warn)
- except GException as e:
- print(e)
- return
- print("///////////////////////////")
- gran = temp.GetGranularity()
- print("granularity: " + str(gran))
- pprint(temp.GetLabelsAndMaps())
- def createAbsoluteInterval():
- 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,
- )
- grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
- grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
- grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
- grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
- grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
- grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
- grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite=True)
- grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite=True)
- grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite=True)
- grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite=True)
- grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite=True)
- grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True)
- n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
- fd = open(n1, "w")
- fd.write(
- "prec_1|2001-01-01|2001-02-01\n"
- "prec_2|2001-04-01|2001-05-01\n"
- "prec_3|2001-05-01|2001-09-01\n"
- "prec_4|2001-09-01|2002-01-01\n"
- "prec_5|2002-01-01|2002-05-01\n"
- "prec_6|2002-05-01|2002-07-01\n"
- )
- fd.close()
- n2 = grass.read_command("g.tempfile", pid=2, flags="d").strip()
- fd = open(n2, "w")
- fd.write(
- "temp_1|2000-10-01|2001-01-01\n"
- "temp_2|2001-04-01|2001-05-01\n"
- "temp_3|2001-05-01|2001-09-01\n"
- "temp_4|2001-09-01|2002-01-01\n"
- "temp_5|2002-01-01|2002-05-01\n"
- "temp_6|2002-05-01|2002-07-01\n"
- )
- fd.close()
- name1 = "absinterval1"
- name2 = "absinterval2"
- grass.run_command(
- "t.unregister",
- type="raster",
- 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",
- )
- for name, fname in zip((name1, name2), (n1, n2)):
- grass.run_command(
- "t.create",
- overwrite=True,
- type="strds",
- temporaltype="absolute",
- output=name,
- title="A test with input files",
- descr="A test with input files",
- )
- grass.run_command(
- "t.register", flags="i", input=name, file=fname, overwrite=True
- )
- return name1, name2
- def createRelativeInterval():
- 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,
- )
- grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
- grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
- grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
- grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
- grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
- grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
- grass.mapcalc(exp="temp_1 = rand(0, 550)", overwrite=True)
- grass.mapcalc(exp="temp_2 = rand(0, 450)", overwrite=True)
- grass.mapcalc(exp="temp_3 = rand(0, 320)", overwrite=True)
- grass.mapcalc(exp="temp_4 = rand(0, 510)", overwrite=True)
- grass.mapcalc(exp="temp_5 = rand(0, 300)", overwrite=True)
- grass.mapcalc(exp="temp_6 = rand(0, 650)", overwrite=True)
- n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
- fd = open(n1, "w")
- fd.write(
- "prec_1|1|4\n"
- "prec_2|6|7\n"
- "prec_3|7|10\n"
- "prec_4|10|11\n"
- "prec_5|11|14\n"
- "prec_6|14|17\n"
- )
- fd.close()
- n2 = grass.read_command("g.tempfile", pid=2, flags="d").strip()
- fd = open(n2, "w")
- fd.write(
- "temp_1|5|6\n"
- "temp_2|6|7\n"
- "temp_3|7|10\n"
- "temp_4|10|11\n"
- "temp_5|11|18\n"
- "temp_6|19|22\n"
- )
- fd.close()
- name1 = "relinterval1"
- name2 = "relinterval2"
- grass.run_command(
- "t.unregister",
- type="raster",
- 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",
- )
- for name, fname in zip((name1, name2), (n1, n2)):
- grass.run_command(
- "t.create",
- overwrite=True,
- type="strds",
- temporaltype="relative",
- output=name,
- title="A test with input files",
- descr="A test with input files",
- )
- grass.run_command(
- "t.register",
- flags="i",
- input=name,
- file=fname,
- unit="years",
- overwrite=True,
- )
- return name1, name2
- def createAbsolutePoint():
- 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,
- )
- grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
- grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
- grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
- grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
- grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
- grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
- n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
- fd = open(n1, "w")
- fd.write(
- "prec_1|2001-01-01\n"
- "prec_2|2001-03-01\n"
- "prec_3|2001-04-01\n"
- "prec_4|2001-05-01\n"
- "prec_5|2001-08-01\n"
- "prec_6|2001-09-01\n"
- )
- fd.close()
- name = "abspoint"
- grass.run_command(
- "t.create",
- overwrite=True,
- type="strds",
- temporaltype="absolute",
- output=name,
- title="A test with input files",
- descr="A test with input files",
- )
- grass.run_command("t.register", flags="i", input=name, file=n1, overwrite=True)
- return name
- def createRelativePoint():
- 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,
- )
- grass.mapcalc(exp="prec_1 = rand(0, 550)", overwrite=True)
- grass.mapcalc(exp="prec_2 = rand(0, 450)", overwrite=True)
- grass.mapcalc(exp="prec_3 = rand(0, 320)", overwrite=True)
- grass.mapcalc(exp="prec_4 = rand(0, 510)", overwrite=True)
- grass.mapcalc(exp="prec_5 = rand(0, 300)", overwrite=True)
- grass.mapcalc(exp="prec_6 = rand(0, 650)", overwrite=True)
- n1 = grass.read_command("g.tempfile", pid=1, flags="d").strip()
- fd = open(n1, "w")
- fd.write(
- "prec_1|1\n" "prec_2|3\n" "prec_3|5\n" "prec_4|7\n" "prec_5|11\n" "prec_6|13\n"
- )
- fd.close()
- name = "relpoint"
- grass.run_command(
- "t.create",
- overwrite=True,
- type="strds",
- temporaltype="relative",
- output=name,
- title="A test with input files",
- descr="A test with input files",
- )
- grass.run_command("t.register", unit="day", input=name, file=n1, overwrite=True)
- return name
- if __name__ == "__main__":
- test()
|