Browse Source

Better naming scheme. Added spatial topology class.

git-svn-id: https://svn.osgeo.org/grass/grass/trunk@55998 15284696-431f-4ddb-bdfa-cd5b030d7da7
Soeren Gebbert 12 years ago
parent
commit
d6fc538df8

+ 1 - 1
lib/python/temporal/Makefile

@@ -8,7 +8,7 @@ PYDIR = $(ETC)/python
 GDIR = $(PYDIR)/grass
 DSTDIR = $(GDIR)/temporal
 
-MODULES = base core abstract_dataset abstract_temporal_dataset abstract_map_dataset abstract_space_time_dataset space_time_datasets create factory gui_support list register sampling metadata spatial_extent temporal_extent datetime_math temporal_granularity temporal_relationships unit_tests aggregation stds_export stds_import extract mapcalc univar_statistics
+MODULES = base core abstract_dataset abstract_map_dataset abstract_space_time_dataset space_time_datasets create factory gui_support list register sampling metadata spatial_extent temporal_extent datetime_math temporal_granularity spatio_temporal_relationships unit_tests aggregation stds_export stds_import extract mapcalc univar_statistics temporal_topology_dataset_connector spatial_topology_dataset_connector
 
 PYFILES := $(patsubst %,$(DSTDIR)/%.py,$(MODULES) __init__)
 PYCFILES := $(patsubst %,$(DSTDIR)/%.pyc,$(MODULES) __init__)

+ 3 - 2
lib/python/temporal/__init__.py

@@ -3,14 +3,15 @@ from base import *
 from temporal_extent import *
 from spatial_extent import *
 from metadata import *
+from temporal_topology_dataset_connector import *
+from spatial_topology_dataset_connector import *
 from abstract_dataset import *
-from abstract_temporal_dataset import *
 from abstract_map_dataset import *
 from abstract_space_time_dataset import *
 from space_time_datasets import *
 from datetime_math import *
 from temporal_granularity import *
-from temporal_relationships import *
+from spatio_temporal_relationships import *
 from create import *
 from factory import *
 from gui_support import *

+ 88 - 2
lib/python/temporal/abstract_dataset.py

@@ -35,6 +35,8 @@ import copy
 from temporal_extent import *
 from spatial_extent import *
 from metadata import *
+from temporal_topology_dataset_connector import *
+from spatial_topology_dataset_connector import *
 
 
 class ImplementationError(Exception):
@@ -48,12 +50,96 @@ class ImplementationError(Exception):
     
 ###############################################################################
 
-class AbstractDataset(object):
+class AbstractDataset(SpatialTopologyDatasetConnector, TemporalTopologyDatasetConnector):
     """!This is the base class for all datasets 
        (raster, vector, raster3d, strds, stvds, str3ds)"""
     def __init__(self):
-        pass
+        SpatialTopologyDatasetConnector.__init__(self)
+        TemporalTopologyDatasetConnector.__init__(self)
+        
+    def reset_topology(self):
+        """!Reset any information about temporal topology"""
+        self.reset_spatial_topology()
+        self.reset_temporal_topology()
+        
+    def get_number_of_relations(self):      
+        """! Return a dictionary in which the keys are the relation names and the value
+        are the number of relations.
+        
+        The following relations are available:
+        
+        Spatial relations
+        - equivalent
+        - overlap
+        - in
+        - contain
+        - meet
+        - cover
+        - covered
+        
+        Temporal relations
+        - equal
+        - follows
+        - precedes
+        - overlaps
+        - overlapped
+        - during (including starts, finishes)
+        - contains (including started, finished)
+        - starts
+        - started
+        - finishes
+        - finished
+       
+        To access topological information the spatial, temporal or booth topologies must be build first
+        using the SpatialTopologyBuilder, TemporalTopologyBuilder or SpatioTemporalTopologyBuilder.
+        
+        @return the dictionary with relations as keys and number as values or None in case the topology  wasn't build
+        """
+        if self.is_temporal_topology_build() and not self.is_spatial_topology_build():
+            return self.get_number_of_temporal_relations()
+        elif self.is_spatial_topology_build() and not self.is_temporal_topology_build():
+            self.get_number_of_spatial_relations()
+        else:
+            return  self.get_number_of_temporal_relations() + \
+                    self.get_number_of_spatial_relations()
+            
+        return None
+
+    def set_topology_build_true(self):
+        """!Use this method when the spatio-temporal topology was build"""
+        self.set_spatial_topology_build_true()
+        self.set_temporal_topology_build_true()
+        
+
+    def set_topology_build_false(self):
+        """!Use this method when the spatio-temporal topology was not build"""
+        self.set_spatial_topology_build_false()
+        self.set_temporal_topology_build_false()
+
+    def is_topology_build(self):
+        """!Check if the spatial and temporal topology was build
+        
+           @return A dictionary with "spatial" and "temporal" as keys that have boolen values
+        """
+        d = {}
+        d["spatial"] = self.is_spatial_topology_build()
+        d["temporal"] = self.is_temporal_topology_build()
+        
+        return d
+        
 
+    def print_topology_info(self):
+        if self.is_temporal_topology_build():
+            self.print_temporal_topology_info()
+        if self.is_spatial_topology_build():
+            self.print_spatial_topology_info()
+            
+    def print_topology_shell_info(self):
+        if self.is_temporal_topology_build():
+            self.print_temporal_topology_shell_info()
+        if self.is_spatial_topology_build():
+            self.print_spatial_topology_shell_info()
+            
     def reset(self, ident):
         """!Reset the internal structure and set the identifier
 

+ 3 - 3
lib/python/temporal/abstract_map_dataset.py

@@ -17,10 +17,10 @@ for details.
 
 @author Soeren Gebbert
 """
-from abstract_temporal_dataset import *
+from abstract_dataset import *
 from datetime_math import *
 
