1# -*- coding: utf-8 -*-
2"""
3Temporal topology dataset connector class
4
5Usage:
6
7.. code-block:: python:
8
9    >>> import grass.temporal as tgis
10    >>> tmr = tgis.TemporalTopologyDatasetConnector()
11
12(C) 2012-2013 by the GRASS Development Team
13This program is free software under the GNU General Public
14License (>=v2). Read the file COPYING that comes with GRASS
15for details.
16
17:authors: Soeren Gebbert
18"""
19from __future__ import print_function
20import copy
21
22
23class TemporalTopologyDatasetConnector(object):
24    """This class implements a temporal topology access structure to connect
25       temporal related datasets
26
27       This object will be set up by temporal topology creation method provided
28       by the SpatioTemporalTopologyBuilder.
29
30       If correctly initialize the calls next() and prev()
31       let the user walk temporally forward and backward in time.
32
33       The following temporal relations with access methods are supported:
34
35       - equal
36       - follows
37       - precedes
38       - overlaps
39       - overlapped
40       - during (including starts, finishes)
41       - contains (including started, finished)
42       - starts
43       - started
44       - finishes
45       - finished
46
47       .. code-block:: python:
48
49           # We have build the temporal topology and we know the first map
50           start = first
51           while start:
52
53               # Print all maps this map temporally contains
54               dlist = start.get_contains()
55               for map in dlist:
56                   map.print_info()
57
58               start = start.next()
59
60            >>> import grass.temporal as tgis
61            >>> tgis.init()
62            >>> map = tgis.RasterDataset("a@P")
63            >>> tmr = tgis.TemporalTopologyDatasetConnector()
64            >>> tmr.set_next(map)
65            >>> tmr.set_prev(map)
66            >>> tmr.append_equal(map)
67            >>> tmr.append_follows(map)
68            >>> tmr.append_precedes(map)
69            >>> tmr.append_overlapped(map)
70            >>> tmr.append_overlaps(map)
71            >>> tmr.append_during(map)
72            >>> tmr.append_contains(map)
73            >>> tmr.append_starts(map)
74            >>> tmr.append_started(map)
75            >>> tmr.append_finishes(map)
76            >>> tmr.append_finished(map)
77            >>> tmr.print_temporal_topology_info()
78             +-------------------- Temporal Topology -------------------------------------+
79             | Next: ...................... a@P
80             | Previous: .................. a@P
81             | Equal:...................... a@P
82             | Follows: ................... a@P
83             | Precedes: .................. a@P
84             | Overlaps: .................. a@P
85             | Overlapped: ................ a@P
86             | During: .................... a@P
87             | Contains: .................. a@P
88             | Starts:.. .................. a@P
89             | Started:. .................. a@P
90             | Finishes:................... a@P
91             | Finished:................... a@P
92            >>> tmr.print_temporal_topology_shell_info()
93            next=a@P
94            prev=a@P
95            equal=a@P
96            follows=a@P
97            precedes=a@P
98            overlaps=a@P
99            overlapped=a@P
100            during=a@P
101            contains=a@P
102            starts=a@P
103            started=a@P
104            finishes=a@P
105            finished=a@P
106            >>> rlist = tmr.get_temporal_relations()
107            >>> if "FINISHED" in rlist.keys():
108            ...    print(rlist["FINISHED"][0].get_id())
109            a@P
110
111    """
112
113    def __init__(self):
114        self.reset_temporal_topology()
115
116    def reset_temporal_topology(self):
117        """Reset any information about temporal topology"""
118        self._temporal_topology = {}
119        self._has_temporal_topology = False
120
121    def get_temporal_relations(self):
122        """Return the dictionary of temporal relationships
123
124            Keys are the temporal relationships in upper case,
125            values are abstract map objects.
126
127            :return: The temporal relations dictionary
128        """
129        return copy.copy(self._temporal_topology)
130
131    def get_number_of_temporal_relations(self):
132        """ Return a dictionary in which the keys are the relation names and
133        the value are the number of relations.
134
135        The following relations are available:
136
137        - equal
138        - follows
139        - precedes
140        - overlaps
141        - overlapped
142        - during (including starts, finishes)
143        - contains (including started, finished)
144        - starts
145        - started
146        - finishes
147        - finished
148
149        To access topological information the temporal topology must be build
150        first using the SpatioTemporalTopologyBuilder.
151
152        :return: the dictionary with relations as keys and number as values
153                 or None in case the topology wasn't build
154        """
155        if self._has_temporal_topology is False:
156            return None
157
158        relations = {}
159        try:
160            relations["equal"] = len(self._temporal_topology["EQUAL"])
161        except:
162            relations["equal"] = 0
163        try:
164            relations["follows"] = len(self._temporal_topology["FOLLOWS"])
165        except:
166            relations["follows"] = 0
167        try:
168            relations["precedes"] = len(self._temporal_topology["PRECEDES"])
169        except:
170            relations["precedes"] = 0
171        try:
172            relations["overlaps"] = len(self._temporal_topology["OVERLAPS"])
173        except:
174            relations["overlaps"] = 0
175        try:
176            relations["overlapped"] = len(self._temporal_topology["OVERLAPPED"])
177        except:
178            relations["overlapped"] = 0
179        try:
180            relations["during"] = len(self._temporal_topology["DURING"])
181        except:
182            relations["during"] = 0
183        try:
184            relations["contains"] = len(self._temporal_topology["CONTAINS"])
185        except:
186            relations["contains"] = 0
187        try:
188            relations["starts"] = len(self._temporal_topology["STARTS"])
189        except:
190            relations["starts"] = 0
191        try:
192            relations["started"] = len(self._temporal_topology["STARTED"])
193        except:
194            relations["started"] = 0
195        try:
196            relations["finishes"] = len(self._temporal_topology["FINISHES"])
197        except:
198            relations["finishes"] = 0
199        try:
200            relations["finished"] = len(self._temporal_topology["FINISHED"])
201        except:
202            relations["finished"] = 0
203
204        return relations
205
206    def set_temporal_topology_build_true(self):
207        """Same as name"""
208        self._has_temporal_topology = True
209
210    def set_temporal_topology_build_false(self):
211        """Same as name"""
212        self._has_temporal_topology = False
213
214    def is_temporal_topology_build(self):
215        """Check if the temporal topology was build"""
216        return self._has_temporal_topology
217
218    def set_next(self, map):
219        """Set the map that is temporally as closest located after this map.
220
221           Temporally located means that the start time of the "next" map is
222           temporally located AFTER the start time of this map, but temporally
223           near than other maps of the same dataset.
224
225           :param map: This object should be of type AbstractMapDataset
226                       or derived classes
227        """
228        self._temporal_topology["NEXT"] = map
229
230    def set_prev(self, map):
231        """Set the map that is temporally as closest located before this map.
232
233           Temporally located means that the start time of the "previous" map
234           is temporally located BEFORE the start time of this map, but
235           temporally near than other maps of the same dataset.
236
237           :param map: This object should be of type AbstractMapDataset
238                       or derived classes
239        """
240        self._temporal_topology["PREV"] = map
241
242    def next(self):
243        """Return the map with a start time temporally located after
244           the start time of this map, but temporal closer than other maps
245
246           :return: A map object or None
247        """
248        if "NEXT" not in self._temporal_topology:
249            return None
250        return self._temporal_topology["NEXT"]
251
252    def prev(self):
253        """Return the map with a start time temporally located before
254           the start time of this map, but temporal closer than other maps
255
256           :return: A map object or None
257        """
258        if "PREV" not in self._temporal_topology:
259            return None
260        return self._temporal_topology["PREV"]
261
262    def append_equal(self, map):
263        """Append a map with equivalent temporal extent as this map
264
265           :param map: This object should be of type AbstractMapDataset
266                       or derived classes
267        """
268        if "EQUAL" not in self._temporal_topology:
269            self._temporal_topology["EQUAL"] = []
270        self._temporal_topology["EQUAL"].append(map)
271
272    def get_equal(self):
273        """Return a list of map objects with equivalent temporal extent as
274           this map
275
276           :return: A list of map objects or None
277        """
278        if "EQUAL" not in self._temporal_topology:
279            return None
280        return self._temporal_topology["EQUAL"]
281
282    def append_starts(self, map):
283        """Append a map that this map temporally starts with
284
285           :param map: This object should be of type AbstractMapDataset
286                       or derived classes
287        """
288        if "STARTS" not in self._temporal_topology:
289            self._temporal_topology["STARTS"] = []
290        self._temporal_topology["STARTS"].append(map)
291
292    def get_starts(self):
293        """Return a list of map objects that this map temporally starts with
294
295           :return: A list of map objects or None
296        """
297        if "STARTS" not in self._temporal_topology:
298            return None
299        return self._temporal_topology["STARTS"]
300
301    def append_started(self, map):
302        """Append a map that this map temporally started with
303
304           :param map: This object should be of type AbstractMapDataset
305                       or derived classes
306        """
307        if "STARTED" not in self._temporal_topology:
308            self._temporal_topology["STARTED"] = []
309        self._temporal_topology["STARTED"].append(map)
310
311    def get_started(self):
312        """Return a list of map objects that this map temporally started with
313
314           :return: A list of map objects or None
315        """
316        if "STARTED" not in self._temporal_topology:
317            return None
318        return self._temporal_topology["STARTED"]
319
320    def append_finishes(self, map):
321        """Append a map that this map temporally finishes with
322
323           :param map: This object should be of type AbstractMapDataset
324                       or derived classes
325        """
326        if "FINISHES" not in self._temporal_topology:
327            self._temporal_topology["FINISHES"] = []
328        self._temporal_topology["FINISHES"].append(map)
329
330    def get_finishes(self):
331        """Return a list of map objects that this map temporally finishes with
332
333           :return: A list of map objects or None
334        """
335        if "FINISHES" not in self._temporal_topology:
336            return None
337        return self._temporal_topology["FINISHES"]
338
339    def append_finished(self, map):
340        """Append a map that this map temporally finished with
341
342           :param map: This object should be of type AbstractMapDataset
343                       or derived classes
344        """
345        if "FINISHED" not in self._temporal_topology:
346            self._temporal_topology["FINISHED"] = []
347        self._temporal_topology["FINISHED"].append(map)
348
349    def get_finished(self):
350        """Return a list of map objects that this map temporally finished with
351
352           :return: A list of map objects or None
353        """
354        if "FINISHED" not in self._temporal_topology:
355            return None
356        return self._temporal_topology["FINISHED"]
357
358    def append_overlaps(self, map):
359        """Append a map that this map temporally overlaps
360
361           :param map: This object should be of type AbstractMapDataset
362                       or derived classes
363        """
364        if "OVERLAPS" not in self._temporal_topology:
365            self._temporal_topology["OVERLAPS"] = []
366        self._temporal_topology["OVERLAPS"].append(map)
367
368    def get_overlaps(self):
369        """Return a list of map objects that this map temporally overlaps
370
371           :return: A list of map objects or None
372        """
373        if "OVERLAPS" not in self._temporal_topology:
374            return None
375        return self._temporal_topology["OVERLAPS"]
376
377    def append_overlapped(self, map):
378        """Append a map that this map temporally overlapped
379
380           :param map: This object should be of type AbstractMapDataset
381                       or derived classes
382        """
383        if "OVERLAPPED" not in self._temporal_topology:
384            self._temporal_topology["OVERLAPPED"] = []
385        self._temporal_topology["OVERLAPPED"].append(map)
386
387    def get_overlapped(self):
388        """Return a list of map objects that this map temporally overlapped
389
390           :return: A list of map objects or None
391        """
392        if "OVERLAPPED" not in self._temporal_topology:
393            return None
394        return self._temporal_topology["OVERLAPPED"]
395
396    def append_follows(self, map):
397        """Append a map that this map temporally follows
398
399           :param map: This object should be of type AbstractMapDataset
400                       or derived classes
401        """
402        if "FOLLOWS" not in self._temporal_topology:
403            self._temporal_topology["FOLLOWS"] = []
404        self._temporal_topology["FOLLOWS"].append(map)
405
406    def get_follows(self):
407        """Return a list of map objects that this map temporally follows
408
409           :return: A list of map objects or None
410        """
411        if "FOLLOWS" not in self._temporal_topology:
412            return None
413        return self._temporal_topology["FOLLOWS"]
414
415    def append_precedes(self, map):
416        """Append a map that this map temporally precedes
417
418           :param map: This object should be of type AbstractMapDataset
419                       or derived classes
420        """
421        if "PRECEDES" not in self._temporal_topology:
422            self._temporal_topology["PRECEDES"] = []
423        self._temporal_topology["PRECEDES"].append(map)
424
425    def get_precedes(self):
426        """Return a list of map objects that this map temporally precedes
427
428           :return: A list of map objects or None
429        """
430        if "PRECEDES" not in self._temporal_topology:
431            return None
432        return self._temporal_topology["PRECEDES"]
433
434    def append_during(self, map):
435        """Append a map that this map is temporally located during
436           This includes temporal relationships starts and finishes
437
438           :param map: This object should be of type
439                       AbstractMapDataset or derived classes
440        """
441        if "DURING" not in self._temporal_topology:
442            self._temporal_topology["DURING"] = []
443        self._temporal_topology["DURING"].append(map)
444
445    def get_during(self):
446        """Return a list of map objects that this map is temporally located during
447           This includes temporally relationships starts and finishes
448
449           :return: A list of map objects or None
450        """
451        if "DURING" not in self._temporal_topology:
452            return None
453        return self._temporal_topology["DURING"]
454
455    def append_contains(self, map):
456        """Append a map that this map temporally contains
457           This includes temporal relationships started and finished
458
459           :param map: This object should be of type AbstractMapDataset
460                       or derived classes
461        """
462        if "CONTAINS" not in self._temporal_topology:
463            self._temporal_topology["CONTAINS"] = []
464        self._temporal_topology["CONTAINS"].append(map)
465
466    def get_contains(self):
467        """Return a list of map objects that this map temporally contains
468           This includes temporal relationships started and finished
469
470           :return: A list of map objects or None
471        """
472        if "CONTAINS" not in self._temporal_topology:
473            return None
474        return self._temporal_topology["CONTAINS"]
475
476    def _generate_map_list_string(self, map_list, line_wrap=True):
477        count = 0
478        string = ""
479        for map_ in map_list:
480            if line_wrap and count > 0 and count % 3 == 0:
481                string += "\n | ............................ "
482                count = 0
483            if count == 0:
484                string += map_.get_id()
485            else:
486                string += ",%s" % map_.get_id()
487            count += 1
488
489        return string
490
491    # Set the properties
492    equal = property(fget=get_equal, fset=append_equal)
493    follows = property(fget=get_follows, fset=append_follows)
494    precedes = property(fget=get_precedes, fset=append_precedes)
495    overlaps = property(fget=get_overlaps, fset=append_overlaps)
496    overlapped = property(fget=get_overlapped, fset=append_overlapped)
497    during = property(fget=get_during, fset=append_during)
498    contains = property(fget=get_contains, fset=append_contains)
499    starts = property(fget=get_starts, fset=append_starts)
500    started = property(fget=get_started, fset=append_started)
501    finishes = property(fget=get_finishes, fset=append_finishes)
502    finished = property(fget=get_finished, fset=append_finished)
503
504    def print_temporal_topology_info(self):
505        """Print information about this class in human readable style"""
506
507        print(" +-------------------- Temporal Topology -------------------------------------+")
508        #          0123456789012345678901234567890
509        if self.next() is not None:
510            print(" | Next: ...................... " + str(self.next().get_id()))
511        if self.prev() is not None:
512            print(" | Previous: .................. " + str(self.prev().get_id()))
513        if self.equal is not None:
514            print(" | Equal:...................... " +
515                  self._generate_map_list_string(self.equal))
516        if self.follows is not None:
517            print(" | Follows: ................... " +
518                  self._generate_map_list_string(self.follows))
519        if self.precedes is not None:
520            print(" | Precedes: .................. " +
521                  self._generate_map_list_string(self.precedes))
522        if self.overlaps is not None:
523            print(" | Overlaps: .................. " +
524                  self._generate_map_list_string(self.overlaps))
525        if self.overlapped is not None:
526            print(" | Overlapped: ................ " +
527                  self._generate_map_list_string(self.overlapped))
528        if self.during is not None:
529            print(" | During: .................... " +
530                  self._generate_map_list_string(self.during))
531        if self.contains is not None:
532            print(" | Contains: .................. " +
533                  self._generate_map_list_string(self.contains))
534        if self.starts is not None:
535            print(" | Starts:.. .................. " +
536                  self._generate_map_list_string(self.starts))
537        if self.started is not None:
538            print(" | Started:. .................. " +
539                  self._generate_map_list_string(self.started))
540        if self.finishes is not None:
541            print(" | Finishes:................... " +
542                  self._generate_map_list_string(self.finishes))
543        if self.finished is not None:
544            print(" | Finished:................... " +
545                  self._generate_map_list_string(self.finished))
546
547    def print_temporal_topology_shell_info(self):
548        """Print information about this class in shell style"""
549
550        if self.next() is not None:
551            print("next=" + self.next().get_id())
552        if self.prev() is not None:
553            print("prev=" + self.prev().get_id())
554        if self.equal is not None:
555            print("equal=" + self._generate_map_list_string(self.equal, False))
556        if self.follows is not None:
557            print("follows=" + self._generate_map_list_string(self.follows,
558                                                              False))
559        if self.precedes is not None:
560            print("precedes=" + self._generate_map_list_string(
561                  self.precedes, False))
562        if self.overlaps is not None:
563            print("overlaps=" + self._generate_map_list_string(
564                  self.overlaps, False))
565        if self.overlapped is not None:
566            print("overlapped=" +
567                  self._generate_map_list_string(self.overlapped, False))
568        if self.during is not None:
569            print("during=" + self._generate_map_list_string(self.during,
570                                                             False))
571        if self.contains is not None:
572            print("contains=" + self._generate_map_list_string(
573                  self.contains, False))
574        if self.starts is not None:
575            print("starts=" +
576                  self._generate_map_list_string(self.starts))
577        if self.started is not None:
578            print("started=" +
579                  self._generate_map_list_string(self.started))
580        if self.finishes is not None:
581            print("finishes=" +
582                  self._generate_map_list_string(self.finishes))
583        if self.finished is not None:
584            print("finished=" +
585                  self._generate_map_list_string(self.finished))
586
587###############################################################################
588
589if __name__ == "__main__":
590    import doctest
591    doctest.testmod()
592