1# -*- coding: utf-8 -*-
2"""
3Spatial topology connector class
4
5Usage:
6
7>>> import grass.temporal as tgis
8>>> tmr = tgis.SpatialTopologyDatasetConnector()
9
10(C) 2012-2013 by the GRASS Development Team
11This program is free software under the GNU General Public
12License (>=v2). Read the file COPYING that comes with GRASS
13for details.
14
15:authors: Soeren Gebbert
16"""
17from __future__ import print_function
18import copy
19
20
21class SpatialTopologyDatasetConnector(object):
22    """This class implements a spatial topology access structure to connect
23       spatial related datasets
24
25       This object will be set up by spatial topology creation method provided
26       by the SpatioTemporalTopologyBuilder.
27
28       The following spatial relations with access methods are supported:
29
30       - equivalent
31       - overlap
32       - in
33       - contain
34       - meet
35       - cover
36       - covered
37
38        Usage:
39
40        .. code-block:: python
41
42            >>> import grass.temporal as tgis
43            >>> tgis.init()
44            >>> map = tgis.RasterDataset("a@P")
45            >>> tmr = tgis.SpatialTopologyDatasetConnector()
46            >>> tmr.append_equivalent(map)
47            >>> tmr.append_overlap(map)
48            >>> tmr.append_in(map)
49            >>> tmr.append_contain(map)
50            >>> tmr.append_meet(map)
51            >>> tmr.append_cover(map)
52            >>> tmr.append_covered(map)
53            >>> tmr.print_spatial_topology_info()
54             +-------------------- Spatial Topology --------------------------------------+
55             | Equivalent: ................ a@P
56             | Cover: ..................... a@P
57             | Covered: ................... a@P
58             | Overlap: ................... a@P
59             | In: ........................ a@P
60             | Contain: ................... a@P
61             | Meet: ...................... a@P
62            >>> tmr.print_spatial_topology_shell_info()
63            equivalent=a@P
64            cover=a@P
65            covered=a@P
66            overlap=a@P
67            in=a@P
68            contain=a@P
69            meet=a@P
70            >>> rlist = tmr.get_spatial_relations()
71            >>> if "COVER" in rlist.keys():
72            ...    print(rlist["COVER"][0].get_id())
73            a@P
74
75    """
76
77    def __init__(self):
78        self.reset_spatial_topology()
79
80    def reset_spatial_topology(self):
81        """Reset any information about temporal topology"""
82        self._spatial_topology = {}
83        self._has_spatial_topology = False
84
85    def get_spatial_relations(self):
86        """Return the dictionary of spatial relationships
87
88           Keys are the spatial relationships in upper case,
89           values are abstract map objects.
90
91           :return: The spatial relations dictionary
92        """
93        return copy.copy(self._spatial_topology)
94
95    def get_number_of_spatial_relations(self):
96        """ Return a dictionary in which the keys are the relation names and the value
97            are the number of relations.
98
99            The following relations are available:
100
101            - equivalent
102            - overlap
103            - in
104            - contain
105            - meet
106            - cover
107            - covered
108
109            To access topological information the spatial topology must be
110            build first using the SpatialTopologyBuilder.
111
112            :return: the dictionary with relations as keys and number as
113                     values or None in case the topology wasn't build
114        """
115        if self._has_spatial_topology is False:
116            return None
117
118        relations = {}
119        try:
120            relations["equivalent"] = len(self._spatial_topology["EQUIVALENT"])
121        except:
122            relations["equivalent"] = 0
123        try:
124            relations["overlap"] = len(self._spatial_topology["OVERLAP"])
125        except:
126            relations["overlap"] = 0
127        try:
128            relations["in"] = len(self._spatial_topology["IN"])
129        except:
130            relations["in"] = 0
131        try:
132            relations["contain"] = len(self._spatial_topology["CONTAIN"])
133        except:
134            relations["contain"] = 0
135        try:
136            relations["meet"] = len(self._spatial_topology["MEET"])
137        except:
138            relations["meet"] = 0
139        try:
140            relations["cover"] = len(self._spatial_topology["COVER"])
141        except:
142            relations["cover"] = 0
143        try:
144            relations["covered"] = len(self._spatial_topology["COVERED"])
145        except:
146            relations["covered"] = 0
147
148        return relations
149
150    def set_spatial_topology_build_true(self):
151        """Same as name"""
152        self._has_spatial_topology = True
153
154    def set_spatial_topology_build_false(self):
155        """Same as name"""
156        self._has_spatial_topology = False
157
158    def is_spatial_topology_build(self):
159        """Check if the temporal topology was build"""
160        return self._has_spatial_topology
161
162    def append_equivalent(self, map):
163        """Append a map with equivalent spatial extent as this map
164
165           :param map: This object should be of type AbstractMapDataset
166                       or derived classes
167        """
168        if "EQUIVALENT" not in self._spatial_topology:
169            self._spatial_topology["EQUIVALENT"] = []
170        self._spatial_topology["EQUIVALENT"].append(map)
171
172    def get_equivalent(self):
173        """Return a list of map objects with equivalent spatial extent as this map
174
175           :return: A list of map objects or None
176        """
177        if "EQUIVALENT" not in self._spatial_topology:
178            return None
179        return self._spatial_topology["EQUIVALENT"]
180
181    def append_overlap(self, map):
182        """Append a map that this spatial overlap with this map
183
184           :param map: This object should be of type AbstractMapDataset
185                       or derived classes
186        """
187        if "OVERLAP" not in self._spatial_topology:
188            self._spatial_topology["OVERLAP"] = []
189        self._spatial_topology["OVERLAP"].append(map)
190
191    def get_overlap(self):
192        """Return a list of map objects that this map spatial overlap with
193
194           :return: A list of map objects or None
195        """
196        if "OVERLAP" not in self._spatial_topology:
197            return None
198        return self._spatial_topology["OVERLAP"]
199
200    def append_in(self, map):
201        """Append a map that this is spatial in this map
202
203           :param map: This object should be of type AbstractMapDataset
204                       or derived classes
205        """
206        if "IN" not in self._spatial_topology:
207            self._spatial_topology["IN"] = []
208        self._spatial_topology["IN"].append(map)
209
210    def get_in(self):
211        """Return a list of map objects that are spatial in this map
212
213           :return: A list of map objects or None
214        """
215        if "IN" not in self._spatial_topology:
216            return None
217        return self._spatial_topology["IN"]
218
219    def append_contain(self, map):
220        """Append a map that this map spatially contains
221
222           :param map: This object should be of type AbstractMapDataset
223                       or derived classes
224        """
225        if "CONTAIN" not in self._spatial_topology:
226            self._spatial_topology["CONTAIN"] = []
227        self._spatial_topology["CONTAIN"].append(map)
228
229    def get_contain(self):
230        """Return a list of map objects that this map contains
231
232           :return: A list of map objects or None
233        """
234        if "CONTAIN" not in self._spatial_topology:
235            return None
236        return self._spatial_topology["CONTAIN"]
237
238    def append_meet(self, map):
239        """Append a map that spatially meet with this map
240
241           :param map: This object should be of type AbstractMapDataset
242                       or derived classes
243        """
244        if "MEET" not in self._spatial_topology:
245            self._spatial_topology["MEET"] = []
246        self._spatial_topology["MEET"].append(map)
247
248    def get_meet(self):
249        """Return a list of map objects that spatially meet with this map
250
251           :return: A list of map objects or None
252        """
253        if "MEET" not in self._spatial_topology:
254            return None
255        return self._spatial_topology["MEET"]
256
257    def append_cover(self, map):
258        """Append a map that spatially cover this map
259
260           :param map: This object should be of type AbstractMapDataset
261                       or derived classes
262        """
263        if "COVER" not in self._spatial_topology:
264            self._spatial_topology["COVER"] = []
265        self._spatial_topology["COVER"].append(map)
266
267    def get_cover(self):
268        """Return a list of map objects that spatially cover this map
269
270           :return: A list of map objects or None
271        """
272        if "COVER" not in self._spatial_topology:
273            return None
274        return self._spatial_topology["COVER"]
275
276    def append_covered(self, map):
277        """Append a map that is spatially covered by this map
278
279           :param map: This object should be of type AbstractMapDataset
280                       or derived classes
281        """
282        if "COVERED" not in self._spatial_topology:
283            self._spatial_topology["COVERED"] = []
284        self._spatial_topology["COVERED"].append(map)
285
286    def get_covered(self):
287        """Return a list of map objects that are spatially covered by this map
288
289           :return: A list of map objects or None
290        """
291        if "COVERED" not in self._spatial_topology:
292            return None
293        return self._spatial_topology["COVERED"]
294
295    def _generate_map_list_string(self, map_list, line_wrap=True):
296        count = 0
297        string = ""
298        for map_ in map_list:
299            if line_wrap and count > 0 and count % 3 == 0:
300                string += "\n | ............................ "
301                count = 0
302            if count == 0:
303                string += map_.get_id()
304            else:
305                string += ",%s" % map_.get_id()
306            count += 1
307
308        return string
309
310    # Set the properties
311    equivalent = property(fget=get_equivalent, fset=append_equivalent)
312    cover = property(fget=get_cover, fset=append_cover)
313    covered = property(fget=get_covered, fset=append_covered)
314    overlap = property(fget=get_overlap, fset=append_overlap)
315    in_ = property(fget=get_in, fset=append_in)
316    contain = property(fget=get_contain, fset=append_contain)
317    meet = property(fget=get_meet, fset=append_meet)
318
319    def print_spatial_topology_info(self):
320        """Print information about this class in human readable style"""
321
322        print(" +-------------------- Spatial Topology --------------------------------------+")
323        #          0123456789012345678901234567890
324        if self.equivalent is not None:
325            print(" | Equivalent: ................ " +
326                  self._generate_map_list_string(self.equivalent))
327        if self.cover is not None:
328            print(" | Cover: ..................... " +
329                  self._generate_map_list_string(self.cover))
330        if self.covered is not None:
331            print(" | Covered: ................... " +
332                  self._generate_map_list_string(self.covered))
333        if self.overlap is not None:
334            print(" | Overlap: ................... " +
335                  self._generate_map_list_string(self.overlap))
336        if self.in_ is not None:
337            print(" | In: ........................ " +
338                  self._generate_map_list_string(self.in_))
339        if self.contain is not None:
340            print(" | Contain: ................... " +
341                  self._generate_map_list_string(self.contain))
342        if self.meet is not None:
343            print(" | Meet: ...................... " +
344                  self._generate_map_list_string(self.meet))
345
346    def print_spatial_topology_shell_info(self):
347        """Print information about this class in shell style"""
348
349        if self.equivalent is not None:
350            print("equivalent=" + self._generate_map_list_string(self.equivalent,
351                                                                 False))
352        if self.cover is not None:
353            print("cover=" + self._generate_map_list_string(
354                  self.cover, False))
355        if self.covered is not None:
356            print("covered=" +
357                  self._generate_map_list_string(self.covered, False))
358        if self.overlap is not None:
359            print("overlap=" +
360                  self._generate_map_list_string(self.overlap))
361        if self.in_ is not None:
362            print("in=" +
363                  self._generate_map_list_string(self.in_))
364        if self.contain is not None:
365            print("contain=" +
366                  self._generate_map_list_string(self.contain))
367        if self.meet is not None:
368            print("meet=" +
369                  self._generate_map_list_string(self.meet))
370
371###############################################################################
372
373if __name__ == "__main__":
374    import doctest
375    doctest.testmod()
376