-class AbstractMapDataset(AbstractTemporalDataset):
+class AbstractMapDataset(AbstractDataset):
     """!This is the base class for all maps (raster, vector, raster3d).
     
         The temporal extent, the spatial extent and the metadata of maps
@@ -37,7 +37,7 @@ class AbstractMapDataset(AbstractTemporalDataset):
         - Abstract methods that must be implemented in the map specific subclasses
     """
     def __init__(self):
-        AbstractTemporalDataset.__init__(self)
+        AbstractDataset.__init__(self)
 
     def get_new_stds_instance(self, ident):
         """!Return a new space time dataset instance that store maps with the type of this map object (rast, rast3d or vect)

+ 2 - 2
lib/python/temporal/abstract_space_time_dataset.py

@@ -15,7 +15,7 @@ for details.
 import sys
 from abstract_dataset import *
 from temporal_granularity import *
-from temporal_relationships import *
+from spatio_temporal_relationships import *
 
 ###############################################################################
 
@@ -973,7 +973,7 @@ class AbstractSpaceTimeDataset(AbstractDataset):
         dbif, connected = init_dbif(dbif)
         obj_list = self.get_registered_maps_as_objects(where, order, dbif)
 
-        tb = TemporalTopologyBuilder()
+        tb = SpatioTemporalTopologyBuilder()
         tb.build(obj_list)
     
         if connected:

+ 2 - 0
lib/python/temporal/space_time_datasets.py

@@ -28,6 +28,7 @@ import grass.lib.vector as libvector
 import grass.lib.raster3d as libraster3d
 import grass.script as grass
 
+from abstract_map_dataset import *
 from abstract_space_time_dataset import *
 
 ###############################################################################
@@ -57,6 +58,7 @@ class RasterDataset(AbstractMapDataset):
         >>> rmap = RasterDataset(identifier)
         >>> rmap.set_absolute_time(start_time=datetime(2001,1,1), 
         ...                        end_time=datetime(2012,1,1))
+        True
         >>> rmap.map_exists()
         True
         >>> rmap.load()

+ 71 - 76
lib/python/temporal/abstract_spatial_dataset.py

@@ -8,7 +8,7 @@ Temporal GIS related functions to be used in temporal GIS Python library package
 Usage:
 
 >>> import grass.temporal as tgis
->>> tmr = tgis.AbstractSpatialDataset()
+>>> tmr = tgis.SpatialTopologyDatasetConnector()
 
 (C) 2008-2011 by the GRASS Development Team
 This program is free software under the GNU General Public
@@ -17,15 +17,12 @@ for details.
 
 @author Soeren Gebbert
 """
-from abstract_dataset import *
-from datetime_math import *
 
-
-class AbstractTemporalDataset(AbstractDataset):
-    """!This class implements a spatial topology access structure for an abstract dataset
+class SpatialTopologyDatasetConnector(object):
+    """!This class implements a spatial topology access structure to connect spatial related datasets
 
        This object will be set up by spatial topology creation method provided by the 
-       SpatialTopologyBuilder.
+       SpatioTemporalTopologyBuilder.
 
        The following spatial relations with access methods are supported:
        - equivalent
@@ -43,7 +40,7 @@ class AbstractTemporalDataset(AbstractDataset):
         >>> import grass.temporal as tgis
         >>> tgis.init()
         >>> map = tgis.RasterDataset("a@P")
-        >>> tmr = tgis.AbstractTemporalDataset()
+        >>> tmr = tgis.SpatialTopologyDatasetConnector()
         >>> tmr.append_equivalent(map)
         >>> tmr.append_overlap(map)
         >>> tmr.append_in(map)
@@ -51,7 +48,7 @@ class AbstractTemporalDataset(AbstractDataset):
         >>> tmr.append_meet(map)
         >>> tmr.append_cover(map)
         >>> tmr.append_covered(map)
-        >>> tmr.print_topology_info()
+        >>> tmr.print_spatial_topology_info()
          +-------------------- Spatial Topology --------------------------------------+
          | Equivalent: ................ a@P
          | Cover: ..................... a@P
@@ -60,28 +57,27 @@ class AbstractTemporalDataset(AbstractDataset):
          | In: ........................ a@P
          | Contain: ................... a@P
          | Meet: ...................... a@P
-        >>> tmr.print_topology_shell_info()
+        >>> tmr.print_spatial_topology_shell_info()
         equivalent=a@P
+        cover=a@P
+        covered=a@P
         overlap=a@P
         in=a@P
         contain=a@P
         meet=a@P
-        cover=a@P
-        covered=a@P
         
         @endcode
     """
 
     def __init__(self):
-        AbstractDataset.__init__(self)
-        self.reset_topology()
+        self.reset_spatial_topology()
 
-    def reset_topology(self):
+    def reset_spatial_topology(self):
         """!Reset any information about temporal topology"""
-        self._topology = {}
-        self._has_topology = False
+        self._spatial_topology = {}
+        self._has_spatial_topology = False
         
-    def get_number_of_relations(self):      
+    def get_number_of_spatial_relations(self):
         """! Return a dictionary in which the keys are the relation names and the value
         are the number of relations.
         
@@ -99,52 +95,52 @@ class AbstractTemporalDataset(AbstractDataset):
         
         @return the dictionary with relations as keys and number as values or None in case the topology wasn't build
         """
-        if self._has_topology == False:
+        if self._has_spatial_topology == False:
             return None
     
         relations = {}
         try:
-            relations["equivalent"] = len(self._topology["EQUIVALENT"]) 
+            relations["equivalent"] = len(self._spatial_topology["EQUIVALENT"]) 
         except:
             relations["equivalent"] = 0
         try: 
-            relations["overlap"] = len(self._topology["OVERLAP"]) 
+            relations["overlap"] = len(self._spatial_topology["OVERLAP"]) 
         except: 
             relations["overlap"] = 0
         try: 
-            relations["in"] = len(self._topology["IN"])
+            relations["in"] = len(self._spatial_topology["IN"])
         except: 
             relations["in"] = 0
         try: 
-            relations["contain"] = len(self._topology["CONTAIN"])
+            relations["contain"] = len(self._spatial_topology["CONTAIN"])
         except: 
             relations["contain"] = 0
         try: 
-            relations["meet"] = len(self._topology["MEET"])
+            relations["meet"] = len(self._spatial_topology["MEET"])
         except: 
             relations["meet"] = 0
         try: 
-            relations["cover"] = len(self._topology["COVER"])
+            relations["cover"] = len(self._spatial_topology["COVER"])
         except: 
             relations["cover"] = 0
         try: 
-            relations["covered"] = len(self._topology["COVERED"])
+            relations["covered"] = len(self._spatial_topology["COVERED"])
         except: 
             relations["covered"] = 0
             
         return relations
 
-    def set_topology_build_true(self):
+    def set_spatial_topology_build_true(self):
         """!Same as name"""
-        self._has_topology = True
+        self._has_spatial_topology = True
 
-    def set_topology_build_false(self):
+    def set_spatial_topology_build_false(self):
         """!Same as name"""
-        self._has_topology = False
+        self._has_spatial_topology = False
 
-    def is_topology_build(self):
+    def is_spatial_topology_build(self):
         """!Check if the temporal topology was build"""
-        return self._has_topology
+        return self._has_spatial_topology
 
     def append_equivalent(self, map):
         """!Append a map with equivalent spatial extent as this map
@@ -152,18 +148,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "EQUIVALENT" not in self._topology:
-            self._topology["EQUIVALENT"] = []
-        self._topology["EQUIVALENT"].append(map)
+        if "EQUIVALENT" not in self._spatial_topology:
+            self._spatial_topology["EQUIVALENT"] = []
+        self._spatial_topology["EQUIVALENT"].append(map)
 
     def get_equivalent(self):
         """!Return a list of map objects with equivalent spatial extent as this map
 
            @return A list of map objects or None
         """
-        if "EQUIVALENT" not in self._topology:
+        if "EQUIVALENT" not in self._spatial_topology:
             return None
-        return self._topology["EQUIVALENT"]
+        return self._spatial_topology["EQUIVALENT"]
 
     def append_overlap(self, map):
         """!Append a map that this spatial overlap with this map
@@ -171,18 +167,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "OVERLAP" not in self._topology:
-            self._topology["OVERLAP"] = []
-        self._topology["OVERLAP"].append(map)
+        if "OVERLAP" not in self._spatial_topology:
+            self._spatial_topology["OVERLAP"] = []
+        self._spatial_topology["OVERLAP"].append(map)
 
     def get_overlap(self):
         """!Return a list of map objects that this map spatial overlap with
 
            @return A list of map objects or None
         """
-        if "OVERLAP" not in self._topology:
+        if "OVERLAP" not in self._spatial_topology:
             return None
-        return self._topology["OVERLAP"]
+        return self._spatial_topology["OVERLAP"]
 
     def append_in(self, map):
         """!Append a map that this is spatial in this map
@@ -190,18 +186,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "IN" not in self._topology:
-            self._topology["IN"] = []
-        self._topology["IN"].append(map)
+        if "IN" not in self._spatial_topology:
+            self._spatial_topology["IN"] = []
+        self._spatial_topology["IN"].append(map)
 
     def get_in(self):
         """!Return a list of map objects that are spatial in this map
 
            @return A list of map objects or None
         """
-        if "IN" not in self._topology:
+        if "IN" not in self._spatial_topology:
             return None
-        return self._topology["IN"]
+        return self._spatial_topology["IN"]
 
     def append_contain(self, map):
         """!Append a map that this map spatially contains
@@ -209,18 +205,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "CONTAIN" not in self._topology:
-            self._topology["CONTAIN"] = []
-        self._topology["CONTAIN"].append(map)
+        if "CONTAIN" not in self._spatial_topology:
+            self._spatial_topology["CONTAIN"] = []
+        self._spatial_topology["CONTAIN"].append(map)
 
     def get_contain(self):
         """!Return a list of map objects that this map contains
 
            @return A list of map objects or None
         """
-        if "CONTAIN" not in self._topology:
+        if "CONTAIN" not in self._spatial_topology:
             return None
-        return self._topology["CONTAIN"]
+        return self._spatial_topology["CONTAIN"]
 
     def append_meet(self, map):
         """!Append a map that spatially meet with this map
@@ -228,18 +224,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "MEET" not in self._topology:
-            self._topology["MEET"] = []
-        self._topology["MEET"].append(map)
+        if "MEET" not in self._spatial_topology:
+            self._spatial_topology["MEET"] = []
+        self._spatial_topology["MEET"].append(map)
 
     def get_meet(self):
         """!Return a list of map objects that spatially meet with this map
 
            @return A list of map objects or None
         """
-        if "MEET" not in self._topology:
+        if "MEET" not in self._spatial_topology:
             return None
-        return self._topology["MEET"]
+        return self._spatial_topology["MEET"]
 
     def append_cover(self, map):
         """!Append a map that spatially cover this map
@@ -247,18 +243,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "COVER" not in self._topology:
-            self._topology["COVER"] = []
-        self._topology["COVER"].append(map)
+        if "COVER" not in self._spatial_topology:
+            self._spatial_topology["COVER"] = []
+        self._spatial_topology["COVER"].append(map)
 
     def get_cover(self):
         """!Return a list of map objects that spatially cover this map
 
            @return A list of map objects or None
         """
-        if "COVER" not in self._topology:
+        if "COVER" not in self._spatial_topology:
             return None
-        return self._topology["COVER"]
+        return self._spatial_topology["COVER"]
 
     def append_covered(self, map):
         """!Append a map that is spatially covered by this map
@@ -266,18 +262,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "COVERED" not in self._topology:
-            self._topology["COVERED"] = []
-        self._topology["COVERED"].append(map)
+        if "COVERED" not in self._spatial_topology:
+            self._spatial_topology["COVERED"] = []
+        self._spatial_topology["COVERED"].append(map)
 
     def get_covered(self):
         """!Return a list of map objects that are spatially covered by this map
 
            @return A list of map objects or None
         """
-        if "COVERED" not in self._topology:
+        if "COVERED" not in self._spatial_topology:
             return None
-        return self._topology["COVERED"]
+        return self._spatial_topology["COVERED"]
 
 
     def _generate_map_list_string(self, map_list, line_wrap=True):
@@ -304,14 +300,14 @@ class AbstractTemporalDataset(AbstractDataset):
                                        fset=append_covered)
     overlap = property(fget=get_overlap, 
                                      fset=append_overlap)
-    in = property(fget=get_in, 
+    in_ = property(fget=get_in, 
                                      fset=append_in)
     contain = property(fget=get_contain, 
                                      fset=append_contain)
     meet = property(fget=get_meet, 
                                      fset=append_meet)
 
-    def print_topology_info(self):
+    def print_spatial_topology_info(self):
         """!Print information about this class in human readable style"""
         
         print " +-------------------- Spatial Topology --------------------------------------+"
@@ -328,9 +324,9 @@ class AbstractTemporalDataset(AbstractDataset):
         if self.overlap is not None:
             print " | Overlap: ................... " + \
                 self._generate_map_list_string(self.overlap)
-        if self.in is not None:
+        if self.in_ is not None:
             print " | In: ........................ " + \
-                self._generate_map_list_string(self.in)
+                self._generate_map_list_string(self.in_)
         if self.contain is not None:
             print " | Contain: ................... " + \
                 self._generate_map_list_string(self.contain)
@@ -338,10 +334,9 @@ class AbstractTemporalDataset(AbstractDataset):
             print " | Meet: ...................... " + \
                 self._generate_map_list_string(self.meet)
 
-    def print_topology_shell_info(self):
+    def print_spatial_topology_shell_info(self):
         """!Print information about this class in shell style"""
-        
-        if self.next() is not None:
+
         if self.equivalent is not None:
             print "equivalent=" + self._generate_map_list_string(self.equivalent, False)
         if self.cover is not None:
@@ -353,9 +348,9 @@ class AbstractTemporalDataset(AbstractDataset):
         if self.overlap is not None:
             print "overlap=" + \
                 self._generate_map_list_string(self.overlap)
-        if self.in is not None:
+        if self.in_ is not None:
             print "in=" + \
-                self._generate_map_list_string(self.in)
+                self._generate_map_list_string(self.in_)
         if self.contain is not None:
             print "contain=" + \
                 self._generate_map_list_string(self.contain)

+ 137 - 26
lib/python/temporal/temporal_relationships.py

@@ -20,7 +20,7 @@ for details.
 
 @author Soeren Gebbert
 """
-from abstract_map_dataset import *
+from abstract_dataset import *
 from datetime_math import *
 import grass.lib.vector as vector
 import grass.lib.gis as gis
@@ -28,9 +28,9 @@ from ctypes import *
 
 ###############################################################################
 
-class TemporalTopologyBuilder(object):
-    """!This class is designed to build the temporal topology 
-       of temporally related abstract dataset objects.
+class SpatioTemporalTopologyBuilder(object):
+    """!This class is designed to build the spatio-temporal topology 
+       of spatio-temporally related abstract dataset objects.
        
        The abstract dataset objects must be provided as a single list, or in two lists.
 
@@ -42,7 +42,7 @@ class TemporalTopologyBuilder(object):
 
         # Now lets build the temporal topology of the maps in the list
         
-        tb = TemporalTopologyBuilder()
+        tb = SpatioTemporalTopologyBuilder()
         
         tb.build(maps)
 
@@ -149,10 +149,16 @@ class TemporalTopologyBuilder(object):
             next_ = map_.next()
             if next_:
                 next_.set_prev(map_)
-            map_.set_topology_build_true()
+            map_.set_temporal_topology_build_true()
         
-    def _map_to_rect(self, tree, map_):
-        """Use the temporal extent of a map to create and return a RTree rectange"""
+    def _map_to_rect(self, tree, map_, spatial=None):
+        """Use the temporal extent of a map to create and return a RTree rectange
+        
+        
+           @param spatial This indicates if the spatial topology is created as well:
+                          spatial can be None (no spatial topology), "2D" using west, east, 
+                          #south, north or "3D" using west, east, south, north, bottom, top
+        """
         rect = vector.RTreeAllocRect(tree)
         
         start, end = map_.get_valid_time()
@@ -163,33 +169,51 @@ class TemporalTopologyBuilder(object):
         if map_.is_time_absolute():
             start = time_delta_to_relative_time(start - self._timeref)
             end = time_delta_to_relative_time(end - self._timeref)
-                
-        vector.RTreeSetRect1D(rect, tree, float(start), float(end))
+        
+        if spatial is None:
+            vector.RTreeSetRect1D(rect, tree, float(start), float(end))
+        elif spatial == "2D":
+            north, south, east, west, top, bottom = map_.get_spatial_extent()
+            vector.RTreeSetRect3D(rect, tree, west, east, south, north, 
+                                  float(start), float(end))
+        elif spatial == "3D":
+            north, south, east, west, top, bottom = map_.get_spatial_extent()
+            vector.RTreeSetRect4D(rect, tree, west, east, south, north, 
+                                  bottom, top, float(start), float(end))
         
         return rect
         
-    def _build_1d_rtree(self, maps):
-        """Build and return the one dimensional R*-Tree"""
-
-        tree = vector.RTreeCreateTree(-1, 0, 1)
+    def _build_rtree(self, maps, spatial=None):
+        """Build and return the 1-4 dimensional R*-Tree
+        
+        
+           @param spatial This indicates if the spatial topology is created as well:
+                          spatial can be None (no spatial topology), "2D" using west, east, 
+                          south, north or "3D" using west, east, south, north, bottom, top
+        """
+        dim = 1
+        if spatial == "2D":
+            dim = 3
+        if spatial == "3D":
+            dim = 4
+        tree = vector.RTreeCreateTree(-1, 0, dim)
 
         for i in xrange(len(maps)):
             
-            rect = self._map_to_rect(tree, maps[i])
+            rect = self._map_to_rect(tree, maps[i], spatial)
             vector.RTreeInsertRect(rect, i + 1, tree)
             
         return tree
 
-    def build(self, mapsA, mapsB=None):
-        """!Build the temporal topology structure between 
+    def build(self, mapsA, mapsB=None, spatial=None):
+        """!Build the spatio-temporal topology structure between 
            one or two unordered lists of abstract dataset objects
 
-           This method builds the temporal topology from mapsA to 
-           mapsB and vice verse. The temporal topology structure of each map, 
-           defined in class temporal_map_relations,
+           This method builds the temporal or spatio-temporal topology from mapsA to 
+           mapsB and vice verse. The spatio-temporal topology structure of each map
            will be reseted and rebuild for mapsA and mapsB.
 
-           After building the temporal topology the modified 
+           After building the temporal or spatio-temporal topology the modified 
            map objects of mapsA can be accessed
            in the same way as a dictionary using there id. 
            The implemented iterator assures
@@ -199,7 +223,13 @@ class TemporalTopologyBuilder(object):
                          objects with initiated temporal extent
            @param mapsB An optional list of abstract_dataset 
                          objects with initiated temporal extent
+           @param spatial This indicates if the spatial topology is created as well:
+                          spatial can be None (no spatial topology), "2D" using west, east, 
+                          south, north or "3D" using west, east, south, north, bottom, top
         """
+        
+        if spatial is not None:
+            dbif, connected = init_dbif(None)
 
         identical = False
         if mapsA == mapsB:
@@ -211,17 +241,23 @@ class TemporalTopologyBuilder(object):
 
         for map_ in mapsA:
             map_.reset_topology()
+            # Spatial extent from the database
+            if spatial is not None:
+                map_.select(dbif)
 
         if not identical:
             for map_ in mapsB:
                 map_.reset_topology()
+                # Spatial extent from the database
+                if spatial is not None:
+                    map_.select(dbif)
 
-        tree = self. _build_1d_rtree(mapsA)
+        tree = self. _build_rtree(mapsA, spatial)
             
         for j in xrange(len(mapsB)):            
         
             list_ = gis.ilist()
-            rect = self._map_to_rect(tree, mapsB[j])
+            rect = self._map_to_rect(tree, mapsB[j], spatial)
             num = vector.RTreeSearch2(tree, rect, byref(list_))
             vector.RTreeFreeRect(rect)
 
@@ -234,12 +270,20 @@ class TemporalTopologyBuilder(object):
                 A = mapsA[i]
                 B = mapsB[j]
                 set_temoral_relationship(A, B, relation)
+                
+                if spatial is not None:
+                    relation = mapsB[j].spatial_relation(mapsA[i])
+                    set_spatial_relationship(A, B, relation)
 
         self._build_internal_iteratable(mapsA)
         if not identical and mapsB != None:
             self._build_iteratable(mapsB)
         
         vector.RTreeDestroyTree(tree)
+        
+        if spatial is not None:
+            if connected:
+                dbif.close()
 
     def __iter__(self):
         start_ = self._first
@@ -258,7 +302,6 @@ class TemporalTopologyBuilder(object):
 
 ###############################################################################
 
-
 def set_temoral_relationship(A, B, relation):
     if relation == "equal":
         if B != A:
@@ -364,6 +407,74 @@ def set_temoral_relationship(A, B, relation):
             A.append_overlaps(B)
 
 ###############################################################################
+       
+def set_spatial_relationship(A, B, relation):
+    if relation == "equivalent":
+        if B != A:
+            if not B.get_equivalent() or \
+            (B.get_equivalent() and \
+            A not in B.get_equivalent()):
+                B.append_equivalent(A)
+            if not A.get_equivalent() or \
+            (A.get_equivalent() and \
+            B not in A.get_equivalent()):
+                A.append_equivalent(B)
+    elif relation == "overlap":
+        if not B.get_overlap() or \
+            (B.get_overlap() and \
+            A not in B.get_overlap()):
+            B.append_overlap(A)
+        if not A.get_overlap() or \
+            (A.get_overlap() and
+            B not in A.get_overlap()):
+            A.append_overlap(B)
+    elif relation == "meet":
+        if not B.get_meet() or \
+            (B.get_meet() and \
+            A not in B.get_meet()):
+            B.append_meet(A)
+        if not A.get_meet() or \
+            (A.get_meet() and
+            B not in A.get_meet()):
+            A.append_meet(B)
+    elif relation == "contain":
+        if not B.get_contain() or \
+            (B.get_contain() and \
+            A not in B.get_contain()):
+            B.append_contain(A)
+        if not A.get_in() or \
+            (A.get_in() and \
+            B not in A.get_in()):
+            A.append_in(B)
+    elif relation == "in":
+        if not B.get_in() or \
+            (B.get_in() and \
+            A not in B.get_in()):
+            B.append_in(A)
+        if not A.get_contain() or \
+            (A.get_contain() and \
+            B not in A.get_contain()):
+            A.append_contain(B)
+    elif relation == "cover":
+        if not B.get_cover() or \
+            (B.get_cover() and \
+            A not in B.get_cover()):
+            B.append_cover(A)
+        if not A.get_covered() or \
+            (A.get_covered() and \
+            B not in A.get_covered()):
+            A.append_covered(B)
+    elif relation == "covered":
+        if not B.get_covered() or \
+            (B.get_covered() and \
+            A not in B.get_covered()):
+            B.append_covered(A)
+        if not A.get_cover() or \
+            (A.get_cover() and \
+            B not in A.get_cover()):
+            A.append_cover(B)
+
+###############################################################################
 
 def print_temporal_topology_relationships(maps1, maps2=None, dbif=None):
     """!Print the temporal relationships of the 
@@ -376,7 +487,7 @@ def print_temporal_topology_relationships(maps1, maps2=None, dbif=None):
         @param dbif The database interface to be used
     """
                     
-    tb = TemporalTopologyBuilder()
+    tb = SpatioTemporalTopologyBuilder()
 
     tb.build(maps1, maps2)
 
@@ -406,7 +517,7 @@ def count_temporal_topology_relationships(maps1, maps2=None, dbif=None):
     """
 
     
-    tb = TemporalTopologyBuilder()
+    tb = SpatioTemporalTopologyBuilder()
     tb.build(maps1, maps2)
 
     dbif, connected = init_dbif(dbif)

+ 94 - 98
lib/python/temporal/abstract_temporal_dataset.py

@@ -8,7 +8,7 @@ Temporal GIS related functions to be used in temporal GIS Python library package
 Usage:
 
 >>> import grass.temporal as tgis
->>> tmr = tgis.AbstractTemporalDataset()
+>>> tmr = tgis.TemporalTopologyDatasetConnector()
 
 (C) 2008-2011 by the GRASS Development Team
 This program is free software under the GNU General Public
@@ -17,15 +17,12 @@ for details.
 
 @author Soeren Gebbert
 """
-from abstract_dataset import *
-from datetime_math import *
 
-
-class AbstractTemporalDataset(AbstractDataset):
-    """!This class implements a temporal topology access structure for an abstract dataset
+class TemporalTopologyDatasetConnector(object):
+    """!This class implements a temporal topology access structure to connect temporal related datasets
 
        This object will be set up by temporal topology creation method provided by the 
-       TemporallyTopologyBuilder.
+       SpatioTemporalTopologyBuilder.
 
        If correctly initialize the calls next() and prev() 
        let the user walk temporally forward and backward in time.
@@ -64,7 +61,7 @@ class AbstractTemporalDataset(AbstractDataset):
         >>> import grass.temporal as tgis
         >>> tgis.init()
         >>> map = tgis.RasterDataset("a@P")
-        >>> tmr = tgis.AbstractTemporalDataset()
+        >>> tmr = tgis.TemporalTopologyDatasetConnector()
         >>> tmr.set_next(map)
         >>> tmr.set_prev(map)
         >>> tmr.append_equal(map)
@@ -78,7 +75,7 @@ class AbstractTemporalDataset(AbstractDataset):
         >>> tmr.append_started(map)
         >>> tmr.append_finishes(map)
         >>> tmr.append_finished(map)
-        >>> tmr.print_topology_info()
+        >>> tmr.print_temporal_topology_info()
          +-------------------- Temporal Topology -------------------------------------+
          | Next: ...................... a@P
          | Previous: .................. a@P
@@ -93,7 +90,7 @@ class AbstractTemporalDataset(AbstractDataset):
          | Started:. .................. a@P
          | Finishes:................... a@P
          | Finished:................... a@P
-        >>> tmr.print_topology_shell_info()
+        >>> tmr.print_temporal_topology_shell_info()
         next=a@P
         prev=a@P
         equal=a@P
@@ -112,15 +109,14 @@ class AbstractTemporalDataset(AbstractDataset):
     """
 
     def __init__(self):
-        AbstractDataset.__init__(self)
-        self.reset_topology()
+        self.reset_temporal_topology()
 
-    def reset_topology(self):
+    def reset_temporal_topology(self):
         """!Reset any information about temporal topology"""
-        self._topology = {}
-        self._has_topology = False
+        self._temporal_topology = {}
+        self._has_temporal_topology = False
         
-    def get_number_of_relations(self):      
+    def get_number_of_temporal_relations(self):
         """! Return a dictionary in which the keys are the relation names and the value
         are the number of relations.
         
@@ -138,72 +134,72 @@ class AbstractTemporalDataset(AbstractDataset):
         - finished
         
         To access topological information the temporal topology must be build first
-        using the TemporalTopologyBuilder.
+        using the SpatioTemporalTopologyBuilder.
         
         @return the dictionary with relations as keys and number as values or None in case the topology wasn't build
         """
-        if self._has_topology == False:
+        if self._has_temporal_topology == False:
             return None
     
         relations = {}
         try:
-            relations["equal"] = len(self._topology["EQUAL"]) 
+            relations["equal"] = len(self._temporal_topology["EQUAL"]) 
         except:
             relations["equal"] = 0
         try: 
-            relations["follows"] = len(self._topology["FOLLOWS"]) 
+            relations["follows"] = len(self._temporal_topology["FOLLOWS"]) 
         except: 
             relations["follows"] = 0
         try: 
-            relations["precedes"] = len(self._topology["PRECEDES"])
+            relations["precedes"] = len(self._temporal_topology["PRECEDES"])
         except: 
             relations["precedes"] = 0
         try: 
-            relations["overlaps"] = len(self._topology["OVERLAPS"])
+            relations["overlaps"] = len(self._temporal_topology["OVERLAPS"])
         except: 
             relations["overlaps"] = 0
         try: 
-            relations["overlapped"] = len(self._topology["OVERLAPPED"])
+            relations["overlapped"] = len(self._temporal_topology["OVERLAPPED"])
         except: 
             relations["overlapped"] = 0
         try: 
-            relations["during"] = len(self._topology["DURING"])
+            relations["during"] = len(self._temporal_topology["DURING"])
         except: 
             relations["during"] = 0
         try: 
-            relations["contains"] = len(self._topology["CONTAINS"])
+            relations["contains"] = len(self._temporal_topology["CONTAINS"])
         except: 
             relations["contains"] = 0
         try: 
-            relations["starts"] = len(self._topology["STARTS"])
+            relations["starts"] = len(self._temporal_topology["STARTS"])
         except: 
             relations["starts"] = 0
         try:    
-            relations["started"] = len(self._topology["STARTED"])
+            relations["started"] = len(self._temporal_topology["STARTED"])
         except: 
             relations["started"] = 0
         try: 
-            relations["finishes"] = len(self._topology["FINISHES"])
+            relations["finishes"] = len(self._temporal_topology["FINISHES"])
         except: 
             relations["finishes"] = 0
         try: 
-            relations["finished"] = len(self._topology["FINISHED"])
+            relations["finished"] = len(self._temporal_topology["FINISHED"])
         except: 
             relations["finished"] = 0
             
         return relations
 
-    def set_topology_build_true(self):
+    def set_temporal_topology_build_true(self):
         """!Same as name"""
-        self._has_topology = True
+        self._has_temporal_topology = True
 
-    def set_topology_build_false(self):
+    def set_temporal_topology_build_false(self):
         """!Same as name"""
-        self._has_topology = False
+        self._has_temporal_topology = False
 
-    def is_topology_build(self):
+    def is_temporal_topology_build(self):
         """!Check if the temporal topology was build"""
-        return self._has_topology
+        return self._has_temporal_topology
 
     def set_next(self, map):
         """!Set the map that is temporally as closest located after this map.
@@ -215,7 +211,7 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        self._topology["NEXT"] = map
+        self._temporal_topology["NEXT"] = map
 
     def set_prev(self, map):
         """!Set the map that is temporally as closest located before this map.
@@ -227,7 +223,7 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        self._topology["PREV"] = map
+        self._temporal_topology["PREV"] = map
 
     def next(self):
         """!Return the map with a start time temporally located after
@@ -235,9 +231,9 @@ class AbstractTemporalDataset(AbstractDataset):
 
            @return A map object or None
         """
-        if "NEXT" not in self._topology:
+        if "NEXT" not in self._temporal_topology:
             return None
-        return self._topology["NEXT"]
+        return self._temporal_topology["NEXT"]
 
     def prev(self):
         """!Return the map with a start time temporally located before
@@ -245,9 +241,9 @@ class AbstractTemporalDataset(AbstractDataset):
 
            @return A map object or None
         """
-        if "PREV" not in self._topology:
+        if "PREV" not in self._temporal_topology:
             return None
-        return self._topology["PREV"]
+        return self._temporal_topology["PREV"]
 
     def append_equal(self, map):
         """!Append a map with equivalent temporal extent as this map
@@ -255,18 +251,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "EQUAL" not in self._topology:
-            self._topology["EQUAL"] = []
-        self._topology["EQUAL"].append(map)
+        if "EQUAL" not in self._temporal_topology:
+            self._temporal_topology["EQUAL"] = []
+        self._temporal_topology["EQUAL"].append(map)
 
     def get_equal(self):
         """!Return a list of map objects with equivalent temporal extent as this map
 
            @return A list of map objects or None
         """
-        if "EQUAL" not in self._topology:
+        if "EQUAL" not in self._temporal_topology:
             return None
-        return self._topology["EQUAL"]
+        return self._temporal_topology["EQUAL"]
 
     def append_starts(self, map):
         """!Append a map that this map temporally starts with
@@ -274,18 +270,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "STARTS" not in self._topology:
-            self._topology["STARTS"] = []
-        self._topology["STARTS"].append(map)
+        if "STARTS" not in self._temporal_topology:
+            self._temporal_topology["STARTS"] = []
+        self._temporal_topology["STARTS"].append(map)
 
     def get_starts(self):
         """!Return a list of map objects that this map temporally starts with
 
            @return A list of map objects or None
         """
-        if "STARTS" not in self._topology:
+        if "STARTS" not in self._temporal_topology:
             return None
-        return self._topology["STARTS"]
+        return self._temporal_topology["STARTS"]
 
     def append_started(self, map):
         """!Append a map that this map temporally started with
@@ -293,18 +289,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "STARTED" not in self._topology:
-            self._topology["STARTED"] = []
-        self._topology["STARTED"].append(map)
+        if "STARTED" not in self._temporal_topology:
+            self._temporal_topology["STARTED"] = []
+        self._temporal_topology["STARTED"].append(map)
 
     def get_started(self):
         """!Return a list of map objects that this map temporally started with
 
            @return A list of map objects or None
         """
-        if "STARTED" not in self._topology:
+        if "STARTED" not in self._temporal_topology:
             return None
-        return self._topology["STARTED"]
+        return self._temporal_topology["STARTED"]
 
     def append_finishes(self, map):
         """!Append a map that this map temporally finishes with
@@ -312,18 +308,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "FINISHES" not in self._topology:
-            self._topology["FINISHES"] = []
-        self._topology["FINISHES"].append(map)
+        if "FINISHES" not in self._temporal_topology:
+            self._temporal_topology["FINISHES"] = []
+        self._temporal_topology["FINISHES"].append(map)
 
     def get_finishes(self):
         """!Return a list of map objects that this map temporally finishes with
 
            @return A list of map objects or None
         """
-        if "FINISHES" not in self._topology:
+        if "FINISHES" not in self._temporal_topology:
             return None
-        return self._topology["FINISHES"]
+        return self._temporal_topology["FINISHES"]
 
     def append_finished(self, map):
         """!Append a map that this map temporally finished with
@@ -331,18 +327,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "FINISHED" not in self._topology:
-            self._topology["FINISHED"] = []
-        self._topology["FINISHED"].append(map)
+        if "FINISHED" not in self._temporal_topology:
+            self._temporal_topology["FINISHED"] = []
+        self._temporal_topology["FINISHED"].append(map)
 
     def get_finished(self):
         """!Return a list of map objects that this map temporally finished with
 
            @return A list of map objects or None
         """
-        if "FINISHED" not in self._topology:
+        if "FINISHED" not in self._temporal_topology:
             return None
-        return self._topology["FINISHED"]
+        return self._temporal_topology["FINISHED"]
 
     def append_overlaps(self, map):
         """!Append a map that this map temporally overlaps
@@ -350,18 +346,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "OVERLAPS" not in self._topology:
-            self._topology["OVERLAPS"] = []
-        self._topology["OVERLAPS"].append(map)
+        if "OVERLAPS" not in self._temporal_topology:
+            self._temporal_topology["OVERLAPS"] = []
+        self._temporal_topology["OVERLAPS"].append(map)
 
     def get_overlaps(self):
         """!Return a list of map objects that this map temporally overlaps
 
            @return A list of map objects or None
         """
-        if "OVERLAPS" not in self._topology:
+        if "OVERLAPS" not in self._temporal_topology:
             return None
-        return self._topology["OVERLAPS"]
+        return self._temporal_topology["OVERLAPS"]
 
     def append_overlapped(self, map):
         """!Append a map that this map temporally overlapped
@@ -369,18 +365,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "OVERLAPPED" not in self._topology:
-            self._topology["OVERLAPPED"] = []
-        self._topology["OVERLAPPED"].append(map)
+        if "OVERLAPPED" not in self._temporal_topology:
+            self._temporal_topology["OVERLAPPED"] = []
+        self._temporal_topology["OVERLAPPED"].append(map)
 
     def get_overlapped(self):
         """!Return a list of map objects that this map temporally overlapped
 
            @return A list of map objects or None
         """
-        if "OVERLAPPED" not in self._topology:
+        if "OVERLAPPED" not in self._temporal_topology:
             return None
-        return self._topology["OVERLAPPED"]
+        return self._temporal_topology["OVERLAPPED"]
 
     def append_follows(self, map):
         """!Append a map that this map temporally follows
@@ -388,18 +384,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "FOLLOWS" not in self._topology:
-            self._topology["FOLLOWS"] = []
-        self._topology["FOLLOWS"].append(map)
+        if "FOLLOWS" not in self._temporal_topology:
+            self._temporal_topology["FOLLOWS"] = []
+        self._temporal_topology["FOLLOWS"].append(map)
 
     def get_follows(self):
         """!Return a list of map objects that this map temporally follows
 
            @return A list of map objects or None
         """
-        if "FOLLOWS" not in self._topology:
+        if "FOLLOWS" not in self._temporal_topology:
             return None
-        return self._topology["FOLLOWS"]
+        return self._temporal_topology["FOLLOWS"]
 
     def append_precedes(self, map):
         """!Append a map that this map temporally precedes
@@ -407,18 +403,18 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "PRECEDES" not in self._topology:
-            self._topology["PRECEDES"] = []
-        self._topology["PRECEDES"].append(map)
+        if "PRECEDES" not in self._temporal_topology:
+            self._temporal_topology["PRECEDES"] = []
+        self._temporal_topology["PRECEDES"].append(map)
 
     def get_precedes(self):
         """!Return a list of map objects that this map temporally precedes
 
            @return A list of map objects or None
         """
-        if "PRECEDES" not in self._topology:
+        if "PRECEDES" not in self._temporal_topology:
             return None
-        return self._topology["PRECEDES"]
+        return self._temporal_topology["PRECEDES"]
 
     def append_during(self, map):
         """!Append a map that this map is temporally located during
@@ -427,9 +423,9 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type 
                         AbstractMapDataset or derived classes
         """
-        if "DURING" not in self._topology:
-            self._topology["DURING"] = []
-        self._topology["DURING"].append(map)
+        if "DURING" not in self._temporal_topology:
+            self._temporal_topology["DURING"] = []
+        self._temporal_topology["DURING"].append(map)
 
     def get_during(self):
         """!Return a list of map objects that this map is temporally located during
@@ -437,9 +433,9 @@ class AbstractTemporalDataset(AbstractDataset):
 
            @return A list of map objects or None
         """
-        if "DURING" not in self._topology:
+        if "DURING" not in self._temporal_topology:
             return None
-        return self._topology["DURING"]
+        return self._temporal_topology["DURING"]
 
     def append_contains(self, map):
         """!Append a map that this map temporally contains
@@ -448,9 +444,9 @@ class AbstractTemporalDataset(AbstractDataset):
            @param map This object should be of type AbstractMapDataset 
                         or derived classes
         """
-        if "CONTAINS" not in self._topology:
-            self._topology["CONTAINS"] = []
-        self._topology["CONTAINS"].append(map)
+        if "CONTAINS" not in self._temporal_topology:
+            self._temporal_topology["CONTAINS"] = []
+        self._temporal_topology["CONTAINS"].append(map)
 
     def get_contains(self):
         """!Return a list of map objects that this map temporally contains
@@ -458,9 +454,9 @@ class AbstractTemporalDataset(AbstractDataset):
 
            @return A list of map objects or None
         """
-        if "CONTAINS" not in self._topology:
+        if "CONTAINS" not in self._temporal_topology:
             return None
-        return self._topology["CONTAINS"]
+        return self._temporal_topology["CONTAINS"]
 
     def _generate_map_list_string(self, map_list, line_wrap=True):
         count = 0
@@ -501,7 +497,7 @@ class AbstractTemporalDataset(AbstractDataset):
     finished = property(fget=get_finished, 
                                      fset=append_finished)
 
-    def print_topology_info(self):
+    def print_temporal_topology_info(self):
         """!Print information about this class in human readable style"""
         
         print " +-------------------- Temporal Topology -------------------------------------+"
@@ -544,7 +540,7 @@ class AbstractTemporalDataset(AbstractDataset):
             print " | Finished:................... " + \
                 self._generate_map_list_string(self.finished)
 
-    def print_topology_shell_info(self):
+    def print_temporal_topology_shell_info(self):
         """!Print information about this class in shell style"""
         
         if self.next() is not None:

+ 3 - 3
lib/python/temporal/unit_tests.py

@@ -1392,7 +1392,7 @@ def test_temporal_topology_builder():
     _map.set_absolute_time(datetime(2001, 05, 01), datetime(2001, 06, 01))
     map_listA.append(copy.copy(_map))
 
-    tb = TemporalTopologyBuilder()
+    tb = SpatioTemporalTopologyBuilder()
     tb.build(map_listA)
 
     count = 0
@@ -1422,7 +1422,7 @@ def test_temporal_topology_builder():
     _map.set_absolute_time(datetime(2001, 05, 01), datetime(2001, 05, 14))
     map_listB.append(copy.copy(_map))
 
-    tb = TemporalTopologyBuilder()
+    tb = SpatioTemporalTopologyBuilder()
     tb.build(map_listB)
 
     # Probing some relations
@@ -1445,7 +1445,7 @@ def test_temporal_topology_builder():
                 (_map.get_id(), map_listB[count].get_id()))
         count += 1
 
-    tb = TemporalTopologyBuilder()
+    tb = SpatioTemporalTopologyBuilder()
     tb.build(map_listA, map_listB)
 
     count = 0